root/setup.rb

Revision baf5e97c8b6471933cf950ef6122ffe58747a556, 35.3 kB (checked in by Giuseppe Bilotta <giuseppe.bilotta@gmail.com>, 5 months ago)

setup.rb: some sanitizing

  • Property mode set to 100755
Line 
1 #!/usr/bin/env ruby
2 #
3 # setup.rb
4 #
5 # Copyright (c) 2000-2005 Minero Aoki
6 #
7 # This program is free software.
8 # You can distribute/modify this program under the terms of
9 # the GNU LGPL, Lesser General Public License version 2.1.
10 #
11
12 unless Enumerable.method_defined?(:map)   # Ruby 1.4.6
13   module Enumerable
14     alias map collect
15   end
16 end
17
18 unless File.respond_to?(:read)   # Ruby 1.6
19   def File.read(fname)
20     open(fname) {|f|
21       return f.read
22     }
23   end
24 end
25
26 unless Errno.const_defined?(:ENOTEMPTY)   # Windows?
27   module Errno
28     class ENOTEMPTY
29       # We do not raise this exception, implementation is not needed.
30     end
31   end
32 end
33
34 def File.binread(fname)
35   open(fname, 'rb') {|f|
36     return f.read
37   }
38 end
39
40 # for corrupted Windows' stat(2)
41 def File.dir?(path)
42   File.directory?((path[-1,1] == '/') ? path : path + '/')
43 end
44
45
46 class ConfigTable
47
48   include Enumerable
49
50   def initialize(rbconfig)
51     @rbconfig = rbconfig
52     @items = []
53     @table = {}
54     # options
55     @install_prefix = nil
56     @config_opt = nil
57     @verbose = true
58     @no_harm = false
59   end
60
61   attr_accessor :install_prefix
62   attr_accessor :config_opt
63
64   attr_writer :verbose
65
66   def verbose?
67     @verbose
68   end
69
70   attr_writer :no_harm
71
72   def no_harm?
73     @no_harm
74   end
75
76   def [](key)
77     lookup(key).resolve(self)
78   end
79
80   def []=(key, val)
81     lookup(key).set val
82   end
83
84   def names
85     @items.map {|i| i.name }
86   end
87
88   def each(&block)
89     @items.each(&block)
90   end
91
92   def key?(name)
93     @table.key?(name)
94   end
95
96   def lookup(name)
97     @table[name] or setup_rb_error "no such config item: #{name}"
98   end
99
100   def add(item)
101     @items.push item
102     @table[item.name] = item
103   end
104
105   def remove(name)
106     item = lookup(name)
107     @items.delete_if {|i| i.name == name }
108     @table.delete_if {|name, i| i.name == name }
109     item
110   end
111
112   def load_script(path, inst = nil)
113     if File.file?(path)
114       MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
115     end
116   end
117
118   def savefile
119     '.config'
120   end
121
122   def load_savefile
123     begin
124       File.foreach(savefile()) do |line|
125         k, v = *line.split(/=/, 2)
126         self[k] = v.strip
127       end
128     rescue Errno::ENOENT
129       setup_rb_error $!.message + "\n#{File.basename($0)} config first"
130     end
131   end
132
133   def save
134     @items.each {|i| i.value }
135     File.open(savefile(), 'w') {|f|
136       @items.each do |i|
137         f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
138       end
139     }
140   end
141
142   def load_standard_entries
143     standard_entries(@rbconfig).each do |ent|
144       add ent
145     end
146   end
147
148   def standard_entries(rbconfig)
149     c = rbconfig
150
151     rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
152
153     major = c['MAJOR'].to_i
154     minor = c['MINOR'].to_i
155     teeny = c['TEENY'].to_i
156     version = "#{major}.#{minor}"
157
158     # ruby ver. >= 1.4.4?
159     newpath_p = ((major >= 2) or
160                  ((major == 1) and
161                   ((minor >= 5) or
162                    ((minor == 4) and (teeny >= 4)))))
163
164     if c['rubylibdir']
165       # V > 1.6.3
166       libruby         = "#{c['prefix']}/lib/ruby"
167       librubyver      = c['rubylibdir']
168       librubyverarch  = c['archdir']
169       siteruby        = c['sitedir']
170       siterubyver     = c['sitelibdir']
171       siterubyverarch = c['sitearchdir']
172     elsif newpath_p
173       # 1.4.4 <= V <= 1.6.3
174       libruby         = "#{c['prefix']}/lib/ruby"
175       librubyver      = "#{c['prefix']}/lib/ruby/#{version}"
176       librubyverarch  = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
177       siteruby        = c['sitedir']
178       siterubyver     = "$siteruby/#{version}"
179       siterubyverarch = "$siterubyver/#{c['arch']}"
180     else
181       # V < 1.4.4
182       libruby         = "#{c['prefix']}/lib/ruby"
183       librubyver      = "#{c['prefix']}/lib/ruby/#{version}"
184       librubyverarch  = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
185       siteruby        = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
186       siterubyver     = siteruby
187       siterubyverarch = "$siterubyver/#{c['arch']}"
188     end
189     parameterize = lambda {|path|
190       path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
191     }
192
193     if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
194       makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
195     else
196       makeprog = 'make'
197     end
198
199     [
200       ExecItem.new('installdirs', 'std/site/home',
201                    'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
202           {|val, table|
203             case val
204             when 'std'
205               table['rbdir'] = '$librubyver'
206               table['sodir'] = '$librubyverarch'
207             when 'site'
208               table['rbdir'] = '$siterubyver'
209               table['sodir'] = '$siterubyverarch'
210             when 'home'
211               setup_rb_error '$HOME was not set' unless ENV['HOME']
212               table['prefix'] = ENV['HOME']
213               table['rbdir'] = '$libdir/ruby'
214               table['sodir'] = '$libdir/ruby'
215             end
216           },
217       PathItem.new('prefix', 'path', c['prefix'],
218                    'path prefix of target environment'),
219       PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
220                    'the directory for commands'),
221       PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
222                    'the directory for libraries'),
223       PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
224                    'the directory for shared data'),
225       PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
226                    'the directory for man pages'),
227       PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
228                    'the directory for system configuration files'),
229       PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
230                    'the directory for local state data'),
231       PathItem.new('libruby', 'path', libruby,
232                    'the directory for ruby libraries'),
233       PathItem.new('librubyver', 'path', librubyver,
234                    'the directory for standard ruby libraries'),
235       PathItem.new('librubyverarch', 'path', librubyverarch,
236                    'the directory for standard ruby extensions'),
237       PathItem.new('siteruby', 'path', siteruby,
238           'the directory for version-independent aux ruby libraries'),
239       PathItem.new('siterubyver', 'path', siterubyver,
240                    'the directory for aux ruby libraries'),
241       PathItem.new('siterubyverarch', 'path', siterubyverarch,
242                    'the directory for aux ruby binaries'),
243       PathItem.new('rbdir', 'path', '$siterubyver',
244                    'the directory for ruby scripts'),
245       PathItem.new('sodir', 'path', '$siterubyverarch',
246                    'the directory for ruby extentions'),
247       PathItem.new('rubypath', 'path', rubypath,
248                    'the path to set to #! line'),
249       ProgramItem.new('rubyprog', 'name', rubypath,
250                       'the ruby program using for installation'),
251       ProgramItem.new('makeprog', 'name', makeprog,
252                       'the make program to compile ruby extentions'),
253       SelectItem.new('shebang', 'all/ruby/never', 'never',
254                      'shebang line (#!) editing mode'),
255       BoolItem.new('without-ext', 'yes/no', 'no',
256                    'does not compile/install ruby extentions')
257     ]
258   end
259   private :standard_entries
260
261   def load_multipackage_entries
262     multipackage_entries().each do |ent|
263       add ent
264     end
265   end
266
267   def multipackage_entries
268     [
269       PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
270                                'package names that you want to install'),
271       PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
272                                'package names that you do not want to install')
273     ]
274   end
275   private :multipackage_entries
276
277   ALIASES = {
278     'std-ruby'         => 'librubyver',
279     'stdruby'          => 'librubyver',
280     'rubylibdir'       => 'librubyver',
281     'archdir'          => 'librubyverarch',
282     'site-ruby-common' => 'siteruby',     # For backward compatibility
283     'site-ruby'        => 'siterubyver',  # For backward compatibility
284     'bin-dir'          => 'bindir',
285     'bin-dir'          => 'bindir',
286     'rb-dir'           => 'rbdir',
287     'so-dir'           => 'sodir',
288     'data-dir'         => 'datadir',
289     'ruby-path'        => 'rubypath',
290     'ruby-prog'        => 'rubyprog',
291     'ruby'             => 'rubyprog',
292     'make-prog'        => 'makeprog',
293     'make'             => 'makeprog'
294   }
295
296   def fixup
297     ALIASES.each do |ali, name|
298       @table[ali] = @table[name]
299     end
300     @items.freeze
301     @table.freeze
302     @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
303   end
304
305   def parse_opt(opt)
306     m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
307     m.to_a[1,2]
308   end
309
310   def dllext
311     @rbconfig['DLEXT']
312   end
313
314   def value_config?(name)
315     lookup(name).value?
316   end
317
318   class Item
319     def initialize(name, template, default, desc)
320       @name = name.freeze
321       @template = template
322       @value = default
323       @default = default
324       @description = desc
325     end
326
327     attr_reader :name
328     attr_reader :description
329
330     attr_accessor :default
331     alias help_default default
332
333     def help_opt
334       "--#{@name}=#{@template}"
335     end
336
337     def value?
338       true
339     end
340
341     def value
342       @value
343     end
344
345     def resolve(table)
346       @value.gsub(%r<\$([^/]+)>) { table[$1] }
347     end
348
349     def set(val)
350       @value = check(val)
351     end
352
353     private
354
355     def check(val)
356       setup_rb_error "config: --#{name} requires argument" unless val
357       val
358     end
359   end
360
361   class BoolItem < Item
362     def config_type
363       'bool'
364     end
365
366     def help_opt
367       "--#{@name}"
368     end
369
370     private
371
372     def check(val)
373       return 'yes' unless val
374       case val
375       when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
376       when /\An(o)?\z/i, /\Af(alse)\z/i  then 'no'
377       else
378         setup_rb_error "config: --#{@name} accepts only yes/no for argument"
379       end
380     end
381   end
382
383   class PathItem < Item
384     def config_type
385       'path'
386     end
387
388     private
389
390     def check(path)
391       setup_rb_error "config: --#{@name} requires argument"  unless path
392       path[0,1] == '$' ? path : File.expand_path(path)
393     end
394   end
395
396   class ProgramItem < Item
397     def config_type
398       'program'
399     end
400   end
401
402   class SelectItem < Item
403     def initialize(name, selection, default, desc)
404       super
405       @ok = selection.split('/')
406     end
407
408     def config_type
409       'select'
410     end
411
412     private
413
414     def check(val)
415       unless @ok.include?(val.strip)
416         setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
417       end
418       val.strip
419     end
420   end
421
422   class ExecItem < Item
423     def initialize(name, selection, desc, &block)
424       super name, selection, nil, desc
425       @ok = selection.split('/')
426       @action = block
427     end
428
429     def config_type
430       'exec'
431     end
432
433     def value?
434       false
435     end
436
437     def resolve(table)
438       setup_rb_error "$#{name()} wrongly used as option value"
439     end
440
441     undef set
442
443     def evaluate(val, table)
444       v = val.strip.downcase
445       unless @ok.include?(v)
446         setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
447       end
448       @action.call v, table
449     end
450   end
451
452   class PackageSelectionItem < Item
453     def initialize(name, template, default, help_default, desc)
454       super name, template, default, desc
455       @help_default = help_default
456     end
457
458     attr_reader :help_default
459
460     def config_type
461       'package'
462     end
463
464     private
465
466     def check(val)
467       unless File.dir?("packages/#{val}")
468         setup_rb_error "config: no such package: #{val}"
469       end
470       val
471     end
472   end
473
474   class MetaConfigEnvironment
475     def initialize(config, installer)
476       @config = config
477       @installer = installer
478     end
479
480     def config_names
481       @config.names
482     end
483
484     def config?(name)
485       @config.key?(name)
486     end
487
488     def bool_config?(name)
489       @config.lookup(name).config_type == 'bool'
490     end
491
492     def path_config?(name)
493       @config.lookup(name).config_type == 'path'
494     end
495
496     def value_config?(name)
497       @config.lookup(name).config_type != 'exec'
498     end
499
500     def add_config(item)
501       @config.add item
502     end
503
504     def add_bool_config(name, default, desc)
505       @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
506     end
507
508     def add_path_config(name, default, desc)
509       @config.add PathItem.new(name, 'path', default, desc)
510     end
511
512     def set_config_default(name, default)
513       @config.lookup(name).default = default
514     end
515
516     def remove_config(name)
517       @config.remove(name)
518     end
519
520     # For only multipackage
521     def packages
522       raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
523       @installer.packages
524     end
525
526     # For only multipackage
527     def declare_packages(list)
528       raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
529       @installer.packages = list
530     end
531   end
532
533 end   # class ConfigTable
534
535
536 # This module requires: #verbose?, #no_harm?
537 module FileOperations
538
539   def mkdir_p(dirname, prefix = nil)
540     dirname = prefix + File.expand_path(dirname) if prefix
541     $stderr.puts "mkdir -p #{dirname}" if verbose?
542     return if no_harm?
543
544     # Does not check '/', it's too abnormal.
545     dirs = File.expand_path(dirname).split(%r<(?=/)>)
546     if /\A[a-z]:\z/i =~ dirs[0]
547       disk = dirs.shift
548       dirs[0] = disk + dirs[0]
549     end
550     dirs.each_index do |idx|
551       path = dirs[0..idx].join('')
552       Dir.mkdir path unless File.dir?(path)
553     end
554   end
555
556   def rm_f(path)
557     $stderr.puts "rm -f #{path}" if verbose?
558     return if no_harm?
559     force_remove_file path
560   end
561
562   def rm_rf(path)
563     $stderr.puts "rm -rf #{path}" if verbose?
564     return if no_harm?
565     remove_tree path
566   end
567
568   def remove_tree(path)
569     if File.symlink?(path)
570       remove_file path
571     elsif File.dir?(path)
572       remove_tree0 path
573     else
574       force_remove_file path
575     end
576   end
577
578   def remove_tree0(path)
579     Dir.foreach(path) do |ent|
580       next if ent == '.'
581       next if ent == '..'
582       entpath = "#{path}/#{ent}"
583       if File.symlink?(entpath)
584         remove_file entpath
585       elsif File.dir?(entpath)
586         remove_tree0 entpath
587       else
588         force_remove_file entpath
589       end
590     end
591     begin
592       Dir.rmdir path
593     rescue Errno::ENOTEMPTY
594       # directory may not be empty
595     end
596   end
597
598   def move_file(src, dest)
599     force_remove_file dest
600     begin
601       File.rename src, dest
602     rescue
603       File.open(dest, 'wb') {|f|
604         f.write File.binread(src)
605       }
606       File.chmod File.stat(src).mode, dest
607       File.unlink src
608     end
609   end
610
611   def force_remove_file(path)
612     begin
613       remove_file path
614     rescue
615     end
616   end
617
618   def remove_file(path)
619     File.chmod 0777, path
620     File.