]> git.pld-linux.org Git - packages/amrita.git/blob - setup.rb
- added
[packages/amrita.git] / setup.rb
1 #
2 # setup.rb
3 #
4 # Copyright (c) 2000-2004 Minero Aoki
5 #
6 # This program is free software.
7 # You can distribute/modify this program under the terms of
8 # the GNU Lesser General Public License version 2.1.
9 #
10
11 #
12 # For backward compatibility
13 #
14
15 unless Enumerable.method_defined?(:map)
16   module Enumerable
17     alias map collect
18   end
19 end
20
21 unless Enumerable.method_defined?(:detect)
22   module Enumerable
23     alias detect find
24   end
25 end
26
27 unless Enumerable.method_defined?(:select)
28   module Enumerable
29     alias select find_all
30   end
31 end
32
33 unless Enumerable.method_defined?(:reject)
34   module Enumerable
35     def reject
36       result = []
37       each do |i|
38         result.push i unless yield(i)
39       end
40       result
41     end
42   end
43 end
44
45 unless Enumerable.method_defined?(:inject)
46   module Enumerable
47     def inject(result)
48       each do |i|
49         result = yield(result, i)
50       end
51       result
52     end
53   end
54 end
55
56 unless Enumerable.method_defined?(:any?)
57   module Enumerable
58     def any?
59       each do |i|
60         return true if yield(i)
61       end
62       false
63     end
64   end
65 end
66
67 unless File.respond_to?(:read)
68   def File.read(fname)
69     open(fname) {|f|
70       return f.read
71     }
72   end
73 end
74
75 #
76 # Application independent utilities
77 #
78
79 def File.binread(fname)
80   open(fname, 'rb') {|f|
81     return f.read
82   }
83 end
84
85 # for corrupted windows stat(2)
86 def File.dir?(path)
87   File.directory?((path[-1,1] == '/') ? path : path + '/')
88 end
89
90 #
91 # Config
92 #
93
94 if arg = ARGV.detect{|arg| /\A--rbconfig=/ =~ arg }
95   ARGV.delete(arg)
96   require arg.split(/=/, 2)[1]
97   $".push 'rbconfig.rb'
98 else
99   require 'rbconfig'
100 end
101
102 def multipackage_install?
103   FileTest.directory?(File.dirname($0) + '/packages')
104 end
105
106
107 class ConfigTable
108
109   c = ::Config::CONFIG
110
111   rubypath = c['bindir'] + '/' + c['ruby_install_name']
112
113   major = c['MAJOR'].to_i
114   minor = c['MINOR'].to_i
115   teeny = c['TEENY'].to_i
116   version = "#{major}.#{minor}"
117
118   # ruby ver. >= 1.4.4?
119   newpath_p = ((major >= 2) or
120                ((major == 1) and
121                 ((minor >= 5) or
122                  ((minor == 4) and (teeny >= 4)))))
123   
124   subprefix = lambda {|path|
125     path.sub(/\A#{Regexp.quote(c['prefix'])}/o, '$prefix')
126   }
127
128   if c['rubylibdir']
129     # V < 1.6.3
130     stdruby    = subprefix.call(c['rubylibdir'])
131     siteruby   = subprefix.call(c['sitedir'])
132     versite    = subprefix.call(c['sitelibdir'])
133     sodir      = subprefix.call(c['sitearchdir'])
134   elsif newpath_p
135     # 1.4.4 <= V <= 1.6.3
136     stdruby    = "$prefix/lib/ruby/#{version}"
137     siteruby   = subprefix.call(c['sitedir'])
138     versite    = siteruby + '/' + version
139     sodir      = "$site-ruby/#{c['arch']}"
140   else
141     # V < 1.4.4
142     stdruby    = "$prefix/lib/ruby/#{version}"
143     siteruby   = "$prefix/lib/ruby/#{version}/site_ruby"
144     versite    = siteruby
145     sodir      = "$site-ruby/#{c['arch']}"
146   end
147
148   if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
149     makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
150   else
151     makeprog = 'make'
152   end
153
154   common_descripters = [
155     [ 'prefix',    [ c['prefix'],
156                      'path',
157                      'path prefix of target environment' ] ],
158     [ 'std-ruby',  [ stdruby,
159                      'path',
160                      'the directory for standard ruby libraries' ] ],
161     [ 'site-ruby-common', [ siteruby,
162                      'path',
163                      'the directory for version-independent non-standard ruby libraries' ] ],
164     [ 'site-ruby', [ versite,
165                      'path',
166                      'the directory for non-standard ruby libraries' ] ],
167     [ 'bin-dir',   [ '$prefix/bin',
168                      'path',
169                      'the directory for commands' ] ],
170     [ 'rb-dir',    [ '$site-ruby',
171                      'path',
172                      'the directory for ruby scripts' ] ],
173     [ 'so-dir',    [ sodir,
174                      'path',
175                      'the directory for ruby extentions' ] ],
176     [ 'data-dir',  [ '$prefix/share',
177                      'path',
178                      'the directory for shared data' ] ],
179     [ 'ruby-path', [ rubypath,
180                      'path',
181                      'path to set to #! line' ] ],
182     [ 'ruby-prog', [ rubypath,
183                      'name',
184                      'the ruby program using for installation' ] ],
185     [ 'make-prog', [ makeprog,
186                      'name',
187                      'the make program to compile ruby extentions' ] ],
188     [ 'without-ext', [ 'no',
189                        'yes/no',
190                        'does not compile/install ruby extentions' ] ]
191   ]
192   multipackage_descripters = [
193     [ 'with',      [ '',
194                      'name,name...',
195                      'package names that you want to install',
196                      'ALL' ] ],
197     [ 'without',   [ '',
198                      'name,name...',
199                      'package names that you do not want to install',
200                      'NONE' ] ]
201   ]
202   if multipackage_install?
203     DESCRIPTER = common_descripters + multipackage_descripters
204   else
205     DESCRIPTER = common_descripters
206   end
207
208   SAVE_FILE = 'config.save'
209
210   def ConfigTable.each_name(&block)
211     keys().each(&block)
212   end
213
214   def ConfigTable.keys
215     DESCRIPTER.map {|name, *dummy| name }
216   end
217
218   def ConfigTable.each_definition(&block)
219     DESCRIPTER.each(&block)
220   end
221
222   def ConfigTable.get_entry(name)
223     name, ent = DESCRIPTER.assoc(name)
224     ent
225   end
226
227   def ConfigTable.get_entry!(name)
228     get_entry(name) or raise ArgumentError, "no such config: #{name}"
229   end
230
231   def ConfigTable.add_entry(name, vals)
232     ConfigTable::DESCRIPTER.push [name,vals]
233   end
234
235   def ConfigTable.remove_entry(name)
236     get_entry(name) or raise ArgumentError, "no such config: #{name}"
237     DESCRIPTER.delete_if {|n, arr| n == name }
238   end
239
240   def ConfigTable.config_key?(name)
241     get_entry(name) ? true : false
242   end
243
244   def ConfigTable.bool_config?(name)
245     ent = get_entry(name) or return false
246     ent[1] == 'yes/no'
247   end
248
249   def ConfigTable.value_config?(name)
250     ent = get_entry(name) or return false
251     ent[1] != 'yes/no'
252   end
253
254   def ConfigTable.path_config?(name)
255     ent = get_entry(name) or return false
256     ent[1] == 'path'
257   end
258
259
260   class << self
261     alias newobj new
262   end
263
264   def ConfigTable.new
265     c = newobj()
266     c.initialize_from_table
267     c
268   end
269
270   def ConfigTable.load
271     c = newobj()
272     c.initialize_from_file
273     c
274   end
275
276   def initialize_from_table
277     @table = {}
278     DESCRIPTER.each do |k, (default, vname, desc, default2)|
279       @table[k] = default
280     end
281   end
282
283   def initialize_from_file
284     raise InstallError, "#{File.basename $0} config first"\
285         unless File.file?(SAVE_FILE)
286     @table = {}
287     File.foreach(SAVE_FILE) do |line|
288       k, v = line.split(/=/, 2)
289       @table[k] = v.strip
290     end
291   end
292
293   def save
294     File.open(SAVE_FILE, 'w') {|f|
295       @table.each do |k, v|
296         f.printf "%s=%s\n", k, v if v
297       end
298     }
299   end
300
301   def []=(k, v)
302     raise InstallError, "unknown config option #{k}"\
303         unless ConfigTable.config_key?(k)
304     @table[k] = v
305   end
306     
307   def [](key)
308     return nil unless @table[key]
309     @table[key].gsub(%r<\$([^/]+)>) { self[$1] }
310   end
311
312   def set_raw(key, val)
313     @table[key] = val
314   end
315
316   def get_raw(key)
317     @table[key]
318   end
319
320 end
321
322
323 module MetaConfigAPI
324
325   def eval_file_ifexist(fname)
326     instance_eval File.read(fname), fname, 1 if File.file?(fname)
327   end
328
329   def config_names
330     ConfigTable.keys
331   end
332
333   def config?(name)
334     ConfigTable.config_key?(name)
335   end
336
337   def bool_config?(name)
338     ConfigTable.bool_config?(name)
339   end
340
341   def value_config?(name)
342     ConfigTable.value_config?(name)
343   end
344
345   def path_config?(name)
346     ConfigTable.path_config?(name)
347   end
348
349   def add_config(name, argname, default, desc)
350     ConfigTable.add_entry name,[default,argname,desc]
351   end
352
353   def add_path_config(name, default, desc)
354     add_config name, 'path', default, desc
355   end
356
357   def add_bool_config(name, default, desc)
358     add_config name, 'yes/no', default ? 'yes' : 'no', desc
359   end
360
361   def set_config_default(name, default)
362     if bool_config?(name)
363       ConfigTable.get_entry!(name)[0] = (default ? 'yes' : 'no')
364     else
365       ConfigTable.get_entry!(name)[0] = default
366     end
367   end
368
369   def remove_config(name)
370     ent = ConfigTable.get_entry(name)
371     ConfigTable.remove_entry name
372     ent
373   end
374
375 end
376
377 #
378 # File Operations
379 #
380
381 module FileOperations
382
383   def mkdir_p(dirname, prefix = nil)
384     dirname = prefix + dirname if prefix
385     $stderr.puts "mkdir -p #{dirname}" if verbose?
386     return if no_harm?
387
388     # does not check '/'... it's too abnormal case
389     dirs = dirname.split(%r<(?=/)>)
390     if /\A[a-z]:\z/i =~ dirs[0]
391       disk = dirs.shift
392       dirs[0] = disk + dirs[0]
393     end
394     dirs.each_index do |idx|
395       path = dirs[0..idx].join('')
396       Dir.mkdir path unless File.dir?(path)
397     end
398   end
399
400   def rm_f(fname)
401     $stderr.puts "rm -f #{fname}" if verbose?
402     return if no_harm?
403
404     if File.exist?(fname) or File.symlink?(fname)
405       File.chmod 0777, fname
406       File.unlink fname
407     end
408   end
409
410   def rm_rf(dn)
411     $stderr.puts "rm -rf #{dn}" if verbose?
412     return if no_harm?
413
414     Dir.chdir dn
415     Dir.foreach('.') do |fn|
416       next if fn == '.'
417       next if fn == '..'
418       if File.dir?(fn)
419         verbose_off {
420           rm_rf fn
421         }
422       else
423         verbose_off {
424           rm_f fn
425         }
426       end
427     end
428     Dir.chdir '..'
429     Dir.rmdir dn
430   end
431
432   def move_file(src, dest)
433     File.unlink dest if File.exist?(dest)
434     begin
435       File.rename src, dest
436     rescue
437       File.open(dest, 'wb') {|f| f.write File.binread(src) }
438       File.chmod File.stat(src).mode, dest
439       File.unlink src
440     end
441   end
442
443   def install(from, dest, mode, prefix = nil)
444     $stderr.puts "install #{from} #{dest}" if verbose?
445     return if no_harm?
446
447     realdest = prefix + dest if prefix
448     realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
449     str = File.binread(from)
450     if diff?(str, realdest)
451       verbose_off {
452         rm_f realdest if File.exist?(realdest)
453       }
454       File.open(realdest, 'wb') {|f|
455         f.write str
456       }
457       File.chmod mode, realdest
458
459       File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
460         if prefix
461           f.puts realdest.sub(prefix, '')
462         else
463           f.puts realdest
464         end
465       }
466     end
467   end
468
469   def diff?(new_content, path)
470     return true unless File.exist?(path)
471     new_content != File.binread(path)
472   end
473
474   def command(str)
475     $stderr.puts str if verbose?
476     system str or raise RuntimeError, "'system #{str}' failed"
477   end
478
479   def ruby(str)
480     command config('ruby-prog') + ' ' + str
481   end
482   
483   def make(task = '')
484     command config('make-prog') + ' ' + task
485   end
486
487   def extdir?(dir)
488     File.exist?(dir + '/MANIFEST')
489   end
490
491   def all_files_in(dirname)
492     Dir.open(dirname) {|d|
493       return d.select {|ent| File.file?("#{dirname}/#{ent}") }
494     }
495   end
496
497   REJECT_DIRS = %w(
498     CVS SCCS RCS CVS.adm
499   )
500
501   def all_dirs_in(dirname)
502     Dir.open(dirname) {|d|
503       return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS
504     }
505   end
506
507 end
508
509 #
510 # Main Installer
511 #
512
513 class InstallError < StandardError; end
514
515
516 module HookUtils
517
518   def run_hook(name)
519     try_run_hook "#{curr_srcdir()}/#{name}" or
520     try_run_hook "#{curr_srcdir()}/#{name}.rb"
521   end
522
523   def try_run_hook(fname)
524     return false unless File.file?(fname)
525     begin
526       instance_eval File.read(fname), fname, 1
527     rescue
528       raise InstallError, "hook #{fname} failed:\n" + $!.message
529     end
530     true
531   end
532
533 end
534
535
536 module HookScriptAPI
537
538   def get_config(key)
539     @config[key]
540   end
541
542   alias config get_config
543
544   def set_config(key, val)
545     @config[key] = val
546   end
547
548   #
549   # srcdir/objdir (works only in the package directory)
550   #
551
552   #abstract srcdir_root
553   #abstract objdir_root
554   #abstract relpath
555
556   def curr_srcdir
557     "#{srcdir_root()}/#{relpath()}"
558   end
559
560   def curr_objdir
561     "#{objdir_root()}/#{relpath()}"
562   end
563
564   def srcfile(path)
565     "#{curr_srcdir()}/#{path}"
566   end
567
568   def srcexist?(path)
569     File.exist?(srcfile(path))
570   end
571
572   def srcdirectory?(path)
573     File.dir?(srcfile(path))
574   end
575   
576   def srcfile?(path)
577     File.file? srcfile(path)
578   end
579
580   def srcentries(path = '.')
581     Dir.open("#{curr_srcdir()}/#{path}") {|d|
582       return d.to_a - %w(. ..)
583     }
584   end
585
586   def srcfiles(path = '.')
587     srcentries(path).select {|fname|
588       File.file?(File.join(curr_srcdir(), path, fname))
589     }
590   end
591
592   def srcdirectories(path = '.')
593     srcentries(path).select {|fname|
594       File.dir?(File.join(curr_srcdir(), path, fname))
595     }
596   end
597
598 end
599
600
601 class ToplevelInstaller
602
603   Version   = '3.2.4'
604   Copyright = 'Copyright (c) 2000-2004 Minero Aoki'
605
606   TASKS = [
607     [ 'config',   'saves your configurations' ],
608     [ 'show',     'shows current configuration' ],
609     [ 'setup',    'compiles ruby extentions and others' ],
610     [ 'install',  'installs files' ],
611     [ 'clean',    "does `make clean' for each extention" ],
612     [ 'distclean',"does `make distclean' for each extention" ]
613   ]
614
615   def ToplevelInstaller.invoke
616     instance().invoke
617   end
618
619   @singleton = nil
620
621   def ToplevelInstaller.instance
622     @singleton ||= new(File.dirname($0))
623     @singleton
624   end
625
626   include MetaConfigAPI
627
628   def initialize(ardir_root)
629     @config = nil
630     @options = { 'verbose' => true }
631     @ardir = File.expand_path(ardir_root)
632   end
633
634   def inspect
635     "#<#{self.class} #{__id__()}>"
636   end
637
638   def invoke
639     run_metaconfigs
640     task = parsearg_global()
641     @config = load_config(task)
642     __send__ "parsearg_#{task}"
643     init_installers
644     __send__ "exec_#{task}"
645   end
646
647   def run_metaconfigs
648     eval_file_ifexist "#{@ardir}/metaconfig"
649   end
650
651   def load_config(task)
652     case task
653     when 'config'
654       ConfigTable.new
655     when 'clean', 'distclean'
656       if File.exist?('config.save')
657       then ConfigTable.load
658       else ConfigTable.new
659       end
660     else
661       ConfigTable.load
662     end
663   end
664
665   def init_installers
666     @installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))
667   end
668
669   #
670   # Hook Script API bases
671   #
672
673   def srcdir_root
674     @ardir
675   end
676
677   def objdir_root
678     '.'
679   end
680
681   def relpath
682     '.'
683   end
684
685   #
686   # Option Parsing
687   #
688
689   def parsearg_global
690     valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/
691
692     while arg = ARGV.shift
693       case arg
694       when /\A\w+\z/
695         raise InstallError, "invalid task: #{arg}" unless valid_task =~ arg
696         return arg
697
698       when '-q', '--quiet'
699         @options['verbose'] = false
700
701       when       '--verbose'
702         @options['verbose'] = true
703
704       when '-h', '--help'
705         print_usage $stdout
706         exit 0
707
708       when '-v', '--version'
709         puts "#{File.basename($0)} version #{Version}"
710         exit 0
711       
712       when '--copyright'
713         puts Copyright
714         exit 0
715
716       else
717         raise InstallError, "unknown global option '#{arg}'"
718       end
719     end
720
721     raise InstallError, <<EOS
722 No task or global option given.
723 Typical installation procedure is:
724     $ ruby #{File.basename($0)} config
725     $ ruby #{File.basename($0)} setup
726     # ruby #{File.basename($0)} install  (may require root privilege)
727 EOS
728   end
729
730
731   def parsearg_no_options
732     raise InstallError, "#{task}:  unknown options: #{ARGV.join ' '}"\
733         unless ARGV.empty?
734   end
735
736   alias parsearg_show       parsearg_no_options
737   alias parsearg_setup      parsearg_no_options
738   alias parsearg_clean      parsearg_no_options
739   alias parsearg_distclean  parsearg_no_options
740
741   def parsearg_config
742     re = /\A--(#{ConfigTable.keys.join '|'})(?:=(.*))?\z/
743     @options['config-opt'] = []
744
745     while i = ARGV.shift
746       if /\A--?\z/ =~ i
747         @options['config-opt'] = ARGV.dup
748         break
749       end
750       m = re.match(i) or raise InstallError, "config: unknown option #{i}"
751       name, value = m.to_a[1,2]
752       if value
753         if ConfigTable.bool_config?(name)
754           raise InstallError, "config: --#{name} allows only yes/no for argument"\
755               unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ value
756           value = (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no'
757         end
758       else
759         raise InstallError, "config: --#{name} requires argument"\
760             unless ConfigTable.bool_config?(name)
761         value = 'yes'
762       end
763       @config[name] = value
764     end
765   end
766
767   def parsearg_install
768     @options['no-harm'] = false
769     @options['install-prefix'] = ''
770     while a = ARGV.shift
771       case a
772       when /\A--no-harm\z/
773         @options['no-harm'] = true
774       when /\A--prefix=(.*)\z/
775         path = $1
776         path = File.expand_path(path) unless path[0,1] == '/'
777         @options['install-prefix'] = path
778       else
779         raise InstallError, "install: unknown option #{a}"
780       end
781     end
782   end
783
784   def print_usage(out)
785     out.puts 'Typical Installation Procedure:'
786     out.puts "  $ ruby #{File.basename $0} config"
787     out.puts "  $ ruby #{File.basename $0} setup"
788     out.puts "  # ruby #{File.basename $0} install (may require root privilege)"
789     out.puts
790     out.puts 'Detailed Usage:'
791     out.puts "  ruby #{File.basename $0} <global option>"
792     out.puts "  ruby #{File.basename $0} [<global options>] <task> [<task options>]"
793
794     fmt = "  %-20s %s\n"
795     out.puts
796     out.puts 'Global options:'
797     out.printf fmt, '-q,--quiet',   'suppress message outputs'
798     out.printf fmt, '   --verbose', 'output messages verbosely'
799     out.printf fmt, '-h,--help',    'print this message'
800     out.printf fmt, '-v,--version', 'print version and quit'
801     out.printf fmt, '   --copyright',  'print copyright and quit'
802
803     out.puts
804     out.puts 'Tasks:'
805     TASKS.each do |name, desc|
806       out.printf "  %-10s  %s\n", name, desc
807     end
808
809     out.puts
810     out.puts 'Options for config:'
811     ConfigTable.each_definition do |name, (default, arg, desc, default2)|
812       out.printf "  %-20s %s [%s]\n",
813                  '--'+ name + (ConfigTable.bool_config?(name) ? '' : '='+arg),
814                  desc,
815                  default2 || default
816     end
817     out.printf "  %-20s %s [%s]\n",
818         '--rbconfig=path', 'your rbconfig.rb to load', "running ruby's"
819
820     out.puts
821     out.puts 'Options for install:'
822     out.printf "  %-20s %s [%s]\n",
823         '--no-harm', 'only display what to do if given', 'off'
824     out.printf "  %-20s %s [%s]\n",
825         '--prefix',  'install path prefix', '$prefix'
826
827     out.puts
828   end
829
830   #
831   # Task Handlers
832   #
833
834   def exec_config
835     @installer.exec_config
836     @config.save   # must be final
837   end
838
839   def exec_setup
840     @installer.exec_setup
841   end
842
843   def exec_install
844     @installer.exec_install
845   end
846
847   def exec_show
848     ConfigTable.each_name do |k|
849       v = @config.get_raw(k)
850       if not v or v.empty?
851         v = '(not specified)'
852       end
853       printf "%-10s %s\n", k, v
854     end
855   end
856
857   def exec_clean
858     @installer.exec_clean
859   end
860
861   def exec_distclean
862     @installer.exec_distclean
863   end
864
865 end
866
867
868 class ToplevelInstallerMulti < ToplevelInstaller
869
870   include HookUtils
871   include HookScriptAPI
872   include FileOperations
873
874   def initialize(ardir)
875     super
876     @packages = all_dirs_in("#{@ardir}/packages")
877     raise 'no package exists' if @packages.empty?
878   end
879
880   def run_metaconfigs
881     eval_file_ifexist "#{@ardir}/metaconfig"
882     @packages.each do |name|
883       eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig"
884     end
885   end
886
887   def init_installers
888     @installers = {}
889     @packages.each do |pack|
890       @installers[pack] = Installer.new(@config, @options,
891                                        "#{@ardir}/packages/#{pack}",
892                                        "packages/#{pack}")
893     end
894
895     with    = extract_selection(config('with'))
896     without = extract_selection(config('without'))
897     @selected = @installers.keys.select {|name|
898                   (with.empty? or with.include?(name)) \
899                       and not without.include?(name)
900                 }
901   end
902
903   def extract_selection(list)
904     a = list.split(/,/)
905     a.each do |name|
906       raise InstallError, "no such package: #{name}" \
907               unless @installers.key?(name)
908     end
909     a
910   end
911
912   def print_usage(f)
913     super
914     f.puts 'Inluded packages:'
915     f.puts '  ' + @packages.sort.join(' ')
916     f.puts
917   end
918
919   #
920   # multi-package metaconfig API
921   #
922
923   attr_reader :packages
924
925   def declare_packages(list)
926     raise 'package list is empty' if list.empty?
927     list.each do |name|
928       raise "directory packages/#{name} does not exist"\
929               unless File.dir?("#{@ardir}/packages/#{name}")
930     end
931     @packages = list
932   end
933
934   #
935   # Task Handlers
936   #
937
938   def exec_config
939     run_hook 'pre-config'
940     each_selected_installers {|inst| inst.exec_config }
941     run_hook 'post-config'
942     @config.save   # must be final
943   end
944
945   def exec_setup
946     run_hook 'pre-setup'
947     each_selected_installers {|inst| inst.exec_setup }
948     run_hook 'post-setup'
949   end
950
951   def exec_install
952     run_hook 'pre-install'
953     each_selected_installers {|inst| inst.exec_install }
954     run_hook 'post-install'
955   end
956
957   def exec_clean
958     rm_f 'config.save'
959     run_hook 'pre-clean'
960     each_selected_installers {|inst| inst.exec_clean }
961     run_hook 'post-clean'
962   end
963
964   def exec_distclean
965     rm_f 'config.save'
966     run_hook 'pre-distclean'
967     each_selected_installers {|inst| inst.exec_distclean }
968     run_hook 'post-distclean'
969   end
970
971   #
972   # lib
973   #
974
975   def each_selected_installers
976     Dir.mkdir 'packages' unless File.dir?('packages')
977     @selected.each do |pack|
978       $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose']
979       Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
980       Dir.chdir "packages/#{pack}"
981       yield @installers[pack]
982       Dir.chdir '../..'
983     end
984   end
985
986   def verbose?
987     @options['verbose']
988   end
989
990   def no_harm?
991     @options['no-harm']
992   end
993
994 end
995
996
997 class Installer
998
999   FILETYPES = %w( bin lib ext data )
1000
1001   include HookScriptAPI
1002   include HookUtils
1003   include FileOperations
1004
1005   def initialize(config, opt, srcroot, objroot)
1006     @config = config
1007     @options = opt
1008     @srcdir = File.expand_path(srcroot)
1009     @objdir = File.expand_path(objroot)
1010     @currdir = '.'
1011   end
1012
1013   def inspect
1014     "#<#{self.class} #{File.basename(@srcdir)}>"
1015   end
1016
1017   #
1018   # Hook Script API bases
1019   #
1020
1021   def srcdir_root
1022     @srcdir
1023   end
1024
1025   def objdir_root
1026     @objdir
1027   end
1028
1029   def relpath
1030     @currdir
1031   end
1032
1033   #
1034   # configs/options
1035   #
1036
1037   def no_harm?
1038     @options['no-harm']
1039   end
1040
1041   def verbose?
1042     @options['verbose']
1043   end
1044
1045   def verbose_off
1046     begin
1047       save, @options['verbose'] = @options['verbose'], false
1048       yield
1049     ensure
1050       @options['verbose'] = save
1051     end
1052   end
1053
1054   #
1055   # TASK config
1056   #
1057
1058   def exec_config
1059     exec_task_traverse 'config'
1060   end
1061
1062   def config_dir_bin(rel)
1063   end
1064
1065   def config_dir_lib(rel)
1066   end
1067
1068   def config_dir_ext(rel)
1069     extconf if extdir?(curr_srcdir())
1070   end
1071
1072   def extconf
1073     opt = @options['config-opt'].join(' ')
1074     command "#{config('ruby-prog')} #{curr_srcdir()}/extconf.rb #{opt}"
1075   end
1076
1077   def config_dir_data(rel)
1078   end
1079
1080   #
1081   # TASK setup
1082   #
1083
1084   def exec_setup
1085     exec_task_traverse 'setup'
1086   end
1087
1088   def setup_dir_bin(rel)
1089     all_files_in(curr_srcdir()).each do |fname|
1090       adjust_shebang "#{curr_srcdir()}/#{fname}"
1091     end
1092   end
1093
1094   # modify: #!/usr/bin/ruby
1095   # modify: #! /usr/bin/ruby
1096   # modify: #!ruby
1097   # not modify: #!/usr/bin/env ruby
1098   SHEBANG_RE = /\A\#!\s*\S*ruby\S*/
1099
1100   def adjust_shebang(path)
1101     return if no_harm?
1102
1103     tmpfile = File.basename(path) + '.tmp'
1104     begin
1105       File.open(path, 'rb') {|r|
1106         File.open(tmpfile, 'wb') {|w|
1107           first = r.gets
1108           return unless SHEBANG_RE =~ first
1109
1110           $stderr.puts "adjusting shebang: #{File.basename path}" if verbose?
1111           w.print first.sub(SHEBANG_RE, '#!' + config('ruby-path'))
1112           w.write r.read
1113         }
1114       }
1115       move_file tmpfile, File.basename(path)
1116     ensure
1117       File.unlink tmpfile if File.exist?(tmpfile)
1118     end
1119   end
1120
1121   def setup_dir_lib(rel)
1122   end
1123
1124   def setup_dir_ext(rel)
1125     make if extdir?(curr_srcdir())
1126   end
1127
1128   def setup_dir_data(rel)
1129   end
1130
1131   #
1132   # TASK install
1133   #
1134
1135   def exec_install
1136     exec_task_traverse 'install'
1137   end
1138
1139   def install_dir_bin(rel)
1140     install_files collect_filenames_auto(), "#{config('bin-dir')}/#{rel}", 0755
1141   end
1142
1143   def install_dir_lib(rel)
1144     install_files ruby_scripts(), "#{config('rb-dir')}/#{rel}", 0644
1145   end
1146
1147   def install_dir_ext(rel)
1148     return unless extdir?(curr_srcdir())
1149     install_files ruby_extentions('.'),
1150                   "#{config('so-dir')}/#{File.dirname(rel)}",
1151                   0555
1152   end
1153
1154   def install_dir_data(rel)
1155     install_files collect_filenames_auto(), "#{config('data-dir')}/#{rel}", 0644
1156   end
1157
1158   def install_files(list, dest, mode)
1159     mkdir_p dest, @options['install-prefix']
1160     list.each do |fname|
1161       install fname, dest, mode, @options['install-prefix']
1162     end
1163   end
1164
1165   def ruby_scripts
1166     collect_filenames_auto().select {|n| /\.rb\z/ =~ n }
1167   end
1168   
1169   # picked up many entries from cvs-1.11.1/src/ignore.c
1170   reject_patterns = %w( 
1171     core RCSLOG tags TAGS .make.state
1172     .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
1173     *~ *.old *.bak *.BAK *.orig *.rej _$* *$
1174
1175     *.org *.in .*
1176   )
1177   mapping = {
1178     '.' => '\.',
1179     '$' => '\$',
1180     '#' => '\#',
1181     '*' => '.*'
1182   }
1183   REJECT_PATTERNS = Regexp.new('\A(?:' +
1184                                reject_patterns.map {|pat|
1185                                  pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] }
1186                                }.join('|') +
1187                                ')\z')
1188
1189   def collect_filenames_auto
1190     mapdir((existfiles() - hookfiles()).reject {|fname|
1191              REJECT_PATTERNS =~ fname
1192            })
1193   end
1194
1195   def existfiles
1196     all_files_in(curr_srcdir()) | all_files_in('.')
1197   end
1198
1199   def hookfiles
1200     %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
1201       %w( config setup install clean ).map {|t| sprintf(fmt, t) }
1202     }.flatten
1203   end
1204
1205   def mapdir(filelist)
1206     filelist.map {|fname|
1207       if File.exist?(fname)   # objdir
1208         fname
1209       else                    # srcdir
1210         File.join(curr_srcdir(), fname)
1211       end
1212     }
1213   end
1214
1215   def ruby_extentions(dir)
1216     _ruby_extentions(dir) or
1217         raise InstallError, "no ruby extention exists: 'ruby #{$0} setup' first"
1218   end
1219
1220   DLEXT = /\.#{ ::Config::CONFIG['DLEXT'] }\z/
1221
1222   def _ruby_extentions(dir)
1223     Dir.open(dir) {|d|
1224       return d.select {|fname| DLEXT =~ fname }
1225     }
1226   end
1227
1228   #
1229   # TASK clean
1230   #
1231
1232   def exec_clean
1233     exec_task_traverse 'clean'
1234     rm_f 'config.save'
1235     rm_f 'InstalledFiles'
1236   end
1237
1238   def clean_dir_bin(rel)
1239   end
1240
1241   def clean_dir_lib(rel)
1242   end
1243
1244   def clean_dir_ext(rel)
1245     return unless extdir?(curr_srcdir())
1246     make 'clean' if File.file?('Makefile')
1247   end
1248
1249   def clean_dir_data(rel)
1250   end
1251
1252   #
1253   # TASK distclean
1254   #
1255
1256   def exec_distclean
1257     exec_task_traverse 'distclean'
1258     rm_f 'config.save'
1259     rm_f 'InstalledFiles'
1260   end
1261
1262   def distclean_dir_bin(rel)
1263   end
1264
1265   def distclean_dir_lib(rel)
1266   end
1267
1268   def distclean_dir_ext(rel)
1269     return unless extdir?(curr_srcdir())
1270     make 'distclean' if File.file?('Makefile')
1271   end
1272
1273   #
1274   # lib
1275   #
1276
1277   def exec_task_traverse(task)
1278     run_hook "pre-#{task}"
1279     FILETYPES.each do |type|
1280       if config('without-ext') == 'yes' and type == 'ext'
1281         $stderr.puts 'skipping ext/* by user option' if verbose?
1282         next
1283       end
1284       traverse task, type, "#{task}_dir_#{type}"
1285     end
1286     run_hook "post-#{task}"
1287   end
1288
1289   def traverse(task, rel, mid)
1290     dive_into(rel) {
1291       run_hook "pre-#{task}"
1292       __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1293       all_dirs_in(curr_srcdir()).each do |d|
1294         traverse task, "#{rel}/#{d}", mid
1295       end
1296       run_hook "post-#{task}"
1297     }
1298   end
1299
1300   def dive_into(rel)
1301     return unless File.dir?("#{@srcdir}/#{rel}")
1302
1303     dir = File.basename(rel)
1304     Dir.mkdir dir unless File.dir?(dir)
1305     prevdir = Dir.pwd
1306     Dir.chdir dir
1307     $stderr.puts '---> ' + rel if verbose?
1308     @currdir = rel
1309     yield
1310     Dir.chdir prevdir
1311     $stderr.puts '<--- ' + rel if verbose?
1312     @currdir = File.dirname(rel)
1313   end
1314
1315 end
1316
1317
1318 if $0 == __FILE__
1319   begin
1320     if multipackage_install?
1321       ToplevelInstallerMulti.invoke
1322     else
1323       ToplevelInstaller.invoke
1324     end
1325   rescue
1326     raise if $DEBUG
1327     $stderr.puts $!.message
1328     $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
1329     exit 1
1330   end
1331 end
This page took 0.300145 seconds and 3 git commands to generate.