4 # Copyright (c) 2000-2004 Minero Aoki
6 # This program is free software.
7 # You can distribute/modify this program under the terms of
8 # the GNU LGPL, Lesser General Public License version 2.1.
11 unless Enumerable.method_defined?(:map) # Ruby 1.4.6
17 unless File.respond_to?(:read) # Ruby 1.6
25 def File.binread(fname)
26 open(fname, 'rb') {|f|
31 # for corrupted windows stat(2)
33 File.directory?((path[-1,1] == '/') ? path : path + '/')
37 class SetupError < StandardError; end
39 def setup_rb_error(msg)
47 if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
49 require arg.split(/=/, 2)[1]
55 def multipackage_install?
56 FileTest.directory?(File.dirname($0) + '/packages')
61 def initialize(name, template, default, desc)
65 @default = default.dup.freeze
70 attr_reader :description
72 attr_accessor :default
73 alias help_default default
76 "--#{@name}=#{@template}"
84 @value.gsub(%r<\$([^/]+)>) { table[$1] }
94 setup_rb_error "config: --#{name} requires argument" unless val
99 class BoolItem < ConfigItem
111 return 'yes' unless val
112 unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ val
113 setup_rb_error "config: --#{@name} accepts only yes/no for argument"
115 (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no'
119 class PathItem < ConfigItem
127 setup_rb_error "config: --#{@name} requires argument" unless path
128 path[0,1] == '$' ? path : File.expand_path(path)
132 class ProgramItem < ConfigItem
138 class SelectItem < ConfigItem
139 def initialize(name, template, default, desc)
141 @ok = template.split('/')
151 unless @ok.include?(val.strip)
152 setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
158 class PackageSelectionItem < ConfigItem
159 def initialize(name, template, default, help_default, desc)
160 super name, template, default, desc
161 @help_default = help_default
164 attr_reader :help_default
173 unless File.dir?("packages/#{val}")
174 setup_rb_error "config: no such package: #{val}"
180 class ConfigTable_class
182 def initialize(items)
188 ALIASES.each do |ali, name|
189 @table[ali] = @table[name]
204 @table[name] or raise ArgumentError, "no such config item: #{name}"
209 @table[item.name] = item
214 @items.delete_if {|i| i.name == name }
215 @table.delete_if {|name, i| i.name == name }
230 File.foreach(savefile()) do |line|
231 k, v = *line.split(/=/, 2)
236 setup_rb_error $!.message + "#{File.basename($0)} config first"
241 @items.each {|i| i.value }
242 File.open(savefile(), 'w') {|f|
244 f.printf "%s=%s\n", i.name, i.value if i.value
250 lookup(key).eval(self)
261 rubypath = c['bindir'] + '/' + c['ruby_install_name']
263 major = c['MAJOR'].to_i
264 minor = c['MINOR'].to_i
265 teeny = c['TEENY'].to_i
266 version = "#{major}.#{minor}"
268 # ruby ver. >= 1.4.4?
269 newpath_p = ((major >= 2) or
272 ((minor == 4) and (teeny >= 4)))))
276 _stdruby = c['rubylibdir']
277 _siteruby = c['sitedir']
278 _siterubyver = c['sitelibdir']
279 _siterubyverarch = c['sitearchdir']
281 # 1.4.4 <= V <= 1.6.3
282 _stdruby = "$prefix/lib/ruby/#{version}"
283 _siteruby = c['sitedir']
284 _siterubyver = "$siteruby/#{version}"
285 _siterubyverarch = "$siterubyver/#{c['arch']}"
288 _stdruby = "$prefix/lib/ruby/#{version}"
289 _siteruby = "$prefix/lib/ruby/#{version}/site_ruby"
290 _siterubyver = _siteruby
291 _siterubyverarch = "$siterubyver/#{c['arch']}"
293 libdir = '-* dummy libdir *-'
294 stdruby = '-* dummy rubylibdir *-'
295 siteruby = '-* dummy site_ruby *-'
296 siterubyver = '-* dummy site_ruby version *-'
297 parameterize = lambda {|path|
298 path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')\
299 .sub(/\A#{Regexp.quote(libdir)}/, '$libdir')\
300 .sub(/\A#{Regexp.quote(stdruby)}/, '$stdruby')\
301 .sub(/\A#{Regexp.quote(siteruby)}/, '$siteruby')\
302 .sub(/\A#{Regexp.quote(siterubyver)}/, '$siterubyver')
304 libdir = parameterize.call(c['libdir'])
305 stdruby = parameterize.call(_stdruby)
306 siteruby = parameterize.call(_siteruby)
307 siterubyver = parameterize.call(_siterubyver)
308 siterubyverarch = parameterize.call(_siterubyverarch)
310 if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
311 makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
317 PathItem.new('prefix', 'path', c['prefix'],
318 'path prefix of target environment'),
319 PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
320 'the directory for commands'),
321 PathItem.new('libdir', 'path', libdir,
322 'the directory for libraries'),
323 PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
324 'the directory for shared data'),
325 PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
326 'the directory for man pages'),
327 PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
328 'the directory for man pages'),
329 PathItem.new('stdruby', 'path', stdruby,
330 'the directory for standard ruby libraries'),
331 PathItem.new('siteruby', 'path', siteruby,
332 'the directory for version-independent aux ruby libraries'),
333 PathItem.new('siterubyver', 'path', siterubyver,
334 'the directory for aux ruby libraries'),
335 PathItem.new('siterubyverarch', 'path', siterubyverarch,
336 'the directory for aux ruby binaries'),
337 PathItem.new('rbdir', 'path', '$siterubyver',
338 'the directory for ruby scripts'),
339 PathItem.new('sodir', 'path', '$siterubyverarch',
340 'the directory for ruby extentions'),
341 PathItem.new('rubypath', 'path', rubypath,
342 'the path to set to #! line'),
343 ProgramItem.new('rubyprog', 'name', rubypath,
344 'the ruby program using for installation'),
345 ProgramItem.new('makeprog', 'name', makeprog,
346 'the make program to compile ruby extentions'),
347 SelectItem.new('shebang', 'all/ruby/never', 'ruby',
348 'shebang line (#!) editing mode'),
349 BoolItem.new('without-ext', 'yes/no', 'no',
350 'does not compile/install ruby extentions')
352 class ConfigTable_class # open again
354 'std-ruby' => 'stdruby',
355 'site-ruby-common' => 'siteruby', # For backward compatibility
356 'site-ruby' => 'siterubyver', # For backward compatibility
357 'bin-dir' => 'bindir',
360 'data-dir' => 'datadir',
361 'ruby-path' => 'rubypath',
362 'ruby-prog' => 'rubyprog',
363 'ruby' => 'rubyprog',
364 'make-prog' => 'makeprog',
368 multipackage_conf = [
369 PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
370 'package names that you want to install'),
371 PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
372 'package names that you do not want to install')
374 if multipackage_install?
375 ConfigTable = ConfigTable_class.new(common_conf + multipackage_conf)
377 ConfigTable = ConfigTable_class.new(common_conf)
383 def eval_file_ifexist(fname)
384 instance_eval File.read(fname), fname, 1 if File.file?(fname)
388 ConfigTable.map {|i| i.name }
392 ConfigTable.key?(name)
395 def bool_config?(name)
396 ConfigTable.lookup(name).config_type == 'bool'
399 def path_config?(name)
400 ConfigTable.lookup(name).config_type == 'path'
403 def value_config?(name)
404 case ConfigTable.lookup(name).config_type
416 def add_bool_config(name, default, desc)
417 ConfigTable.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
420 def add_path_config(name, default, desc)
421 ConfigTable.add PathItem.new(name, 'path', default, desc)
424 def set_config_default(name, default)
425 ConfigTable.lookup(name).default = default
428 def remove_config(name)
429 ConfigTable.remove(name)
439 module FileOperations
441 def mkdir_p(dirname, prefix = nil)
442 dirname = prefix + File.expand_path(dirname) if prefix
443 $stderr.puts "mkdir -p #{dirname}" if verbose?
446 # does not check '/'... it's too abnormal case
447 dirs = File.expand_path(dirname).split(%r<(?=/)>)
448 if /\A[a-z]:\z/i =~ dirs[0]
450 dirs[0] = disk + dirs[0]
452 dirs.each_index do |idx|
453 path = dirs[0..idx].join('')
454 Dir.mkdir path unless File.dir?(path)
459 $stderr.puts "rm -f #{fname}" if verbose?
462 if File.exist?(fname) or File.symlink?(fname)
463 File.chmod 0777, fname
469 $stderr.puts "rm -rf #{dn}" if verbose?
473 Dir.foreach('.') do |fn|
490 def move_file(src, dest)
491 File.unlink dest if File.exist?(dest)
493 File.rename src, dest
495 File.open(dest, 'wb') {|f| f.write File.binread(src) }
496 File.chmod File.stat(src).mode, dest
501 def install(from, dest, mode, prefix = nil)
502 $stderr.puts "install #{from} #{dest}" if verbose?
505 realdest = prefix ? prefix + File.expand_path(dest) : dest
506 realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
507 str = File.binread(from)
508 if diff?(str, realdest)
510 rm_f realdest if File.exist?(realdest)
512 File.open(realdest, 'wb') {|f|
515 File.chmod mode, realdest
517 File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
519 f.puts realdest.sub(prefix, '')
527 def diff?(new_content, path)
528 return true unless File.exist?(path)
529 new_content != File.binread(path)
533 $stderr.puts str if verbose?
534 system str or raise RuntimeError, "'system #{str}' failed"
538 command config('rubyprog') + ' ' + str
542 command config('makeprog') + ' ' + task
546 File.exist?(dir + '/MANIFEST')
549 def all_files_in(dirname)
550 Dir.open(dirname) {|d|
551 return d.select {|ent| File.file?("#{dirname}/#{ent}") }
556 CVS SCCS RCS CVS.adm .svn
559 def all_dirs_in(dirname)
560 Dir.open(dirname) {|d|
561 return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS
575 try_run_hook "#{curr_srcdir()}/#{name}" or
576 try_run_hook "#{curr_srcdir()}/#{name}.rb"
579 def try_run_hook(fname)
580 return false unless File.file?(fname)
582 instance_eval File.read(fname), fname, 1
584 setup_rb_error "hook #{fname} failed:\n" + $!.message
598 alias config get_config
600 def set_config(key, val)
605 # srcdir/objdir (works only in the package directory)
608 #abstract srcdir_root
609 #abstract objdir_root
613 "#{srcdir_root()}/#{relpath()}"
617 "#{objdir_root()}/#{relpath()}"
621 "#{curr_srcdir()}/#{path}"
625 File.exist?(srcfile(path))
628 def srcdirectory?(path)
629 File.dir?(srcfile(path))
633 File.file? srcfile(path)
636 def srcentries(path = '.')
637 Dir.open("#{curr_srcdir()}/#{path}") {|d|
638 return d.to_a - %w(. ..)
642 def srcfiles(path = '.')
643 srcentries(path).select {|fname|
644 File.file?(File.join(curr_srcdir(), path, fname))
648 def srcdirectories(path = '.')
649 srcentries(path).select {|fname|
650 File.dir?(File.join(curr_srcdir(), path, fname))
657 class ToplevelInstaller
660 Copyright = 'Copyright (c) 2000-2004 Minero Aoki'
663 [ 'all', 'do config, setup, then install' ],
664 [ 'config', 'saves your configurations' ],
665 [ 'show', 'shows current configuration' ],
666 [ 'setup', 'compiles ruby extentions and others' ],
667 [ 'install', 'installs files' ],
668 [ 'clean', "does `make clean' for each extention" ],
669 [ 'distclean',"does `make distclean' for each extention" ]
672 def ToplevelInstaller.invoke
678 def ToplevelInstaller.instance
679 @singleton ||= new(File.dirname($0))
683 include MetaConfigAPI
685 def initialize(ardir_root)
687 @options = { 'verbose' => true }
688 @ardir = File.expand_path(ardir_root)
692 "#<#{self.class} #{__id__()}>"
697 case task = parsearg_global()
699 @config = load_config('config')
706 @config = load_config(task)
707 __send__ "parsearg_#{task}"
709 __send__ "exec_#{task}"
714 eval_file_ifexist "#{@ardir}/metaconfig"
717 def load_config(task)
721 when 'clean', 'distclean'
722 if File.exist?(ConfigTable.savefile)
723 then ConfigTable.load
732 @installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))
736 # Hook Script API bases
756 valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/
758 while arg = ARGV.shift
761 setup_rb_error "invalid task: #{arg}" unless valid_task =~ arg
765 @options['verbose'] = false
768 @options['verbose'] = true
774 when '-v', '--version'
775 puts "#{File.basename($0)} version #{Version}"
783 setup_rb_error "unknown global option '#{arg}'"
791 def parsearg_no_options
793 setup_rb_error "#{task}: unknown options: #{ARGV.join ' '}"
797 alias parsearg_show parsearg_no_options
798 alias parsearg_setup parsearg_no_options
799 alias parsearg_clean parsearg_no_options
800 alias parsearg_distclean parsearg_no_options
803 re = /\A--(#{ConfigTable.map {|i| i.name }.join('|')})(?:=(.*))?\z/
804 @options['config-opt'] = []
808 @options['config-opt'] = ARGV.dup
811 m = re.match(i) or setup_rb_error "config: unknown option #{i}"
812 name, value = *m.to_a[1,2]
813 @config[name] = value
818 @options['no-harm'] = false
819 @options['install-prefix'] = ''
823 @options['no-harm'] = true
824 when /\A--prefix=(.*)\z/
826 path = File.expand_path(path) unless path[0,1] == '/'
827 @options['install-prefix'] = path
829 setup_rb_error "install: unknown option #{a}"
835 out.puts 'Typical Installation Procedure:'
836 out.puts " $ ruby #{File.basename $0} config"
837 out.puts " $ ruby #{File.basename $0} setup"
838 out.puts " # ruby #{File.basename $0} install (may require root privilege)"
840 out.puts 'Detailed Usage:'
841 out.puts " ruby #{File.basename $0} <global option>"
842 out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
846 out.puts 'Global options:'
847 out.printf fmt, '-q,--quiet', 'suppress message outputs'
848 out.printf fmt, ' --verbose', 'output messages verbosely'
849 out.printf fmt, '-h,--help', 'print this message'
850 out.printf fmt, '-v,--version', 'print version and quit'
851 out.printf fmt, ' --copyright', 'print copyright and quit'
854 TASKS.each do |name, desc|
855 out.printf fmt, name, desc
858 fmt = " %-24s %s [%s]\n"
860 out.puts 'Options for CONFIG or ALL:'
861 ConfigTable.each do |item|
862 out.printf fmt, item.help_opt, item.description, item.help_default
864 out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
866 out.puts 'Options for INSTALL:'
867 out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
868 out.printf fmt, '--prefix=path', 'install path prefix', '$prefix'
877 @installer.exec_config
878 @config.save # must be final
882 @installer.exec_setup
886 @installer.exec_install
890 ConfigTable.each do |i|
891 printf "%-20s %s\n", i.name, i.value
896 @installer.exec_clean
900 @installer.exec_distclean
906 class ToplevelInstallerMulti < ToplevelInstaller
909 include HookScriptAPI
910 include FileOperations
912 def initialize(ardir)
914 @packages = all_dirs_in("#{@ardir}/packages")
915 raise 'no package exists' if @packages.empty?
919 eval_file_ifexist "#{@ardir}/metaconfig"
920 @packages.each do |name|
921 eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig"
927 @packages.each do |pack|
928 @installers[pack] = Installer.new(@config, @options,
929 "#{@ardir}/packages/#{pack}",
933 with = extract_selection(config('with'))
934 without = extract_selection(config('without'))
935 @selected = @installers.keys.select {|name|
936 (with.empty? or with.include?(name)) \
937 and not without.include?(name)
941 def extract_selection(list)
944 setup_rb_error "no such package: #{name}" unless @installers.key?(name)
951 f.puts 'Inluded packages:'
952 f.puts ' ' + @packages.sort.join(' ')
957 # multi-package metaconfig API
960 attr_reader :packages
962 def declare_packages(list)
963 raise 'package list is empty' if list.empty?
965 raise "directory packages/#{name} does not exist"\
966 unless File.dir?("#{@ardir}/packages/#{name}")
976 run_hook 'pre-config'
977 each_selected_installers {|inst| inst.exec_config }
978 run_hook 'post-config'
979 @config.save # must be final
984 each_selected_installers {|inst| inst.exec_setup }
985 run_hook 'post-setup'
989 run_hook 'pre-install'
990 each_selected_installers {|inst| inst.exec_install }
991 run_hook 'post-install'
995 rm_f ConfigTable.savefile
997 each_selected_installers {|inst| inst.exec_clean }
998 run_hook 'post-clean'
1002 rm_f ConfigTable.savefile
1003 run_hook 'pre-distclean'
1004 each_selected_installers {|inst| inst.exec_distclean }
1005 run_hook 'post-distclean'
1012 def each_selected_installers
1013 Dir.mkdir 'packages' unless File.dir?('packages')
1014 @selected.each do |pack|
1015 $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose']
1016 Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
1017 Dir.chdir "packages/#{pack}"
1018 yield @installers[pack]
1036 FILETYPES = %w( bin lib ext data )
1038 include HookScriptAPI
1040 include FileOperations
1042 def initialize(config, opt, srcroot, objroot)
1045 @srcdir = File.expand_path(srcroot)
1046 @objdir = File.expand_path(objroot)
1051 "#<#{self.class} #{File.basename(@srcdir)}>"
1055 # Hook Script API base methods
1084 save, @options['verbose'] = @options['verbose'], false
1087 @options['verbose'] = save
1096 exec_task_traverse 'config'
1099 def config_dir_bin(rel)
1102 def config_dir_lib(rel)
1105 def config_dir_ext(rel)
1106 extconf if extdir?(curr_srcdir())
1110 opt = @options['config-opt'].join(' ')
1111 command "#{config('rubyprog')} #{curr_srcdir()}/extconf.rb #{opt}"
1114 def config_dir_data(rel)
1122 exec_task_traverse 'setup'
1125 def setup_dir_bin(rel)
1126 all_files_in(curr_srcdir()).each do |fname|
1127 adjust_shebang "#{curr_srcdir()}/#{fname}"
1131 def adjust_shebang(path)
1133 tmpfile = File.basename(path) + '.tmp'
1135 File.open(path, 'rb') {|r|
1137 return unless File.basename(config('rubypath')) == 'ruby'
1138 return unless File.basename(first.sub(/\A\#!/, '').split[0]) == 'ruby'
1139 $stderr.puts "adjusting shebang: #{File.basename(path)}" if verbose?
1140 File.open(tmpfile, 'wb') {|w|
1141 w.print first.sub(/\A\#!\s*\S+/, '#! ' + config('rubypath'))
1144 move_file tmpfile, File.basename(path)
1147 File.unlink tmpfile if File.exist?(tmpfile)
1151 def setup_dir_lib(rel)
1154 def setup_dir_ext(rel)
1155 make if extdir?(curr_srcdir())
1158 def setup_dir_data(rel)
1166 rm_f 'InstalledFiles'
1167 exec_task_traverse 'install'
1170 def install_dir_bin(rel)
1171 install_files collect_filenames_auto(), "#{config('bindir')}/#{rel}", 0755
1174 def install_dir_lib(rel)
1175 install_files ruby_scripts(), "#{config('rbdir')}/#{rel}", 0644
1178 def install_dir_ext(rel)
1179 return unless extdir?(curr_srcdir())
1180 install_files ruby_extentions('.'),
1181 "#{config('sodir')}/#{File.dirname(rel)}",
1185 def install_dir_data(rel)
1186 install_files collect_filenames_auto(), "#{config('datadir')}/#{rel}", 0644
1189 def install_files(list, dest, mode)
1190 mkdir_p dest, @options['install-prefix']
1191 list.each do |fname|
1192 install fname, dest, mode, @options['install-prefix']
1197 collect_filenames_auto().select {|n| /\.rb\z/ =~ n }
1200 # picked up many entries from cvs-1.11.1/src/ignore.c
1201 reject_patterns = %w(
1202 core RCSLOG tags TAGS .make.state
1203 .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
1204 *~ *.old *.bak *.BAK *.orig *.rej _$* *$
1214 REJECT_PATTERNS = Regexp.new('\A(?:' +
1215 reject_patterns.map {|pat|
1216 pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] }
1220 def collect_filenames_auto
1221 mapdir((existfiles() - hookfiles()).reject {|fname|
1222 REJECT_PATTERNS =~ fname
1227 all_files_in(curr_srcdir()) | all_files_in('.')
1231 %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
1232 %w( config setup install clean ).map {|t| sprintf(fmt, t) }
1236 def mapdir(filelist)
1237 filelist.map {|fname|
1238 if File.exist?(fname) # objdir
1241 File.join(curr_srcdir(), fname)
1246 def ruby_extentions(dir)
1248 ents = d.select {|fname| /\.#{::Config::CONFIG['DLEXT']}\z/ =~ fname }
1250 setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
1261 exec_task_traverse 'clean'
1262 rm_f ConfigTable.savefile
1263 rm_f 'InstalledFiles'
1266 def clean_dir_bin(rel)
1269 def clean_dir_lib(rel)
1272 def clean_dir_ext(rel)
1273 return unless extdir?(curr_srcdir())
1274 make 'clean' if File.file?('Makefile')
1277 def clean_dir_data(rel)
1285 exec_task_traverse 'distclean'
1286 rm_f ConfigTable.savefile
1287 rm_f 'InstalledFiles'
1290 def distclean_dir_bin(rel)
1293 def distclean_dir_lib(rel)
1296 def distclean_dir_ext(rel)
1297 return unless extdir?(curr_srcdir())
1298 make 'distclean' if File.file?('Makefile')
1305 def exec_task_traverse(task)
1306 run_hook "pre-#{task}"
1307 FILETYPES.each do |type|
1308 if config('without-ext') == 'yes' and type == 'ext'
1309 $stderr.puts 'skipping ext/* by user option' if verbose?
1312 traverse task, type, "#{task}_dir_#{type}"
1314 run_hook "post-#{task}"
1317 def traverse(task, rel, mid)
1319 run_hook "pre-#{task}"
1320 __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1321 all_dirs_in(curr_srcdir()).each do |d|
1322 traverse task, "#{rel}/#{d}", mid
1324 run_hook "post-#{task}"
1329 return unless File.dir?("#{@srcdir}/#{rel}")
1331 dir = File.basename(rel)
1332 Dir.mkdir dir unless File.dir?(dir)
1335 $stderr.puts '---> ' + rel if verbose?
1339 $stderr.puts '<--- ' + rel if verbose?
1340 @currdir = File.dirname(rel)
1348 if multipackage_install?
1349 ToplevelInstallerMulti.invoke
1351 ToplevelInstaller.invoke
1355 $stderr.puts $!.message
1356 $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."