removing log dir from .gitignore
[monkeycharger.git] / vendor / rails / actionwebservice / setup.rb
blob9ab880d357ebc3d823fb5903788a13256b383d6f
2 # setup.rb
4 # Copyright (c) 2000-2004 Minero Aoki
6 # Permission is hereby granted, free of charge, to any person obtaining
7 # a copy of this software and associated documentation files (the
8 # "Software"), to deal in the Software without restriction, including
9 # without limitation the rights to use, copy, modify, merge, publish,
10 # distribute, sublicense, and/or sell copies of the Software, and to
11 # permit persons to whom the Software is furnished to do so, subject to
12 # the following conditions:
14 # The above copyright notice and this permission notice shall be
15 # included in all copies or substantial portions of the Software.
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
26 # with permission of Minero Aoki.
30 unless Enumerable.method_defined?(:map)   # Ruby 1.4.6
31   module Enumerable
32     alias map collect
33   end
34 end
36 unless File.respond_to?(:read)   # Ruby 1.6
37   def File.read(fname)
38     open(fname) {|f|
39       return f.read
40     }
41   end
42 end
44 def File.binread(fname)
45   open(fname, 'rb') {|f|
46     return f.read
47   }
48 end
50 # for corrupted windows stat(2)
51 def File.dir?(path)
52   File.directory?((path[-1,1] == '/') ? path : path + '/')
53 end
56 class SetupError < StandardError; end
58 def setup_rb_error(msg)
59   raise SetupError, msg
60 end
63 # Config
66 if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
67   ARGV.delete(arg)
68   require arg.split(/=/, 2)[1]
69   $".push 'rbconfig.rb'
70 else
71   require 'rbconfig'
72 end
74 def multipackage_install?
75   FileTest.directory?(File.dirname($0) + '/packages')
76 end
79 class ConfigItem
80   def initialize(name, template, default, desc)
81     @name = name.freeze
82     @template = template
83     @value = default
84     @default = default.dup.freeze
85     @description = desc
86   end
88   attr_reader :name
89   attr_reader :description
91   attr_accessor :default
92   alias help_default default
94   def help_opt
95     "--#{@name}=#{@template}"
96   end
98   def value
99     @value
100   end
102   def eval(table)
103     @value.gsub(%r<\$([^/]+)>) { table[$1] }
104   end
106   def set(val)
107     @value = check(val)
108   end
110   private
112   def check(val)
113     setup_rb_error "config: --#{name} requires argument" unless val
114     val
115   end
118 class BoolItem < ConfigItem
119   def config_type
120     'bool'
121   end
123   def help_opt
124     "--#{@name}"
125   end
127   private
129   def check(val)
130     return 'yes' unless val
131     unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ val
132       setup_rb_error "config: --#{@name} accepts only yes/no for argument"
133     end
134     (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no'
135   end
138 class PathItem < ConfigItem
139   def config_type
140     'path'
141   end
143   private
145   def check(path)
146     setup_rb_error "config: --#{@name} requires argument"  unless path
147     path[0,1] == '$' ? path : File.expand_path(path)
148   end
151 class ProgramItem < ConfigItem
152   def config_type
153     'program'
154   end
157 class SelectItem < ConfigItem
158   def initialize(name, template, default, desc)
159     super
160     @ok = template.split('/')
161   end
163   def config_type
164     'select'
165   end
167   private
169   def check(val)
170     unless @ok.include?(val.strip)
171       setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
172     end
173     val.strip
174   end
177 class PackageSelectionItem < ConfigItem
178   def initialize(name, template, default, help_default, desc)
179     super name, template, default, desc
180     @help_default = help_default
181   end
183   attr_reader :help_default
185   def config_type
186     'package'
187   end
189   private
191   def check(val)
192     unless File.dir?("packages/#{val}")
193       setup_rb_error "config: no such package: #{val}"
194     end
195     val
196   end
199 class ConfigTable_class
201   def initialize(items)
202     @items = items
203     @table = {}
204     items.each do |i|
205       @table[i.name] = i
206     end
207     ALIASES.each do |ali, name|
208       @table[ali] = @table[name]
209     end
210   end
212   include Enumerable
214   def each(&block)
215     @items.each(&block)
216   end
218   def key?(name)
219     @table.key?(name)
220   end
222   def lookup(name)
223     @table[name] or raise ArgumentError, "no such config item: #{name}"
224   end
226   def add(item)
227     @items.push item
228     @table[item.name] = item
229   end
231   def remove(name)
232     item = lookup(name)
233     @items.delete_if {|i| i.name == name }
234     @table.delete_if {|name, i| i.name == name }
235     item
236   end
238   def new
239     dup()
240   end
242   def savefile
243     '.config'
244   end
246   def load
247     begin
248       t = dup()
249       File.foreach(savefile()) do |line|
250         k, v = *line.split(/=/, 2)
251         t[k] = v.strip
252       end
253       t
254     rescue Errno::ENOENT
255       setup_rb_error $!.message + "#{File.basename($0)} config first"
256     end
257   end
259   def save
260     @items.each {|i| i.value }
261     File.open(savefile(), 'w') {|f|
262       @items.each do |i|
263         f.printf "%s=%s\n", i.name, i.value if i.value
264       end
265     }
266   end
268   def [](key)
269     lookup(key).eval(self)
270   end
272   def []=(key, val)
273     lookup(key).set val
274   end
278 c = ::Config::CONFIG
280 rubypath = c['bindir'] + '/' + c['ruby_install_name']
282 major = c['MAJOR'].to_i
283 minor = c['MINOR'].to_i
284 teeny = c['TEENY'].to_i
285 version = "#{major}.#{minor}"
287 # ruby ver. >= 1.4.4?
288 newpath_p = ((major >= 2) or
289              ((major == 1) and
290               ((minor >= 5) or
291                ((minor == 4) and (teeny >= 4)))))
293 if c['rubylibdir']
294   # V < 1.6.3
295   _stdruby         = c['rubylibdir']
296   _siteruby        = c['sitedir']
297   _siterubyver     = c['sitelibdir']
298   _siterubyverarch = c['sitearchdir']
299 elsif newpath_p
300   # 1.4.4 <= V <= 1.6.3
301   _stdruby         = "$prefix/lib/ruby/#{version}"
302   _siteruby        = c['sitedir']
303   _siterubyver     = "$siteruby/#{version}"
304   _siterubyverarch = "$siterubyver/#{c['arch']}"
305 else
306   # V < 1.4.4
307   _stdruby         = "$prefix/lib/ruby/#{version}"
308   _siteruby        = "$prefix/lib/ruby/#{version}/site_ruby"
309   _siterubyver     = _siteruby
310   _siterubyverarch = "$siterubyver/#{c['arch']}"
312 libdir = '-* dummy libdir *-'
313 stdruby = '-* dummy rubylibdir *-'
314 siteruby = '-* dummy site_ruby *-'
315 siterubyver = '-* dummy site_ruby version *-'
316 parameterize = lambda {|path|
317   path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')\
318       .sub(/\A#{Regexp.quote(libdir)}/,      '$libdir')\
319       .sub(/\A#{Regexp.quote(stdruby)}/,     '$stdruby')\
320       .sub(/\A#{Regexp.quote(siteruby)}/,    '$siteruby')\
321       .sub(/\A#{Regexp.quote(siterubyver)}/, '$siterubyver')
323 libdir          = parameterize.call(c['libdir'])
324 stdruby         = parameterize.call(_stdruby)
325 siteruby        = parameterize.call(_siteruby)
326 siterubyver     = parameterize.call(_siterubyver)
327 siterubyverarch = parameterize.call(_siterubyverarch)
329 if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
330   makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
331 else
332   makeprog = 'make'
335 common_conf = [
336   PathItem.new('prefix', 'path', c['prefix'],
337                'path prefix of target environment'),
338   PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
339                'the directory for commands'),
340   PathItem.new('libdir', 'path', libdir,
341                'the directory for libraries'),
342   PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
343                'the directory for shared data'),
344   PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
345                'the directory for man pages'),
346   PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
347                'the directory for man pages'),
348   PathItem.new('stdruby', 'path', stdruby,
349                'the directory for standard ruby libraries'),
350   PathItem.new('siteruby', 'path', siteruby,
351       'the directory for version-independent aux ruby libraries'),
352   PathItem.new('siterubyver', 'path', siterubyver,
353                'the directory for aux ruby libraries'),
354   PathItem.new('siterubyverarch', 'path', siterubyverarch,
355                'the directory for aux ruby binaries'),
356   PathItem.new('rbdir', 'path', '$siterubyver',
357                'the directory for ruby scripts'),
358   PathItem.new('sodir', 'path', '$siterubyverarch',
359                'the directory for ruby extentions'),
360   PathItem.new('rubypath', 'path', rubypath,
361                'the path to set to #! line'),
362   ProgramItem.new('rubyprog', 'name', rubypath,
363                   'the ruby program using for installation'),
364   ProgramItem.new('makeprog', 'name', makeprog,
365                   'the make program to compile ruby extentions'),
366   SelectItem.new('shebang', 'all/ruby/never', 'ruby',
367                  'shebang line (#!) editing mode'),
368   BoolItem.new('without-ext', 'yes/no', 'no',
369                'does not compile/install ruby extentions')
371 class ConfigTable_class   # open again
372   ALIASES = {
373     'std-ruby'         => 'stdruby',
374     'site-ruby-common' => 'siteruby',     # For backward compatibility
375     'site-ruby'        => 'siterubyver',  # For backward compatibility
376     'bin-dir'          => 'bindir',
377     'bin-dir'          => 'bindir',
378     'rb-dir'           => 'rbdir',
379     'so-dir'           => 'sodir',
380     'data-dir'         => 'datadir',
381     'ruby-path'        => 'rubypath',
382     'ruby-prog'        => 'rubyprog',
383     'ruby'             => 'rubyprog',
384     'make-prog'        => 'makeprog',
385     'make'             => 'makeprog'
386   }
388 multipackage_conf = [
389   PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
390                            'package names that you want to install'),
391   PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
392                            'package names that you do not want to install')
394 if multipackage_install?
395   ConfigTable = ConfigTable_class.new(common_conf + multipackage_conf)
396 else
397   ConfigTable = ConfigTable_class.new(common_conf)
401 module MetaConfigAPI
403   def eval_file_ifexist(fname)
404     instance_eval File.read(fname), fname, 1 if File.file?(fname)
405   end
407   def config_names
408     ConfigTable.map {|i| i.name }
409   end
411   def config?(name)
412     ConfigTable.key?(name)
413   end
415   def bool_config?(name)
416     ConfigTable.lookup(name).config_type == 'bool'
417   end
419   def path_config?(name)
420     ConfigTable.lookup(name).config_type == 'path'
421   end
423   def value_config?(name)
424     case ConfigTable.lookup(name).config_type
425     when 'bool', 'path'
426       true
427     else
428       false
429     end
430   end
432   def add_config(item)
433     ConfigTable.add item
434   end
436   def add_bool_config(name, default, desc)
437     ConfigTable.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
438   end
440   def add_path_config(name, default, desc)
441     ConfigTable.add PathItem.new(name, 'path', default, desc)
442   end
444   def set_config_default(name, default)
445     ConfigTable.lookup(name).default = default
446   end
448   def remove_config(name)
449     ConfigTable.remove(name)
450   end
456 # File Operations
459 module FileOperations
461   def mkdir_p(dirname, prefix = nil)
462     dirname = prefix + File.expand_path(dirname) if prefix
463     $stderr.puts "mkdir -p #{dirname}" if verbose?
464     return if no_harm?
466     # does not check '/'... it's too abnormal case
467     dirs = File.expand_path(dirname).split(%r<(?=/)>)
468     if /\A[a-z]:\z/i =~ dirs[0]
469       disk = dirs.shift
470       dirs[0] = disk + dirs[0]
471     end
472     dirs.each_index do |idx|
473       path = dirs[0..idx].join('')
474       Dir.mkdir path unless File.dir?(path)
475     end
476   end
478   def rm_f(fname)
479     $stderr.puts "rm -f #{fname}" if verbose?
480     return if no_harm?
482     if File.exist?(fname) or File.symlink?(fname)
483       File.chmod 0777, fname
484       File.unlink fname
485     end
486   end
488   def rm_rf(dn)
489     $stderr.puts "rm -rf #{dn}" if verbose?
490     return if no_harm?
492     Dir.chdir dn
493     Dir.foreach('.') do |fn|
494       next if fn == '.'
495       next if fn == '..'
496       if File.dir?(fn)
497         verbose_off {
498           rm_rf fn
499         }
500       else
501         verbose_off {
502           rm_f fn
503         }
504       end
505     end
506     Dir.chdir '..'
507     Dir.rmdir dn
508   end
510   def move_file(src, dest)
511     File.unlink dest if File.exist?(dest)
512     begin
513       File.rename src, dest
514     rescue
515       File.open(dest, 'wb') {|f| f.write File.binread(src) }
516       File.chmod File.stat(src).mode, dest
517       File.unlink src
518     end
519   end
521   def install(from, dest, mode, prefix = nil)
522     $stderr.puts "install #{from} #{dest}" if verbose?
523     return if no_harm?
525     realdest = prefix ? prefix + File.expand_path(dest) : dest
526     realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
527     str = File.binread(from)
528     if diff?(str, realdest)
529       verbose_off {
530         rm_f realdest if File.exist?(realdest)
531       }
532       File.open(realdest, 'wb') {|f|
533         f.write str
534       }
535       File.chmod mode, realdest
537       File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
538         if prefix
539           f.puts realdest.sub(prefix, '')
540         else
541           f.puts realdest
542         end
543       }
544     end
545   end
547   def diff?(new_content, path)
548     return true unless File.exist?(path)
549     new_content != File.binread(path)
550   end
552   def command(str)
553     $stderr.puts str if verbose?
554     system str or raise RuntimeError, "'system #{str}' failed"
555   end
557   def ruby(str)
558     command config('rubyprog') + ' ' + str
559   end
560   
561   def make(task = '')
562     command config('makeprog') + ' ' + task
563   end
565   def extdir?(dir)
566     File.exist?(dir + '/MANIFEST')
567   end
569   def all_files_in(dirname)
570     Dir.open(dirname) {|d|
571       return d.select {|ent| File.file?("#{dirname}/#{ent}") }
572     }
573   end
575   REJECT_DIRS = %w(
576     CVS SCCS RCS CVS.adm .svn
577   )
579   def all_dirs_in(dirname)
580     Dir.open(dirname) {|d|
581       return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS
582     }
583   end
589 # Main Installer
592 module HookUtils
594   def run_hook(name)
595     try_run_hook "#{curr_srcdir()}/#{name}" or
596     try_run_hook "#{curr_srcdir()}/#{name}.rb"
597   end
599   def try_run_hook(fname)
600     return false unless File.file?(fname)
601     begin
602       instance_eval File.read(fname), fname, 1
603     rescue
604       setup_rb_error "hook #{fname} failed:\n" + $!.message
605     end
606     true
607   end
612 module HookScriptAPI
614   def get_config(key)
615     @config[key]
616   end
618   alias config get_config
620   def set_config(key, val)
621     @config[key] = val
622   end
624   #
625   # srcdir/objdir (works only in the package directory)
626   #
628   #abstract srcdir_root
629   #abstract objdir_root
630   #abstract relpath
632   def curr_srcdir
633     "#{srcdir_root()}/#{relpath()}"
634   end
636   def curr_objdir
637     "#{objdir_root()}/#{relpath()}"
638   end
640   def srcfile(path)
641     "#{curr_srcdir()}/#{path}"
642   end
644   def srcexist?(path)
645     File.exist?(srcfile(path))
646   end
648   def srcdirectory?(path)
649     File.dir?(srcfile(path))
650   end
651   
652   def srcfile?(path)
653     File.file? srcfile(path)
654   end
656   def srcentries(path = '.')
657     Dir.open("#{curr_srcdir()}/#{path}") {|d|
658       return d.to_a - %w(. ..)
659     }
660   end
662   def srcfiles(path = '.')
663     srcentries(path).select {|fname|
664       File.file?(File.join(curr_srcdir(), path, fname))
665     }
666   end
668   def srcdirectories(path = '.')
669     srcentries(path).select {|fname|
670       File.dir?(File.join(curr_srcdir(), path, fname))
671     }
672   end
677 class ToplevelInstaller
679   Version   = '3.3.1'
680   Copyright = 'Copyright (c) 2000-2004 Minero Aoki'
682   TASKS = [
683     [ 'all',      'do config, setup, then install' ],
684     [ 'config',   'saves your configurations' ],
685     [ 'show',     'shows current configuration' ],
686     [ 'setup',    'compiles ruby extentions and others' ],
687     [ 'install',  'installs files' ],
688     [ 'clean',    "does `make clean' for each extention" ],
689     [ 'distclean',"does `make distclean' for each extention" ]
690   ]
692   def ToplevelInstaller.invoke
693     instance().invoke
694   end
696   @singleton = nil
698   def ToplevelInstaller.instance
699     @singleton ||= new(File.dirname($0))
700     @singleton
701   end
703   include MetaConfigAPI
705   def initialize(ardir_root)
706     @config = nil
707     @options = { 'verbose' => true }
708     @ardir = File.expand_path(ardir_root)
709   end
711   def inspect
712     "#<#{self.class} #{__id__()}>"
713   end
715   def invoke
716     run_metaconfigs
717     case task = parsearg_global()
718     when nil, 'all'
719       @config = load_config('config')
720       parsearg_config
721       init_installers
722       exec_config
723       exec_setup
724       exec_install
725     else
726       @config = load_config(task)
727       __send__ "parsearg_#{task}"
728       init_installers
729       __send__ "exec_#{task}"
730     end
731   end
732   
733   def run_metaconfigs
734     eval_file_ifexist "#{@ardir}/metaconfig"
735   end
737   def load_config(task)
738     case task
739     when 'config'
740       ConfigTable.new
741     when 'clean', 'distclean'
742       if File.exist?(ConfigTable.savefile)
743       then ConfigTable.load
744       else ConfigTable.new
745       end
746     else
747       ConfigTable.load
748     end
749   end
751   def init_installers
752     @installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))
753   end
755   #
756   # Hook Script API bases
757   #
759   def srcdir_root
760     @ardir
761   end
763   def objdir_root
764     '.'
765   end
767   def relpath
768     '.'
769   end
771   #
772   # Option Parsing
773   #
775   def parsearg_global
776     valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/
778     while arg = ARGV.shift
779       case arg
780       when /\A\w+\z/
781         setup_rb_error "invalid task: #{arg}" unless valid_task =~ arg
782         return arg
784       when '-q', '--quiet'
785         @options['verbose'] = false
787       when       '--verbose'
788         @options['verbose'] = true
790       when '-h', '--help'
791         print_usage $stdout
792         exit 0
794       when '-v', '--version'
795         puts "#{File.basename($0)} version #{Version}"
796         exit 0
797       
798       when '--copyright'
799         puts Copyright
800         exit 0
802       else
803         setup_rb_error "unknown global option '#{arg}'"
804       end
805     end
807     nil
808   end
811   def parsearg_no_options
812     unless ARGV.empty?
813       setup_rb_error "#{task}:  unknown options: #{ARGV.join ' '}"
814     end
815   end
817   alias parsearg_show       parsearg_no_options
818   alias parsearg_setup      parsearg_no_options
819   alias parsearg_clean      parsearg_no_options
820   alias parsearg_distclean  parsearg_no_options
822   def parsearg_config
823     re = /\A--(#{ConfigTable.map {|i| i.name }.join('|')})(?:=(.*))?\z/
824     @options['config-opt'] = []
826     while i = ARGV.shift
827       if /\A--?\z/ =~ i
828         @options['config-opt'] = ARGV.dup
829         break
830       end
831       m = re.match(i)  or setup_rb_error "config: unknown option #{i}"
832       name, value = *m.to_a[1,2]
833       @config[name] = value
834     end
835   end
837   def parsearg_install
838     @options['no-harm'] = false
839     @options['install-prefix'] = ''
840     while a = ARGV.shift
841       case a
842       when /\A--no-harm\z/
843         @options['no-harm'] = true
844       when /\A--prefix=(.*)\z/
845         path = $1
846         path = File.expand_path(path) unless path[0,1] == '/'
847         @options['install-prefix'] = path
848       else
849         setup_rb_error "install: unknown option #{a}"
850       end
851     end
852   end
854   def print_usage(out)
855     out.puts 'Typical Installation Procedure:'
856     out.puts "  $ ruby #{File.basename $0} config"
857     out.puts "  $ ruby #{File.basename $0} setup"
858     out.puts "  # ruby #{File.basename $0} install (may require root privilege)"
859     out.puts
860     out.puts 'Detailed Usage:'
861     out.puts "  ruby #{File.basename $0} <global option>"
862     out.puts "  ruby #{File.basename $0} [<global options>] <task> [<task options>]"
864     fmt = "  %-24s %s\n"
865     out.puts
866     out.puts 'Global options:'
867     out.printf fmt, '-q,--quiet',   'suppress message outputs'
868     out.printf fmt, '   --verbose', 'output messages verbosely'
869     out.printf fmt, '-h,--help',    'print this message'
870     out.printf fmt, '-v,--version', 'print version and quit'
871     out.printf fmt, '   --copyright',  'print copyright and quit'
872     out.puts
873     out.puts 'Tasks:'
874     TASKS.each do |name, desc|
875       out.printf fmt, name, desc
876     end
878     fmt = "  %-24s %s [%s]\n"
879     out.puts
880     out.puts 'Options for CONFIG or ALL:'
881     ConfigTable.each do |item|
882       out.printf fmt, item.help_opt, item.description, item.help_default
883     end
884     out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
885     out.puts
886     out.puts 'Options for INSTALL:'
887     out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
888     out.printf fmt, '--prefix=path',  'install path prefix', '$prefix'
889     out.puts
890   end
892   #
893   # Task Handlers
894   #
896   def exec_config
897     @installer.exec_config
898     @config.save   # must be final
899   end
901   def exec_setup
902     @installer.exec_setup
903   end
905   def exec_install
906     @installer.exec_install
907   end
909   def exec_show
910     ConfigTable.each do |i|
911       printf "%-20s %s\n", i.name, i.value
912     end
913   end
915   def exec_clean
916     @installer.exec_clean
917   end
919   def exec_distclean
920     @installer.exec_distclean
921   end
926 class ToplevelInstallerMulti < ToplevelInstaller
928   include HookUtils
929   include HookScriptAPI
930   include FileOperations
932   def initialize(ardir)
933     super
934     @packages = all_dirs_in("#{@ardir}/packages")
935     raise 'no package exists' if @packages.empty?
936   end
938   def run_metaconfigs
939     eval_file_ifexist "#{@ardir}/metaconfig"
940     @packages.each do |name|
941       eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig"
942     end
943   end
945   def init_installers
946     @installers = {}
947     @packages.each do |pack|
948       @installers[pack] = Installer.new(@config, @options,
949                                        "#{@ardir}/packages/#{pack}",
950                                        "packages/#{pack}")
951     end
953     with    = extract_selection(config('with'))
954     without = extract_selection(config('without'))
955     @selected = @installers.keys.select {|name|
956                   (with.empty? or with.include?(name)) \
957                       and not without.include?(name)
958                 }
959   end
961   def extract_selection(list)
962     a = list.split(/,/)
963     a.each do |name|
964       setup_rb_error "no such package: #{name}"  unless @installers.key?(name)
965     end
966     a
967   end
969   def print_usage(f)
970     super
971     f.puts 'Inluded packages:'
972     f.puts '  ' + @packages.sort.join(' ')
973     f.puts
974   end
976   #
977   # multi-package metaconfig API
978   #
980   attr_reader :packages
982   def declare_packages(list)
983     raise 'package list is empty' if list.empty?
984     list.each do |name|
985       raise "directory packages/#{name} does not exist"\
986               unless File.dir?("#{@ardir}/packages/#{name}")
987     end
988     @packages = list
989   end
991   #
992   # Task Handlers
993   #
995   def exec_config
996     run_hook 'pre-config'
997     each_selected_installers {|inst| inst.exec_config }
998     run_hook 'post-config'
999     @config.save   # must be final
1000   end
1002   def exec_setup
1003     run_hook 'pre-setup'
1004     each_selected_installers {|inst| inst.exec_setup }
1005     run_hook 'post-setup'
1006   end
1008   def exec_install
1009     run_hook 'pre-install'
1010     each_selected_installers {|inst| inst.exec_install }
1011     run_hook 'post-install'
1012   end
1014   def exec_clean
1015     rm_f ConfigTable.savefile
1016     run_hook 'pre-clean'
1017     each_selected_installers {|inst| inst.exec_clean }
1018     run_hook 'post-clean'
1019   end
1021   def exec_distclean
1022     rm_f ConfigTable.savefile
1023     run_hook 'pre-distclean'
1024     each_selected_installers {|inst| inst.exec_distclean }
1025     run_hook 'post-distclean'
1026   end
1028   #
1029   # lib
1030   #
1032   def each_selected_installers
1033     Dir.mkdir 'packages' unless File.dir?('packages')
1034     @selected.each do |pack|
1035       $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose']
1036       Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
1037       Dir.chdir "packages/#{pack}"
1038       yield @installers[pack]
1039       Dir.chdir '../..'
1040     end
1041   end
1043   def verbose?
1044     @options['verbose']
1045   end
1047   def no_harm?
1048     @options['no-harm']
1049   end
1054 class Installer
1056   FILETYPES = %w( bin lib ext data )
1058   include HookScriptAPI
1059   include HookUtils
1060   include FileOperations
1062   def initialize(config, opt, srcroot, objroot)
1063     @config = config
1064     @options = opt
1065     @srcdir = File.expand_path(srcroot)
1066     @objdir = File.expand_path(objroot)
1067     @currdir = '.'
1068   end
1070   def inspect
1071     "#<#{self.class} #{File.basename(@srcdir)}>"
1072   end
1074   #
1075   # Hook Script API base methods
1076   #
1078   def srcdir_root
1079     @srcdir
1080   end
1082   def objdir_root
1083     @objdir
1084   end
1086   def relpath
1087     @currdir
1088   end
1090   #
1091   # configs/options
1092   #
1094   def no_harm?
1095     @options['no-harm']
1096   end
1098   def verbose?
1099     @options['verbose']
1100   end
1102   def verbose_off
1103     begin
1104       save, @options['verbose'] = @options['verbose'], false
1105       yield
1106     ensure
1107       @options['verbose'] = save
1108     end
1109   end
1111   #
1112   # TASK config
1113   #
1115   def exec_config
1116     exec_task_traverse 'config'
1117   end
1119   def config_dir_bin(rel)
1120   end
1122   def config_dir_lib(rel)
1123   end
1125   def config_dir_ext(rel)
1126     extconf if extdir?(curr_srcdir())
1127   end
1129   def extconf
1130     opt = @options['config-opt'].join(' ')
1131     command "#{config('rubyprog')} #{curr_srcdir()}/extconf.rb #{opt}"
1132   end
1134   def config_dir_data(rel)
1135   end
1137   #
1138   # TASK setup
1139   #
1141   def exec_setup
1142     exec_task_traverse 'setup'
1143   end
1145   def setup_dir_bin(rel)
1146     all_files_in(curr_srcdir()).each do |fname|
1147       adjust_shebang "#{curr_srcdir()}/#{fname}"
1148     end
1149   end
1151   def adjust_shebang(path)
1152     return if no_harm?
1153     tmpfile = File.basename(path) + '.tmp'
1154     begin
1155       File.open(path, 'rb') {|r|
1156         first = r.gets
1157         return unless File.basename(config('rubypath')) == 'ruby'
1158         return unless File.basename(first.sub(/\A\#!/, '').split[0]) == 'ruby'
1159         $stderr.puts "adjusting shebang: #{File.basename(path)}" if verbose?
1160         File.open(tmpfile, 'wb') {|w|
1161           w.print first.sub(/\A\#!\s*\S+/, '#! ' + config('rubypath'))
1162           w.write r.read
1163         }
1164         move_file tmpfile, File.basename(path)
1165       }
1166     ensure
1167       File.unlink tmpfile if File.exist?(tmpfile)
1168     end
1169   end
1171   def setup_dir_lib(rel)
1172   end
1174   def setup_dir_ext(rel)
1175     make if extdir?(curr_srcdir())
1176   end
1178   def setup_dir_data(rel)
1179   end
1181   #
1182   # TASK install
1183   #
1185   def exec_install
1186     rm_f 'InstalledFiles'
1187     exec_task_traverse 'install'
1188   end
1190   def install_dir_bin(rel)
1191     install_files collect_filenames_auto(), "#{config('bindir')}/#{rel}", 0755
1192   end
1194   def install_dir_lib(rel)
1195     install_files ruby_scripts(), "#{config('rbdir')}/#{rel}", 0644
1196   end
1198   def install_dir_ext(rel)
1199     return unless extdir?(curr_srcdir())
1200     install_files ruby_extentions('.'),
1201                   "#{config('sodir')}/#{File.dirname(rel)}",
1202                   0555
1203   end
1205   def install_dir_data(rel)
1206     install_files collect_filenames_auto(), "#{config('datadir')}/#{rel}", 0644
1207   end
1209   def install_files(list, dest, mode)
1210     mkdir_p dest, @options['install-prefix']
1211     list.each do |fname|
1212       install fname, dest, mode, @options['install-prefix']
1213     end
1214   end
1216   def ruby_scripts
1217     collect_filenames_auto().select {|n| /\.rb\z/ =~ n }
1218   end
1219   
1220   # picked up many entries from cvs-1.11.1/src/ignore.c
1221   reject_patterns = %w( 
1222     core RCSLOG tags TAGS .make.state
1223     .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
1224     *~ *.old *.bak *.BAK *.orig *.rej _$* *$
1226     *.org *.in .*
1227   )
1228   mapping = {
1229     '.' => '\.',
1230     '$' => '\$',
1231     '#' => '\#',
1232     '*' => '.*'
1233   }
1234   REJECT_PATTERNS = Regexp.new('\A(?:' +
1235                                reject_patterns.map {|pat|
1236                                  pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] }
1237                                }.join('|') +
1238                                ')\z')
1240   def collect_filenames_auto
1241     mapdir((existfiles() - hookfiles()).reject {|fname|
1242              REJECT_PATTERNS =~ fname
1243            })
1244   end
1246   def existfiles
1247     all_files_in(curr_srcdir()) | all_files_in('.')
1248   end
1250   def hookfiles
1251     %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
1252       %w( config setup install clean ).map {|t| sprintf(fmt, t) }
1253     }.flatten
1254   end
1256   def mapdir(filelist)
1257     filelist.map {|fname|
1258       if File.exist?(fname)   # objdir
1259         fname
1260       else                    # srcdir
1261         File.join(curr_srcdir(), fname)
1262       end
1263     }
1264   end
1266   def ruby_extentions(dir)
1267     Dir.open(dir) {|d|
1268       ents = d.select {|fname| /\.#{::Config::CONFIG['DLEXT']}\z/ =~ fname }
1269       if ents.empty?
1270         setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
1271       end
1272       return ents
1273     }
1274   end
1276   #
1277   # TASK clean
1278   #
1280   def exec_clean
1281     exec_task_traverse 'clean'
1282     rm_f ConfigTable.savefile
1283     rm_f 'InstalledFiles'
1284   end
1286   def clean_dir_bin(rel)
1287   end
1289   def clean_dir_lib(rel)
1290   end
1292   def clean_dir_ext(rel)
1293     return unless extdir?(curr_srcdir())
1294     make 'clean' if File.file?('Makefile')
1295   end
1297   def clean_dir_data(rel)
1298   end
1300   #
1301   # TASK distclean
1302   #
1304   def exec_distclean
1305     exec_task_traverse 'distclean'
1306     rm_f ConfigTable.savefile
1307     rm_f 'InstalledFiles'
1308   end
1310   def distclean_dir_bin(rel)
1311   end
1313   def distclean_dir_lib(rel)
1314   end
1316   def distclean_dir_ext(rel)
1317     return unless extdir?(curr_srcdir())
1318     make 'distclean' if File.file?('Makefile')
1319   end
1321   #
1322   # lib
1323   #
1325   def exec_task_traverse(task)
1326     run_hook "pre-#{task}"
1327     FILETYPES.each do |type|
1328       if config('without-ext') == 'yes' and type == 'ext'
1329         $stderr.puts 'skipping ext/* by user option' if verbose?
1330         next
1331       end
1332       traverse task, type, "#{task}_dir_#{type}"
1333     end
1334     run_hook "post-#{task}"
1335   end
1337   def traverse(task, rel, mid)
1338     dive_into(rel) {
1339       run_hook "pre-#{task}"
1340       __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1341       all_dirs_in(curr_srcdir()).each do |d|
1342         traverse task, "#{rel}/#{d}", mid
1343       end
1344       run_hook "post-#{task}"
1345     }
1346   end
1348   def dive_into(rel)
1349     return unless File.dir?("#{@srcdir}/#{rel}")
1351     dir = File.basename(rel)
1352     Dir.mkdir dir unless File.dir?(dir)
1353     prevdir = Dir.pwd
1354     Dir.chdir dir
1355     $stderr.puts '---> ' + rel if verbose?
1356     @currdir = rel
1357     yield
1358     Dir.chdir prevdir
1359     $stderr.puts '<--- ' + rel if verbose?
1360     @currdir = File.dirname(rel)
1361   end
1366 if $0 == __FILE__
1367   begin
1368     if multipackage_install?
1369       ToplevelInstallerMulti.invoke
1370     else
1371       ToplevelInstaller.invoke
1372     end
1373   rescue SetupError
1374     raise if $DEBUG
1375     $stderr.puts $!.message
1376     $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
1377     exit 1
1378   end