Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 1589 lines (1322 sloc) 36.279 kb
b0e887d mfp Initial commit.
mfp authored
1 #
2 # setup.rb
3 #
4 # Copyright (c) 2000-2005 Minero Aoki
5 #
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.
9 #
10
11 unless Enumerable.method_defined?(:map) # Ruby 1.4.6
12 module Enumerable
13 alias map collect
14 end
15 end
16
17 unless File.respond_to?(:read) # Ruby 1.6
18 def File.read(fname)
19 open(fname) {|f|
20 return f.read
21 }
22 end
23 end
24
25 unless Errno.const_defined?(:ENOTEMPTY) # Windows?
26 module Errno
27 class ENOTEMPTY
28 # We do not raise this exception, implementation is not needed.
29 end
30 end
31 end
32
33 def File.binread(fname)
34 open(fname, 'rb') {|f|
35 return f.read
36 }
37 end
38
39 # for corrupted Windows' stat(2)
40 def File.dir?(path)
41 File.directory?((path[-1,1] == '/') ? path : path + '/')
42 end
43
44
45 class ConfigTable
46
47 include Enumerable
48
49 def initialize(rbconfig)
50 @rbconfig = rbconfig
51 @items = []
52 @table = {}
53 # options
54 @install_prefix = nil
55 @config_opt = nil
56 @verbose = true
57 @no_harm = false
58 end
59
60 attr_accessor :install_prefix
61 attr_accessor :config_opt
62
63 attr_writer :verbose
64
65 def verbose?
66 @verbose
67 end
68
69 attr_writer :no_harm
70
71 def no_harm?
72 @no_harm
73 end
74
75 def [](key)
76 lookup(key).resolve(self)
77 end
78
79 def []=(key, val)
80 lookup(key).set val
81 end
82
83 def names
84 @items.map {|i| i.name }
85 end
86
87 def each(&block)
88 @items.each(&block)
89 end
90
91 def key?(name)
92 @table.key?(name)
93 end
94
95 def lookup(name)
96 @table[name] or setup_rb_error "no such config item: #{name}"
97 end
98
99 def add(item)
100 @items.push item
101 @table[item.name] = item
102 end
103
104 def remove(name)
105 item = lookup(name)
106 @items.delete_if {|i| i.name == name }
107 @table.delete_if {|name, i| i.name == name }
108 item
109 end
110
111 def load_script(path, inst = nil)
112 if File.file?(path)
113 MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
114 end
115 end
116
117 def savefile
118 '.config'
119 end
120
121 def load_savefile
122 begin
123 File.foreach(savefile()) do |line|
124 k, v = *line.split(/=/, 2)
125 self[k] = v.strip
126 end
127 rescue Errno::ENOENT
128 setup_rb_error $!.message + "\n#{File.basename($0)} config first"
129 end
130 end
131
132 def save
133 @items.each {|i| i.value }
134 File.open(savefile(), 'w') {|f|
135 @items.each do |i|
136 f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
137 end
138 }
139 end
140
141 def load_standard_entries
142 standard_entries(@rbconfig).each do |ent|
143 add ent
144 end
145 end
146
147 def standard_entries(rbconfig)
148 c = rbconfig
149
150 rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
151
152 major = c['MAJOR'].to_i
153 minor = c['MINOR'].to_i
154 teeny = c['TEENY'].to_i
155 version = "#{major}.#{minor}"
156
157 # ruby ver. >= 1.4.4?
158 newpath_p = ((major >= 2) or
159 ((major == 1) and
160 ((minor >= 5) or
161 ((minor == 4) and (teeny >= 4)))))
162
163 if c['rubylibdir']
164 # V > 1.6.3
165 libruby = "#{c['prefix']}/lib/ruby"
166 librubyver = c['rubylibdir']
167 librubyverarch = c['archdir']
168 siteruby = c['sitedir']
169 siterubyver = c['sitelibdir']
170 siterubyverarch = c['sitearchdir']
171 elsif newpath_p
172 # 1.4.4 <= V <= 1.6.3
173 libruby = "#{c['prefix']}/lib/ruby"
174 librubyver = "#{c['prefix']}/lib/ruby/#{version}"
175 librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
176 siteruby = c['sitedir']
177 siterubyver = "$siteruby/#{version}"
178 siterubyverarch = "$siterubyver/#{c['arch']}"
179 else
180 # V < 1.4.4
181 libruby = "#{c['prefix']}/lib/ruby"
182 librubyver = "#{c['prefix']}/lib/ruby/#{version}"
183 librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
184 siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
185 siterubyver = siteruby
186 siterubyverarch = "$siterubyver/#{c['arch']}"
187 end
188 parameterize = lambda {|path|
189 path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
190 }
191
192 if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
193 makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
194 else
195 makeprog = 'make'
196 end
197
198 [
199 ExecItem.new('installdirs', 'std/site/home',
200 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
201 {|val, table|
202 case val
203 when 'std'
204 table['rbdir'] = '$librubyver'
205 table['sodir'] = '$librubyverarch'
206 when 'site'
207 table['rbdir'] = '$siterubyver'
208 table['sodir'] = '$siterubyverarch'
209 when 'home'
210 setup_rb_error '$HOME was not set' unless ENV['HOME']
211 table['prefix'] = ENV['HOME']
212 table['rbdir'] = '$libdir/ruby'
213 table['sodir'] = '$libdir/ruby'
214 end
215 },
216 PathItem.new('prefix', 'path', c['prefix'],
217 'path prefix of target environment'),
218 PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
219 'the directory for commands'),
220 PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
221 'the directory for libraries'),
222 PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
223 'the directory for shared data'),
224 PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
225 'the directory for man pages'),
226 PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
227 'the directory for system configuration files'),
228 PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
229 'the directory for local state data'),
230 PathItem.new('libruby', 'path', libruby,
231 'the directory for ruby libraries'),
232 PathItem.new('librubyver', 'path', librubyver,
233 'the directory for standard ruby libraries'),
234 PathItem.new('librubyverarch', 'path', librubyverarch,
235 'the directory for standard ruby extensions'),
236 PathItem.new('siteruby', 'path', siteruby,
237 'the directory for version-independent aux ruby libraries'),
238 PathItem.new('siterubyver', 'path', siterubyver,
239 'the directory for aux ruby libraries'),
240 PathItem.new('siterubyverarch', 'path', siterubyverarch,
241 'the directory for aux ruby binaries'),
242 PathItem.new('rbdir', 'path', '$siterubyver',
243 'the directory for ruby scripts'),
244 PathItem.new('sodir', 'path', '$siterubyverarch',
245 'the directory for ruby extentions'),
246 PathItem.new('rubypath', 'path', rubypath,
247 'the path to set to #! line'),
248 ProgramItem.new('rubyprog', 'name', rubypath,
249 'the ruby program using for installation'),
250 ProgramItem.new('makeprog', 'name', makeprog,
251 'the make program to compile ruby extentions'),
252 SelectItem.new('shebang', 'all/ruby/never', 'ruby',
253 'shebang line (#!) editing mode'),
254 BoolItem.new('without-ext', 'yes/no', 'no',
255 'does not compile/install ruby extentions')
256 ]
257 end
258 private :standard_entries
259
260 def load_multipackage_entries
261 multipackage_entries().each do |ent|
262 add ent
263 end
264 end
265
266 def multipackage_entries
267 [
268 PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
269 'package names that you want to install'),
270 PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
271 'package names that you do not want to install')
272 ]
273 end
274 private :multipackage_entries
275
276 ALIASES = {
277 'std-ruby' => 'librubyver',
278 'stdruby' => 'librubyver',
279 'rubylibdir' => 'librubyver',
280 'archdir' => 'librubyverarch',
281 'site-ruby-common' => 'siteruby', # For backward compatibility
282 'site-ruby' => 'siterubyver', # For backward compatibility
283 'bin-dir' => 'bindir',
284 'bin-dir' => 'bindir',
285 'rb-dir' => 'rbdir',
286 'so-dir' => 'sodir',
287 'data-dir' => 'datadir',
288 'ruby-path' => 'rubypath',
289 'ruby-prog' => 'rubyprog',
290 'ruby' => 'rubyprog',
291 'make-prog' => 'makeprog',
292 'make' => 'makeprog'
293 }
294
295 def fixup
296 ALIASES.each do |ali, name|
297 @table[ali] = @table[name]
298 end
299 @items.freeze
300 @table.freeze
301 @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
302 end
303
304 def parse_opt(opt)
305 m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
306 m.to_a[1,2]
307 end
308
309 def dllext
310 @rbconfig['DLEXT']
311 end
312
313 def value_config?(name)
314 lookup(name).value?
315 end
316
317 class Item
318 def initialize(name, template, default, desc)
319 @name = name.freeze
320 @template = template
321 @value = default
322 @default = default
323 @description = desc
324 end
325
326 attr_reader :name
327 attr_reader :description
328
329 attr_accessor :default
330 alias help_default default
331
332 def help_opt
333 "--#{@name}=#{@template}"
334 end
335
336 def value?
337 true
338 end
339
340 def value
341 @value
342 end
343
344 def resolve(table)
345 @value.gsub(%r<\$([^/]+)>) { table[$1] }
346 end
347
348 def set(val)
349 @value = check(val)
350 end
351
352 private
353
354 def check(val)
355 setup_rb_error "config: --#{name} requires argument" unless val
356 val
357 end
358 end
359
360 class BoolItem < Item
361 def config_type
362 'bool'
363 end
364
365 def help_opt
366 "--#{@name}"
367 end
368
369 private
370
371 def check(val)
372 return 'yes' unless val
373 case val
374 when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
375 when /\An(o)?\z/i, /\Af(alse)\z/i then 'no'
376 else
377 setup_rb_error "config: --#{@name} accepts only yes/no for argument"
378 end
379 end
380 end
381
382 class PathItem < Item
383 def config_type
384 'path'
385 end
386
387 private
388
389 def check(path)
390 setup_rb_error "config: --#{@name} requires argument" unless path
391 path[0,1] == '$' ? path : File.expand_path(path)
392 end
393 end
394
395 class ProgramItem < Item
396 def config_type
397 'program'
398 end
399 end
400
401 class SelectItem < Item
402 def initialize(name, selection, default, desc)
403 super
404 @ok = selection.split('/')
405 end
406
407 def config_type
408 'select'
409 end
410
411 private
412
413 def check(val)
414 unless @ok.include?(val.strip)
415 setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
416 end
417 val.strip
418 end
419 end
420
421 class ExecItem < Item
422 def initialize(name, selection, desc, &block)
423 super name, selection, nil, desc
424 @ok = selection.split('/')
425 @action = block
426 end
427
428 def config_type
429 'exec'
430 end
431
432 def value?
433 false
434 end
435
436 def resolve(table)
437 setup_rb_error "$#{name()} wrongly used as option value"
438 end
439
440 undef set
441
442 def evaluate(val, table)
443 v = val.strip.downcase
444 unless @ok.include?(v)
445 setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
446 end
447 @action.call v, table
448 end
449 end
450
451 class PackageSelectionItem < Item
452 def initialize(name, template, default, help_default, desc)
453 super name, template, default, desc
454 @help_default = help_default
455 end
456
457 attr_reader :help_default
458
459 def config_type
460 'package'
461 end
462
463 private
464
465 def check(val)
466 unless File.dir?("packages/#{val}")
467 setup_rb_error "config: no such package: #{val}"
468 end
469 val
470 end
471 end
472
473 class MetaConfigEnvironment
474 def initialize(config, installer)
475 @config = config
476 @installer = installer
477 end
478
479 def config_names
480 @config.names
481 end
482
483 def config?(name)
484 @config.key?(name)
485 end
486
487 def bool_config?(name)
488 @config.lookup(name).config_type == 'bool'
489 end
490
491 def path_config?(name)
492 @config.lookup(name).config_type == 'path'
493 end
494
495 def value_config?(name)
496 @config.lookup(name).config_type != 'exec'
497 end
498
499 def add_config(item)
500 @config.add item
501 end
502
503 def add_bool_config(name, default, desc)
504 @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
505 end
506
507 def add_path_config(name, default, desc)
508 @config.add PathItem.new(name, 'path', default, desc)
509 end
510
511 def set_config_default(name, default)
512 @config.lookup(name).default = default
513 end
514
515 def remove_config(name)
516 @config.remove(name)
517 end
518
519 # For only multipackage
520 def packages
521 raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
522 @installer.packages
523 end
524
525 # For only multipackage
526 def declare_packages(list)
527 raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
528 @installer.packages = list
529 end
530 end
531
532 end # class ConfigTable
533
534
535 # This module requires: #verbose?, #no_harm?
536 module FileOperations
537
538 def mkdir_p(dirname, prefix = nil)
539 dirname = prefix + File.expand_path(dirname) if prefix
540 $stderr.puts "mkdir -p #{dirname}" if verbose?
541 return if no_harm?
542
543 # Does not check '/', it's too abnormal.
544 dirs = File.expand_path(dirname).split(%r<(?=/)>)
545 if /\A[a-z]:\z/i =~ dirs[0]
546 disk = dirs.shift
547 dirs[0] = disk + dirs[0]
548 end
549 dirs.each_index do |idx|
550 path = dirs[0..idx].join('')
551 Dir.mkdir path unless File.dir?(path)
552 end
553 end
554
555 def rm_f(path)
556 $stderr.puts "rm -f #{path}" if verbose?
557 return if no_harm?
558 force_remove_file path
559 end
560
561 def rm_rf(path)
562 $stderr.puts "rm -rf #{path}" if verbose?
563 return if no_harm?
564 remove_tree path
565 end
566
567 def remove_tree(path)
568 if File.symlink?(path)
569 remove_file path
570 elsif File.dir?(path)
571 remove_tree0 path
572 else
573 force_remove_file path
574 end
575 end
576
577 def remove_tree0(path)
578 Dir.foreach(path) do |ent|
579 next if ent == '.'
580 next if ent == '..'
581 entpath = "#{path}/#{ent}"
582 if File.symlink?(entpath)
583 remove_file entpath
584 elsif File.dir?(entpath)
585 remove_tree0 entpath
586 else
587 force_remove_file entpath
588 end
589 end
590 begin
591 Dir.rmdir path
592 rescue Errno::ENOTEMPTY
593 # directory may not be empty
594 end
595 end
596
597 def move_file(src, dest)
598 force_remove_file dest
599 begin
600 File.rename src, dest
601 rescue
602 File.open(dest, 'wb') {|f|
603 f.write File.binread(src)
604 }
605 File.chmod File.stat(src).mode, dest
606 File.unlink src
607 end
608 end
609
610 def force_remove_file(path)
611 begin
612 remove_file path
613 rescue
614 end
615 end
616
617 def remove_file(path)
618 File.chmod 0777, path
619 File.unlink path
620 end
621
622 def install(from, dest, mode, prefix = nil)
623 $stderr.puts "install #{from} #{dest}" if verbose?
624 return if no_harm?
625
626 realdest = prefix ? prefix + File.expand_path(dest) : dest
627 realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
628 str = File.binread(from)
629 if diff?(str, realdest)
630 verbose_off {
631 rm_f realdest if File.exist?(realdest)
632 }
633 File.open(realdest, 'wb') {|f|
634 f.write str
635 }
636 File.chmod mode, realdest
637
638 File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
639 if prefix
640 f.puts realdest.sub(prefix, '')
641 else
642 f.puts realdest
643 end
644 }
645 end
646 end
647
648 def diff?(new_content, path)
649 return true unless File.exist?(path)
650 new_content != File.binread(path)
651 end
652
653 def command(*args)
654 $stderr.puts args.join(' ') if verbose?
655 system(*args) or raise RuntimeError,
656 "system(#{args.map{|a| a.inspect }.join(' ')}) failed"
657 end
658
659 def ruby(*args)
660 command config('rubyprog'), *args
661 end
662
663 def make(task = nil)
664 command(*[config('makeprog'), task].compact)
665 end
666
667 def extdir?(dir)
668 File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
669 end
670
671 def files_of(dir)
672 Dir.open(dir) {|d|
673 return d.select {|ent| File.file?("#{dir}/#{ent}") }
674 }
675 end
676
677 DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
678
679 def directories_of(dir)
680 Dir.open(dir) {|d|
681 return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
682 }
683 end
684
685 end
686
687
688 # This module requires: #srcdir_root, #objdir_root, #relpath
689 module HookScriptAPI
690
691 def get_config(key)
692 @config[key]
693 end
694
695 alias config get_config
696
697 # obsolete: use metaconfig to change configuration
698 def set_config(key, val)
699 @config[key] = val
700 end
701
702 #
703 # srcdir/objdir (works only in the package directory)
704 #
705
706 def curr_srcdir
707 "#{srcdir_root()}/#{relpath()}"
708 end
709
710 def curr_objdir
711 "#{objdir_root()}/#{relpath()}"
712 end
713
714 def srcfile(path)
715 "#{curr_srcdir()}/#{path}"
716 end
717
718 def srcexist?(path)
719 File.exist?(srcfile(path))
720 end
721
722 def srcdirectory?(path)
723 File.dir?(srcfile(path))
724 end
725
726 def srcfile?(path)
727 File.file?(srcfile(path))
728 end
729
730 def srcentries(path = '.')
731 Dir.open("#{curr_srcdir()}/#{path}") {|d|
732 return d.to_a - %w(. ..)
733 }
734 end
735
736 def srcfiles(path = '.')
737 srcentries(path).select {|fname|
738 File.file?(File.join(curr_srcdir(), path, fname))
739 }
740 end
741
742 def srcdirectories(path = '.')
743 srcentries(path).select {|fname|
744 File.dir?(File.join(curr_srcdir(), path, fname))
745 }
746 end
747
748 end
749
750
751 class ToplevelInstaller
752
753 Version = '3.4.1'
754 Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
755
756 TASKS = [
757 [ 'all', 'do config, setup, then install' ],
758 [ 'config', 'saves your configurations' ],
759 [ 'show', 'shows current configuration' ],
760 [ 'setup', 'compiles ruby extentions and others' ],
761 [ 'install', 'installs files' ],
762 [ 'test', 'run all tests in test/' ],
763 [ 'clean', "does `make clean' for each extention" ],
764 [ 'distclean',"does `make distclean' for each extention" ]
765 ]
766
767 def ToplevelInstaller.invoke
768 config = ConfigTable.new(load_rbconfig())
769 config.load_standard_entries
770 config.load_multipackage_entries if multipackage?
771 config.fixup
772 klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
773 klass.new(File.dirname($0), config).invoke
774 end
775
776 def ToplevelInstaller.multipackage?
777 File.dir?(File.dirname($0) + '/packages')
778 end
779
780 def ToplevelInstaller.load_rbconfig
781 if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
782 ARGV.delete(arg)
783 load File.expand_path(arg.split(/=/, 2)[1])
784 $".push 'rbconfig.rb'
785 else
786 require 'rbconfig'
787 end
788 ::Config::CONFIG
789 end
790
791 def initialize(ardir_root, config)
792 @ardir = File.expand_path(ardir_root)
793 @config = config
794 # cache
795 @valid_task_re = nil
796 end
797
798 def config(key)
799 @config[key]
800 end
801
802 def inspect
803 "#<#{self.class} #{__id__()}>"
804 end
805
806 def invoke
807 run_metaconfigs
808 case task = parsearg_global()
809 when nil, 'all'
810 parsearg_config
811 init_installers
812 exec_config
813 exec_setup
814 exec_install
815 else
816 case task
817 when 'config', 'test'
818 ;
819 when 'clean', 'distclean'
820 @config.load_savefile if File.exist?(@config.savefile)
821 else
822 @config.load_savefile
823 end
824 __send__ "parsearg_#{task}"
825 init_installers
826 __send__ "exec_#{task}"
827 end
828 end
829
830 def run_metaconfigs
831 @config.load_script "#{@ardir}/metaconfig"
832 end
833
834 def init_installers
835 @installer = Installer.new(@config, @ardir, File.expand_path('.'))
836 end
837
838 #
839 # Hook Script API bases
840 #
841
842 def srcdir_root
843 @ardir
844 end
845
846 def objdir_root
847 '.'
848 end
849
850 def relpath
851 '.'
852 end
853
854 #
855 # Option Parsing
856 #
857
858 def parsearg_global
859 while arg = ARGV.shift
860 case arg
861 when /\A\w+\z/
862 setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
863 return arg
864 when '-q', '--quiet'
865 @config.verbose = false
866 when '--verbose'
867 @config.verbose = true
868 when '--help'
869 print_usage $stdout
870 exit 0
871 when '--version'
872 puts "#{File.basename($0)} version #{Version}"
873 exit 0
874 when '--copyright'
875 puts Copyright
876 exit 0
877 else
878 setup_rb_error "unknown global option '#{arg}'"
879 end
880 end
881 nil
882 end
883
884 def valid_task?(t)
885 valid_task_re() =~ t
886 end
887
888 def valid_task_re
889 @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
890 end
891
892 def parsearg_no_options
893 unless ARGV.empty?
894 task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
895 setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
896 end
897 end
898
899 alias parsearg_show parsearg_no_options
900 alias parsearg_setup parsearg_no_options
901 alias parsearg_test parsearg_no_options
902 alias parsearg_clean parsearg_no_options
903 alias parsearg_distclean parsearg_no_options
904
905 def parsearg_config
906 evalopt = []
907 set = []
908 @config.config_opt = []
909 while i = ARGV.shift
910 if /\A--?\z/ =~ i
911 @config.config_opt = ARGV.dup
912 break
913 end
914 name, value = *@config.parse_opt(i)
915 if @config.value_config?(name)
916 @config[name] = value
917 else
918 evalopt.push [name, value]
919 end
920 set.push name
921 end
922 evalopt.each do |name, value|
923 @config.lookup(name).evaluate value, @config
924 end
925 # Check if configuration is valid
926 set.each do |n|
927 @config[n] if @config.value_config?(n)
928 end
929 end
930
931 def parsearg_install
932 @config.no_harm = false
933 @config.install_prefix = ''
934 while a = ARGV.shift
935 case a
936 when '--no-harm'
937 @config.no_harm = true
938 when /\A--prefix=/
939 path = a.split(/=/, 2)[1]
940 path = File.expand_path(path) unless path[0,1] == '/'
941 @config.install_prefix = path
942 else
943 setup_rb_error "install: unknown option #{a}"
944 end
945 end
946 end
947
948 def print_usage(out)
949 out.puts 'Typical Installation Procedure:'
950 out.puts " $ ruby #{File.basename $0} config"
951 out.puts " $ ruby #{File.basename $0} setup"
952 out.puts " # ruby #{File.basename $0} install (may require root privilege)"
953 out.puts
954 out.puts 'Detailed Usage:'
955 out.puts " ruby #{File.basename $0} <global option>"
956 out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
957
958 fmt = " %-24s %s\n"
959 out.puts
960 out.puts 'Global options:'
961 out.printf fmt, '-q,--quiet', 'suppress message outputs'
962 out.printf fmt, ' --verbose', 'output messages verbosely'
963 out.printf fmt, ' --help', 'print this message'
964 out.printf fmt, ' --version', 'print version and quit'
965 out.printf fmt, ' --copyright', 'print copyright and quit'
966 out.puts
967 out.puts 'Tasks:'
968 TASKS.each do |name, desc|
969 out.printf fmt, name, desc
970 end
971
972 fmt = " %-24s %s [%s]\n"
973 out.puts
974 out.puts 'Options for CONFIG or ALL:'
975 @config.each do |item|
976 out.printf fmt, item.help_opt, item.description, item.help_default
977 end
978 out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
979 out.puts
980 out.puts 'Options for INSTALL:'
981 out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
982 out.printf fmt, '--prefix=path', 'install path prefix', ''
983 out.puts
984 end
985
986 #
987 # Task Handlers
988 #
989
990 def exec_config
991 @installer.exec_config
992 @config.save # must be final
993 end
994
995 def exec_setup
996 @installer.exec_setup
997 end
998
999 def exec_install
1000 @installer.exec_install
1001 end
1002
1003 def exec_test
1004 @installer.exec_test
1005 end
1006
1007 def exec_show
1008 @config.each do |i|
1009 printf "%-20s %s\n", i.name, i.value if i.value?
1010 end
1011 end
1012
1013 def exec_clean
1014 @installer.exec_clean
1015 end
1016
1017 def exec_distclean
1018 @installer.exec_distclean
1019 end
1020
1021 end # class ToplevelInstaller
1022
1023
1024 class ToplevelInstallerMulti < ToplevelInstaller
1025
1026 include FileOperations
1027
1028 def initialize(ardir_root, config)
1029 super
1030 @packages = directories_of("#{@ardir}/packages")
1031 raise 'no package exists' if @packages.empty?
1032 @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
1033 end
1034
1035 def run_metaconfigs
1036 @config.load_script "#{@ardir}/metaconfig", self
1037 @packages.each do |name|
1038 @config.load_script "#{@ardir}/packages/#{name}/metaconfig"
1039 end
1040 end
1041
1042 attr_reader :packages
1043
1044 def packages=(list)
1045 raise 'package list is empty' if list.empty?
1046 list.each do |name|
1047 raise "directory packages/#{name} does not exist"\
1048 unless File.dir?("#{@ardir}/packages/#{name}")
1049 end
1050 @packages = list
1051 end
1052
1053 def init_installers
1054 @installers = {}
1055 @packages.each do |pack|
1056 @installers[pack] = Installer.new(@config,
1057 "#{@ardir}/packages/#{pack}",
1058 "packages/#{pack}")
1059 end
1060 with = extract_selection(config('with'))
1061 without = extract_selection(config('without'))
1062 @selected = @installers.keys.select {|name|
1063 (with.empty? or with.include?(name)) \
1064 and not without.include?(name)
1065 }
1066 end
1067
1068 def extract_selection(list)
1069 a = list.split(/,/)
1070 a.each do |name|
1071 setup_rb_error "no such package: #{name}" unless @installers.key?(name)
1072 end
1073 a
1074 end
1075
1076 def print_usage(f)
1077 super
1078 f.puts 'Inluded packages:'
1079 f.puts ' ' + @packages.sort.join(' ')
1080 f.puts
1081 end
1082
1083 #
1084 # Task Handlers
1085 #
1086
1087 def exec_config
1088 run_hook 'pre-config'
1089 each_selected_installers {|inst| inst.exec_config }
1090 run_hook 'post-config'
1091 @config.save # must be final
1092 end
1093
1094 def exec_setup
1095 run_hook 'pre-setup'
1096 each_selected_installers {|inst| inst.exec_setup }
1097 run_hook 'post-setup'
1098 end
1099
1100 def exec_install
1101 run_hook 'pre-install'
1102 each_selected_installers {|inst| inst.exec_install }
1103 run_hook 'post-install'
1104 end
1105
1106 def exec_test
1107 run_hook 'pre-test'
1108 each_selected_installers {|inst| inst.exec_test }
1109 run_hook 'post-test'
1110 end
1111
1112 def exec_clean
1113 rm_f @config.savefile
1114 run_hook 'pre-clean'
1115 each_selected_installers {|inst| inst.exec_clean }
1116 run_hook 'post-clean'
1117 end
1118
1119 def exec_distclean
1120 rm_f @config.savefile
1121 run_hook 'pre-distclean'
1122 each_selected_installers {|inst| inst.exec_distclean }
1123 run_hook 'post-distclean'
1124 end
1125
1126 #
1127 # lib
1128 #
1129
1130 def each_selected_installers
1131 Dir.mkdir 'packages' unless File.dir?('packages')
1132 @selected.each do |pack|
1133 $stderr.puts "Processing the package `#{pack}' ..." if verbose?
1134 Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
1135 Dir.chdir "packages/#{pack}"
1136 yield @installers[pack]
1137 Dir.chdir '../..'
1138 end
1139 end
1140
1141 def run_hook(id)
1142 @root_installer.run_hook id
1143 end
1144
1145 # module FileOperations requires this
1146 def verbose?
1147 @config.verbose?
1148 end
1149
1150 # module FileOperations requires this
1151 def no_harm?
1152 @config.no_harm?
1153 end
1154
1155 end # class ToplevelInstallerMulti
1156
1157
1158 class Installer
1159
1160 FILETYPES = %w( bin lib ext data conf man )
1161
1162 include FileOperations
1163 include HookScriptAPI
1164
1165 def initialize(config, srcroot, objroot)
1166 @config = config
1167 @srcdir = File.expand_path(srcroot)
1168 @objdir = File.expand_path(objroot)
1169 @currdir = '.'
1170 end
1171
1172 def inspect
1173 "#<#{self.class} #{File.basename(@srcdir)}>"
1174 end
1175
1176 def noop(rel)
1177 end
1178
1179 #
1180 # Hook Script API base methods
1181 #
1182
1183 def srcdir_root
1184 @srcdir
1185 end
1186
1187 def objdir_root
1188 @objdir
1189 end
1190
1191 def relpath
1192 @currdir
1193 end
1194
1195 #
1196 # Config Access
1197 #
1198
1199 # module FileOperations requires this
1200 def verbose?
1201 @config.verbose?
1202 end
1203
1204 # module FileOperations requires this
1205 def no_harm?
1206 @config.no_harm?
1207 end
1208
1209 def verbose_off
1210 begin
1211 save, @config.verbose = @config.verbose?, false
1212 yield
1213 ensure
1214 @config.verbose = save
1215 end
1216 end
1217
1218 #
1219 # TASK config
1220 #
1221
1222 def exec_config
1223 exec_task_traverse 'config'
1224 end
1225
1226 alias config_dir_bin noop
1227 alias config_dir_lib noop
1228
1229 def config_dir_ext(rel)
1230 extconf if extdir?(curr_srcdir())
1231 end
1232
1233 alias config_dir_data noop
1234 alias config_dir_conf noop
1235 alias config_dir_man noop
1236
1237 def extconf
1238 ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
1239 end
1240
1241 #
1242 # TASK setup
1243 #
1244
1245 def exec_setup
1246 exec_task_traverse 'setup'
1247 end
1248
1249 def setup_dir_bin(rel)
1250 files_of(curr_srcdir()).each do |fname|
1251 update_shebang_line "#{curr_srcdir()}/#{fname}"
1252 end
1253 end
1254
1255 alias setup_dir_lib noop
1256
1257 def setup_dir_ext(rel)
1258 make if extdir?(curr_srcdir())
1259 end
1260
1261 alias setup_dir_data noop
1262 alias setup_dir_conf noop
1263 alias setup_dir_man noop
1264
1265 def update_shebang_line(path)
1266 return if no_harm?
1267 return if config('shebang') == 'never'
1268 old = Shebang.load(path)
1269 if old
1270 $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1
1271 new = new_shebang(old)
1272 return if new.to_s == old.to_s
1273 else
1274 return unless config('shebang') == 'all'
1275 new = Shebang.new(config('rubypath'))
1276 end
1277 $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
1278 open_atomic_writer(path) {|output|
1279 File.open(path, 'rb') {|f|
1280 f.gets if old # discard
1281 output.puts new.to_s
1282 output.print f.read
1283 }
1284 }
1285 end
1286
1287 def new_shebang(old)
1288 if /\Aruby/ =~ File.basename(old.cmd)
1289 Shebang.new(config('rubypath'), old.args)
1290 elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
fe7891e updating setup so that it doesn't rename the shebang
Aaron J. Bedra authored
1291 puts "Skipping shebang rename"
1292 # We don't want this to happen anymore, it doesn't make sense
1293 # Shebang.new(config('rubypath'), old.args[1..-1])
1294 old
b0e887d mfp Initial commit.
mfp authored
1295 else
1296 return old unless config('shebang') == 'all'
1297 Shebang.new(config('rubypath'))
1298 end
1299 end
1300
1301 def open_atomic_writer(path, &block)
1302 tmpfile = File.basename(path) + '.tmp'
1303 begin
1304 File.open(tmpfile, 'wb', &block)
1305 File.rename tmpfile, File.basename(path)
1306 ensure
1307 File.unlink tmpfile if File.exist?(tmpfile)
1308 end
1309 end
1310
1311 class Shebang
1312 def Shebang.load(path)
1313 line = nil
1314 File.open(path) {|f|
1315 line = f.gets
1316 }
1317 return nil unless /\A#!/ =~ line
1318 parse(line)
1319 end
1320
1321 def Shebang.parse(line)
1322 cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
1323 new(cmd, args)
1324 end
1325
1326 def initialize(cmd, args = [])
1327 @cmd = cmd
1328 @args = args
1329 end
1330
1331 attr_reader :cmd
1332 attr_reader :args
1333
1334 def to_s
1335 "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
1336 end
1337 end
1338
1339 #
1340 # TASK install
1341 #
1342
1343 def exec_install
1344 rm_f 'InstalledFiles'
1345 exec_task_traverse 'install'
1346 end
1347
1348 def install_dir_bin(rel)
1349 install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
1350 end
1351
1352 def install_dir_lib(rel)
1353 install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
1354 end
1355
1356 def install_dir_ext(rel)
1357 return unless extdir?(curr_srcdir())
1358 install_files rubyextentions('.'),
1359 "#{config('sodir')}/#{File.dirname(rel)}",
1360 0555
1361 end
1362
1363 def install_dir_data(rel)
1364 install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
1365 end
1366
1367 def install_dir_conf(rel)
1368 # FIXME: should not remove current config files
1369 # (rename previous file to .old/.org)
1370 install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
1371 end
1372
1373 def install_dir_man(rel)
1374 install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
1375 end
1376
1377 def install_files(list, dest, mode)
1378 mkdir_p dest, @config.install_prefix
1379 list.each do |fname|
1380 install fname, dest, mode, @config.install_prefix
1381 end
1382 end
1383
1384 def libfiles
1385 glob_reject(%w(*.y *.output), targetfiles())
1386 end
1387
1388 def rubyextentions(dir)
1389 ents = glob_select("*.#{@config.dllext}", targetfiles())
1390 if ents.empty?
1391 setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
1392 end
1393 ents
1394 end
1395
1396 def targetfiles
1397 mapdir(existfiles() - hookfiles())
1398 end
1399
1400 def mapdir(ents)
1401 ents.map {|ent|
1402 if File.exist?(ent)
1403 then ent # objdir
1404 else "#{curr_srcdir()}/#{ent}" # srcdir
1405 end
1406 }
1407 end
1408
1409 # picked up many entries from cvs-1.11.1/src/ignore.c
1410 JUNK_FILES = %w(
1411 core RCSLOG tags TAGS .make.state
1412 .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
1413 *~ *.old *.bak *.BAK *.orig *.rej _$* *$
1414
1415 *.org *.in .*
1416 )
1417
1418 def existfiles
1419 glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
1420 end
1421
1422 def hookfiles
1423 %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
1424 %w( config setup install clean ).map {|t| sprintf(fmt, t) }
1425 }.flatten
1426 end
1427
1428 def glob_select(pat, ents)
1429 re = globs2re([pat])
1430 ents.select {|ent| re =~ ent }
1431 end
1432
1433 def glob_reject(pats, ents)
1434 re = globs2re(pats)
1435 ents.reject {|ent| re =~ ent }
1436 end
1437
1438 GLOB2REGEX = {
1439 '.' => '\.',
1440 '$' => '\$',
1441 '#' => '\#',
1442 '*' => '.*'
1443 }
1444
1445 def globs2re(pats)
1446 /\A(?:#{
1447 pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
1448 })\z/
1449 end
1450
1451 #
1452 # TASK test
1453 #
1454
1455 TESTDIR = 'test'
1456
1457 def exec_test
1458 unless File.directory?('test')
1459 $stderr.puts 'no test in this package' if verbose?
1460 return
1461 end
1462 $stderr.puts 'Running tests...' if verbose?
1463 begin
1464 require 'test/unit'
1465 rescue LoadError
1466 setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.'
1467 end
1468 runner = Test::Unit::AutoRunner.new(true)
1469 runner.to_run << TESTDIR
1470 runner.run
1471 end
1472
1473 #
1474 # TASK clean
1475 #
1476
1477 def exec_clean
1478 exec_task_traverse 'clean'
1479 rm_f @config.savefile
1480 rm_f 'InstalledFiles'
1481 end
1482
1483 alias clean_dir_bin noop
1484 alias clean_dir_lib noop
1485 alias clean_dir_data noop
1486 alias clean_dir_conf noop
1487 alias clean_dir_man noop
1488
1489 def clean_dir_ext(rel)
1490 return unless extdir?(curr_srcdir())
1491 make 'clean' if File.file?('Makefile')
1492 end
1493
1494 #
1495 # TASK distclean
1496 #
1497
1498 def exec_distclean
1499 exec_task_traverse 'distclean'
1500 rm_f @config.savefile
1501 rm_f 'InstalledFiles'
1502 end
1503
1504 alias distclean_dir_bin noop
1505 alias distclean_dir_lib noop
1506
1507 def distclean_dir_ext(rel)
1508 return unless extdir?(curr_srcdir())
1509 make 'distclean' if File.file?('Makefile')
1510 end
1511
1512 alias distclean_dir_data noop
1513 alias distclean_dir_conf noop
1514 alias distclean_dir_man noop
1515
1516 #
1517 # Traversing
1518 #
1519
1520 def exec_task_traverse(task)
1521 run_hook "pre-#{task}"
1522 FILETYPES.each do |type|
1523 if type == 'ext' and config('without-ext') == 'yes'
1524 $stderr.puts 'skipping ext/* by user option' if verbose?
1525 next
1526 end
1527 traverse task, type, "#{task}_dir_#{type}"
1528 end
1529 run_hook "post-#{task}"
1530 end
1531
1532 def traverse(task, rel, mid)
1533 dive_into(rel) {
1534 run_hook "pre-#{task}"
1535 __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1536 directories_of(curr_srcdir()).each do |d|
1537 traverse task, "#{rel}/#{d}", mid
1538 end
1539 run_hook "post-#{task}"
1540 }
1541 end
1542
1543 def dive_into(rel)
1544 return unless File.dir?("#{@srcdir}/#{rel}")
1545
1546 dir = File.basename(rel)
1547 Dir.mkdir dir unless File.dir?(dir)
1548 prevdir = Dir.pwd
1549 Dir.chdir dir
1550 $stderr.puts '---> ' + rel if verbose?
1551 @currdir = rel
1552 yield
1553 Dir.chdir prevdir
1554 $stderr.puts '<--- ' + rel if verbose?
1555 @currdir = File.dirname(rel)
1556 end
1557
1558 def run_hook(id)
1559 path = [ "#{curr_srcdir()}/#{id}",
1560 "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
1561 return unless path
1562 begin
1563 instance_eval File.read(path), path, 1
1564 rescue
1565 raise if $DEBUG
1566 setup_rb_error "hook #{path} failed:\n" + $!.message
1567 end
1568 end
1569
1570 end # class Installer
1571
1572
1573 class SetupError < StandardError; end
1574
1575 def setup_rb_error(msg)
1576 raise SetupError, msg
1577 end
1578
1579 if $0 == __FILE__
1580 begin
1581 ToplevelInstaller.invoke
1582 rescue SetupError
1583 raise if $DEBUG
1584 $stderr.puts $!.message
1585 $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
1586 exit 1
1587 end
1588 end
Something went wrong with that request. Please try again.