Develop and Download Open Source Software

Browse CVS Repository

Diff of /exerb/exerb/setup.rb

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph | View Patch Patch

revision 1.3 by yuya, Wed Jan 12 08:03:31 2005 UTC revision 1.4 by yuya, Wed Oct 12 02:39:14 2005 UTC
# Line 1  Line 1 
1  #  #
2  # setup.rb  # setup.rb
3  #  #
4  # Copyright (c) 2000-2004 Minero Aoki  # Copyright (c) 2000-2005 Minero Aoki
5  #  #
6  # This program is free software.  # This program is free software.
7  # You can distribute/modify this program under the terms of  # You can distribute/modify this program under the terms of
8  # the GNU Lesser General Public License version 2.1.  # the GNU LGPL, Lesser General Public License version 2.1.
9  #  #
10    
11  #  unless Enumerable.method_defined?(:map)   # Ruby 1.4.6
 # For backward compatibility  
 #  
   
 unless Enumerable.method_defined?(:map)  
12    module Enumerable    module Enumerable
13      alias map collect      alias map collect
14    end    end
15  end  end
16    
17  unless Enumerable.method_defined?(:detect)  unless File.respond_to?(:read)   # Ruby 1.6
   module Enumerable  
     alias detect find  
   end  
 end  
   
 unless Enumerable.method_defined?(:select)  
   module Enumerable  
     alias select find_all  
   end  
 end  
   
 unless Enumerable.method_defined?(:reject)  
   module Enumerable  
     def reject  
       result = []  
       each do |i|  
         result.push i unless yield(i)  
       end  
       result  
     end  
   end  
 end  
   
 unless Enumerable.method_defined?(:inject)  
   module Enumerable  
     def inject(result)  
       each do |i|  
         result = yield(result, i)  
       end  
       result  
     end  
   end  
 end  
   
 unless Enumerable.method_defined?(:any?)  
   module Enumerable  
     def any?  
       each do |i|  
         return true if yield(i)  
       end  
       false  
     end  
   end  
 end  
   
 unless File.respond_to?(:read)  
18    def File.read(fname)    def File.read(fname)
19      open(fname) {|f|      open(fname) {|f|
20        return f.read        return f.read
# Line 72  unless File.respond_to?(:read) Line 22  unless File.respond_to?(:read)
22    end    end
23  end  end
24    
25  #  unless Errno.const_defined?(:ENOTEMPTY)   # Windows?
26  # Application independent utilities    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)  def File.binread(fname)
34    open(fname, 'rb') {|f|    open(fname, 'rb') {|f|
# Line 82  def File.binread(fname) Line 36  def File.binread(fname)
36    }    }
37  end  end
38    
39  # for corrupted windows stat(2)  # for corrupted Windows' stat(2)
40  def File.dir?(path)  def File.dir?(path)
41    File.directory?((path[-1,1] == '/') ? path : path + '/')    File.directory?((path[-1,1] == '/') ? path : path + '/')
42  end  end
43    
 #  
 # Config  
 #  
44    
45  if arg = ARGV.detect{|arg| /\A--rbconfig=/ =~ arg }  class ConfigTable
   ARGV.delete(arg)  
   require arg.split(/=/, 2)[1]  
   $".push 'rbconfig.rb'  
 else  
   require 'rbconfig'  
 end  
46    
47  def multipackage_install?    include Enumerable
   FileTest.directory?(File.dirname($0) + '/packages')  
 end  
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        @libsrc_pattern = '*.rb'
59      end
60    
61  class ConfigTable    attr_accessor :install_prefix
62      attr_accessor :config_opt
63    
64    c = ::Config::CONFIG    attr_writer :verbose
65    
66    rubypath = c['bindir'] + '/' + c['ruby_install_name']    def verbose?
67        @verbose
68      end
69    
70    major = c['MAJOR'].to_i    attr_writer :no_harm
   minor = c['MINOR'].to_i  
   teeny = c['TEENY'].to_i  
   version = "#{major}.#{minor}"  
   
   # ruby ver. >= 1.4.4?  
   newpath_p = ((major >= 2) or  
                ((major == 1) and  
                 ((minor >= 5) or  
                  ((minor == 4) and (teeny >= 4)))))  
     
   subprefix = lambda {|path|  
     path.sub(/\A#{Regexp.quote(c['prefix'])}/o, '$prefix')  
   }  
71    
72    if c['rubylibdir']    def no_harm?
73      # V < 1.6.3      @no_harm
     stdruby    = subprefix.call(c['rubylibdir'])  
     siteruby   = subprefix.call(c['sitedir'])  
     versite    = subprefix.call(c['sitelibdir'])  
     sodir      = subprefix.call(c['sitearchdir'])  
   elsif newpath_p  
     # 1.4.4 <= V <= 1.6.3  
     stdruby    = "$prefix/lib/ruby/#{version}"  
     siteruby   = subprefix.call(c['sitedir'])  
     versite    = siteruby + '/' + version  
     sodir      = "$site-ruby/#{c['arch']}"  
   else  
     # V < 1.4.4  
     stdruby    = "$prefix/lib/ruby/#{version}"  
     siteruby   = "$prefix/lib/ruby/#{version}/site_ruby"  
     versite    = siteruby  
     sodir      = "$site-ruby/#{c['arch']}"  
   end  
   
   if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }  
     makeprog = arg.sub(/'/, '').split(/=/, 2)[1]  
   else  
     makeprog = 'make'  
   end  
   
   common_descripters = [  
     [ 'prefix',    [ c['prefix'],  
                      'path',  
                      'path prefix of target environment' ] ],  
     [ 'std-ruby',  [ stdruby,  
                      'path',  
                      'the directory for standard ruby libraries' ] ],  
     [ 'site-ruby-common', [ siteruby,  
                      'path',  
                      'the directory for version-independent non-standard ruby libraries' ] ],  
     [ 'site-ruby', [ versite,  
                      'path',  
                      'the directory for non-standard ruby libraries' ] ],  
     [ 'bin-dir',   [ '$prefix/bin',  
                      'path',  
                      'the directory for commands' ] ],  
     [ 'rb-dir',    [ '$site-ruby',  
                      'path',  
                      'the directory for ruby scripts' ] ],  
     [ 'so-dir',    [ sodir,  
                      'path',  
                      'the directory for ruby extentions' ] ],  
     [ 'data-dir',  [ '$prefix/share',  
                      'path',  
                      'the directory for shared data' ] ],  
     [ 'ruby-path', [ rubypath,  
                      'path',  
                      'path to set to #! line' ] ],  
     [ 'ruby-prog', [ rubypath,  
                      'name',  
                      'the ruby program using for installation' ] ],  
     [ 'make-prog', [ makeprog,  
                      'name',  
                      'the make program to compile ruby extentions' ] ],  
     [ 'without-ext', [ 'no',  
                        'yes/no',  
                        'does not compile/install ruby extentions' ] ]  
   ]  
   multipackage_descripters = [  
     [ 'with',      [ '',  
                      'name,name...',  
                      'package names that you want to install',  
                      'ALL' ] ],  
     [ 'without',   [ '',  
                      'name,name...',  
                      'package names that you do not want to install',  
                      'NONE' ] ]  
   ]  
   if multipackage_install?  
     DESCRIPTER = common_descripters + multipackage_descripters  
   else  
     DESCRIPTER = common_descripters  
74    end    end
75    
76    SAVE_FILE = 'config.save'    attr_accessor :libsrc_pattern
77    
78    def ConfigTable.each_name(&block)    def [](key)
79      keys().each(&block)      lookup(key).resolve(self)
80    end    end
81    
82    def ConfigTable.keys    def []=(key, val)
83      DESCRIPTER.map {|name, *dummy| name }      lookup(key).set val
84    end    end
85    
86    def ConfigTable.each_definition(&block)    def names
87      DESCRIPTER.each(&block)      @items.map {|i| i.name }
88    end    end
89    
90    def ConfigTable.get_entry(name)    def each(&block)
91      name, ent = DESCRIPTER.assoc(name)      @items.each(&block)
     ent  
92    end    end
93    
94    def ConfigTable.get_entry!(name)    def key?(name)
95      get_entry(name) or raise ArgumentError, "no such config: #{name}"      @table.key?(name)
96    end    end
97    
98    def ConfigTable.add_entry(name, vals)    def lookup(name)
99      ConfigTable::DESCRIPTER.push [name,vals]      @table[name] or setup_rb_error "no such config item: #{name}"
100    end    end
101    
102    def ConfigTable.remove_entry(name)    def add(item)
103      get_entry(name) or raise ArgumentError, "no such config: #{name}"      @items.push item
104      DESCRIPTER.delete_if {|n, arr| n == name }      @table[item.name] = item
105    end    end
106    
107    def ConfigTable.config_key?(name)    def remove(name)
108      get_entry(name) ? true : false      item = lookup(name)
109        @items.delete_if {|i| i.name == name }
110        @table.delete_if {|name, i| i.name == name }
111        item
112    end    end
113    
114    def ConfigTable.bool_config?(name)    def load_script(path, inst = nil)
115      ent = get_entry(name) or return false      if File.file?(path)
116      ent[1] == 'yes/no'        MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
117        end
118    end    end
119    
120    def ConfigTable.value_config?(name)    def savefile
121      ent = get_entry(name) or return false      '.config'
     ent[1] != 'yes/no'  
122    end    end
123    
124    def ConfigTable.path_config?(name)    def load_savefile
125      ent = get_entry(name) or return false      begin
126      ent[1] == 'path'        File.foreach(savefile()) do |line|
127            k, v = *line.split(/=/, 2)
128            self[k] = v.strip
129          end
130        rescue Errno::ENOENT
131          setup_rb_error $!.message + "\n#{File.basename($0)} config first"
132        end
133    end    end
134    
135      def save
136    class << self      @items.each {|i| i.value }
137      alias newobj new      File.open(savefile(), 'w') {|f|
138          @items.each do |i|
139            f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
140          end
141        }
142    end    end
143    
144    def ConfigTable.new    def load_standard_entries
145      c = newobj()      standard_entries(@rbconfig).each do |ent|
146      c.initialize_from_table        add ent
147      c      end
148    end    end
149    
150    def ConfigTable.load    def standard_entries(rbconfig)
151      c = newobj()      c = rbconfig
152      c.initialize_from_file  
153      c      rubypath = c['bindir'] + '/' + c['ruby_install_name']
154    end  
155        major = c['MAJOR'].to_i
156        minor = c['MINOR'].to_i
157        teeny = c['TEENY'].to_i
158        version = "#{major}.#{minor}"
159    
160        # ruby ver. >= 1.4.4?
161        newpath_p = ((major >= 2) or
162                     ((major == 1) and
163                      ((minor >= 5) or
164                       ((minor == 4) and (teeny >= 4)))))
165    
166        if c['rubylibdir']
167          # V > 1.6.3
168          libruby         = "#{c['prefix']}/lib/ruby"
169          librubyver      = c['rubylibdir']
170          librubyverarch  = c['archdir']
171          siteruby        = c['sitedir']
172          siterubyver     = c['sitelibdir']
173          siterubyverarch = c['sitearchdir']
174        elsif newpath_p
175          # 1.4.4 <= V <= 1.6.3
176          libruby         = "#{c['prefix']}/lib/ruby"
177          librubyver      = "#{c['prefix']}/lib/ruby/#{version}"
178          librubyverarch  = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
179          siteruby        = c['sitedir']
180          siterubyver     = "$siteruby/#{version}"
181          siterubyverarch = "$siterubyver/#{c['arch']}"
182        else
183          # V < 1.4.4
184          libruby         = "#{c['prefix']}/lib/ruby"
185          librubyver      = "#{c['prefix']}/lib/ruby/#{version}"
186          librubyverarch  = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
187          siteruby        = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
188          siterubyver     = siteruby
189          siterubyverarch = "$siterubyver/#{c['arch']}"
190        end
191        parameterize = lambda {|path|
192          path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
193        }
194    
195    def initialize_from_table      if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
196      @table = {}        makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
197      DESCRIPTER.each do |k, (default, vname, desc, default2)|      else
198        @table[k] = default        makeprog = 'make'
199      end      end
   end  
200    
201    def initialize_from_file      [
202      raise InstallError, "#{File.basename $0} config first"\        ExecItem.new('installdirs', 'std/site/home',
203          unless File.file?(SAVE_FILE)                     'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
204      @table = {}            {|val, table|
205      File.foreach(SAVE_FILE) do |line|              case val
206        k, v = line.split(/=/, 2)              when 'std'
207        @table[k] = v.strip                table['rbdir'] = '$librubyver'
208                  table['sodir'] = '$librubyverarch'
209                when 'site'
210                  table['rbdir'] = '$siterubyver'
211                  table['sodir'] = '$siterubyverarch'
212                when 'home'
213                  setup_rb_error '$HOME was not set' unless ENV['HOME']
214                  table['prefix'] = ENV['HOME']
215                  table['rbdir'] = '$libdir/ruby'
216                  table['sodir'] = '$libdir/ruby'
217                end
218              },
219          PathItem.new('prefix', 'path', c['prefix'],
220                       'path prefix of target environment'),
221          PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
222                       'the directory for commands'),
223          PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
224                       'the directory for libraries'),
225          PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
226                       'the directory for shared data'),
227          PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
228                       'the directory for man pages'),
229          PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
230                       'the directory for system configuration files'),
231          PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
232                       'the directory for local state data'),
233          PathItem.new('libruby', 'path', libruby,
234                       'the directory for ruby libraries'),
235          PathItem.new('librubyver', 'path', librubyver,
236                       'the directory for standard ruby libraries'),
237          PathItem.new('librubyverarch', 'path', librubyverarch,
238                       'the directory for standard ruby extensions'),
239          PathItem.new('siteruby', 'path', siteruby,
240              'the directory for version-independent aux ruby libraries'),
241          PathItem.new('siterubyver', 'path', siterubyver,
242                       'the directory for aux ruby libraries'),
243          PathItem.new('siterubyverarch', 'path', siterubyverarch,
244                       'the directory for aux ruby binaries'),
245          PathItem.new('rbdir', 'path', '$siterubyver',
246                       'the directory for ruby scripts'),
247          PathItem.new('sodir', 'path', '$siterubyverarch',
248                       'the directory for ruby extentions'),
249          PathItem.new('rubypath', 'path', rubypath,
250                       'the path to set to #! line'),
251          ProgramItem.new('rubyprog', 'name', rubypath,
252                          'the ruby program using for installation'),
253          ProgramItem.new('makeprog', 'name', makeprog,
254                          'the make program to compile ruby extentions'),
255          SelectItem.new('shebang', 'all/ruby/never', 'ruby',
256                         'shebang line (#!) editing mode'),
257          BoolItem.new('without-ext', 'yes/no', 'no',
258                       'does not compile/install ruby extentions')
259        ]
260      end
261      private :standard_entries
262    
263      def load_multipackage_entries
264        multipackage_entries().each do |ent|
265          add ent
266        end
267      end
268    
269      def multipackage_entries
270        [
271          PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
272                                   'package names that you want to install'),
273          PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
274                                   'package names that you do not want to install')
275        ]
276      end
277      private :multipackage_entries
278    
279      ALIASES = {
280        'std-ruby'         => 'librubyver',
281        'stdruby'          => 'librubyver',
282        'rubylibdir'       => 'librubyver',
283        'archdir'          => 'librubyverarch',
284        'site-ruby-common' => 'siteruby',     # For backward compatibility
285        'site-ruby'        => 'siterubyver',  # For backward compatibility
286        'bin-dir'          => 'bindir',
287        'bin-dir'          => 'bindir',
288        'rb-dir'           => 'rbdir',
289        'so-dir'           => 'sodir',
290        'data-dir'         => 'datadir',
291        'ruby-path'        => 'rubypath',
292        'ruby-prog'        => 'rubyprog',
293        'ruby'             => 'rubyprog',
294        'make-prog'        => 'makeprog',
295        'make'             => 'makeprog'
296      }
297    
298      def fixup
299        ALIASES.each do |ali, name|
300          @table[ali] = @table[name]
301      end      end
302        @items.freeze
303        @table.freeze
304        @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
305    end    end
306    
307    def save    def parse_opt(opt)
308      File.open(SAVE_FILE, 'w') {|f|      m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
309        @table.each do |k, v|      m.to_a[1,2]
         f.printf "%s=%s\n", k, v if v  
       end  
     }  
310    end    end
311    
312    def []=(k, v)    def dllext
313      raise InstallError, "unknown config option #{k}"\      @rbconfig['DLEXT']
         unless ConfigTable.config_key?(k)  
     @table[k] = v  
   end  
       
   def [](key)  
     return nil unless @table[key]  
     @table[key].gsub(%r<\$([^/]+)>) { self[$1] }  
314    end    end
315    
316    def set_raw(key, val)    def value_config?(name)
317      @table[key] = val      lookup(name).value?
318    end    end
319    
320    def get_raw(key)    class Item
321      @table[key]      def initialize(name, template, default, desc)
322    end        @name = name.freeze
323          @template = template
324          @value = default
325          @default = default
326          @description = desc
327        end
328    
329  end      attr_reader :name
330        attr_reader :description
331    
332        attr_accessor :default
333        alias help_default default
334    
335  module MetaConfigAPI      def help_opt
336          "--#{@name}=#{@template}"
337        end
338    
339    def eval_file_ifexist(fname)      def value?
340      instance_eval File.read(fname), fname, 1 if File.file?(fname)        true
341    end      end
342    
343    def config_names      def value
344      ConfigTable.keys        @value
345    end      end
346    
347    def config?(name)      def resolve(table)
348      ConfigTable.config_key?(name)        @value.gsub(%r<\$([^/]+)>) { table[$1] }
349    end      end
350    
351        def set(val)
352          @value = check(val)
353        end
354    
355    def bool_config?(name)      private
356      ConfigTable.bool_config?(name)  
357        def check(val)
358          setup_rb_error "config: --#{name} requires argument" unless val
359          val
360        end
361    end    end
362    
363    def value_config?(name)    class BoolItem < Item
364      ConfigTable.value_config?(name)      def config_type
365          'bool'
366        end
367    
368        def help_opt
369          "--#{@name}"
370        end
371    
372        private
373    
374        def check(val)
375          return 'yes' unless val
376          unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ val
377            setup_rb_error "config: --#{@name} accepts only yes/no for argument"
378          end
379          (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no'
380        end
381    end    end
382    
383    def path_config?(name)    class PathItem < Item
384      ConfigTable.path_config?(name)      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    end
395    
396    def add_config(name, argname, default, desc)    class ProgramItem < Item
397      ConfigTable.add_entry name,[default,argname,desc]      def config_type
398          'program'
399        end
400    end    end
401    
402    def add_path_config(name, default, desc)    class SelectItem < Item
403      add_config name, 'path', default, desc      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    end
421    
422    def add_bool_config(name, default, desc)    class ExecItem < Item
423      add_config name, 'yes/no', default ? 'yes' : 'no', desc      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    end
451    
452    def set_config_default(name, default)    class PackageSelectionItem < Item
453      if bool_config?(name)      def initialize(name, template, default, help_default, desc)
454        ConfigTable.get_entry!(name)[0] = (default ? 'yes' : 'no')        super name, template, default, desc
455      else        @help_default = help_default
456        ConfigTable.get_entry!(name)[0] = default      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      end
472    end    end
473    
474    def remove_config(name)    class MetaConfigEnvironment
475      ent = ConfigTable.get_entry(name)      def intiailize(config, installer)
476      ConfigTable.remove_entry name        @config = config
477      ent        @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    end
532    
533  end  end   # class ConfigTable
534    
 #  
 # File Operations  
 #  
535    
536    # This module requires: #verbose?, #no_harm?
537  module FileOperations  module FileOperations
538    
539    def mkdir_p(dirname, prefix = nil)    def mkdir_p(dirname, prefix = nil)
540      dirname = prefix + dirname if prefix      dirname = prefix + File.expand_path(dirname) if prefix
541      $stderr.puts "mkdir -p #{dirname}" if verbose?      $stderr.puts "mkdir -p #{dirname}" if verbose?
542      return if no_harm?      return if no_harm?
543    
544      # does not check '/'... it's too abnormal case      # Does not check '/', it's too abnormal.
545      dirs = dirname.split(%r<(?=/)>)      dirs = File.expand_path(dirname).split(%r<(?=/)>)
546      if /\A[a-z]:\z/i =~ dirs[0]      if /\A[a-z]:\z/i =~ dirs[0]
547        disk = dirs.shift        disk = dirs.shift
548        dirs[0] = disk + dirs[0]        dirs[0] = disk + dirs[0]
# Line 397  module FileOperations Line 553  module FileOperations
553      end      end
554    end    end
555    
556    def rm_f(fname)    def rm_f(path)
557      $stderr.puts "rm -f #{fname}" if verbose?      $stderr.puts "rm -f #{path}" if verbose?
558      return if no_harm?      return if no_harm?
559        force_remove_file path
     if File.exist?(fname) or File.symlink?(fname)  
       File.chmod 0777, fname  
       File.unlink fname  
     end  
560    end    end
561    
562    def rm_rf(dn)    def rm_rf(path)
563      $stderr.puts "rm -rf #{dn}" if verbose?      $stderr.puts "rm -rf #{path}" if verbose?
564      return if no_harm?      return if no_harm?
565        remove_tree path
566      end
567    
568      Dir.chdir dn    def remove_tree(path)
569      Dir.foreach('.') do |fn|      if File.symlink?(path)
570        next if fn == '.'        remove_file path
571        next if fn == '..'      elsif File.dir?(path)
572        if File.dir?(fn)        remove_tree0 path
573          verbose_off {      else
574            rm_rf fn        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        else
588          verbose_off {          force_remove_file entpath
           rm_f fn  
         }  
589        end        end
590      end      end
591      Dir.chdir '..'      begin
592      Dir.rmdir dn        Dir.rmdir path
593        rescue Errno::ENOTEMPTY
594          # directory may not be empty
595        end
596    end    end
597    
598    def move_file(src, dest)    def move_file(src, dest)
599      File.unlink dest if File.exist?(dest)      force_remove_file dest
600      begin      begin
601        File.rename src, dest        File.rename src, dest
602      rescue      rescue
603        File.open(dest, 'wb') {|f| f.write File.binread(src) }        File.open(dest, 'wb') {|f|
604            f.write File.binread(src)
605          }
606        File.chmod File.stat(src).mode, dest        File.chmod File.stat(src).mode, dest
607        File.unlink src        File.unlink src
608      end      end
609    end    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.unlink path
621      end
622    
623    def install(from, dest, mode, prefix = nil)    def install(from, dest, mode, prefix = nil)
624      $stderr.puts "install #{from} #{dest}" if verbose?      $stderr.puts "install #{from} #{dest}" if verbose?
625      return if no_harm?      return if no_harm?
626    
627      realdest = prefix + dest if prefix      realdest = prefix ? prefix + File.expand_path(dest) : dest
628      realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)      realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
629      str = File.binread(from)      str = File.binread(from)
630      if diff?(str, realdest)      if diff?(str, realdest)
# Line 471  module FileOperations Line 651  module FileOperations
651      new_content != File.binread(path)      new_content != File.binread(path)
652    end    end
653    
654    def command(str)    def command(*args)
655      $stderr.puts str if verbose?      $stderr.puts args.join(' ') if verbose?
656      system str or raise RuntimeError, "'system #{str}' failed"      system(*args) or raise RuntimeError,
657            "system(#{args.map{|a| a.inspect }.join(' ')}) failed"
658    end    end
659    
660    def ruby(str)    def ruby(*args)
661      command config('ruby-prog') + ' ' + str      command config('rubyprog'), *args
662    end    end
663        
664    def make(task = '')    def make(task = nil)
665      command config('make-prog') + ' ' + task      command(*[config('makeprog'), task].compact)
666    end    end
667    
668    def extdir?(dir)    def extdir?(dir)
669      File.exist?(dir + '/MANIFEST')      File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
670    end    end
671    
672    def all_files_in(dirname)    def files_of(dir)
673      Dir.open(dirname) {|d|      Dir.open(dir) {|d|
674        return d.select {|ent| File.file?("#{dirname}/#{ent}") }        return d.select {|ent| File.file?("#{dir}/#{ent}") }
675      }      }
676    end    end
677    
678    REJECT_DIRS = %w(    DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
     CVS SCCS RCS CVS.adm  
   )  
679    
680    def all_dirs_in(dirname)    def directories_of(dir)
681      Dir.open(dirname) {|d|      Dir.open(dir) {|d|
682        return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS        return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
683      }      }
684    end    end
685    
686  end  end
687    
 #  
 # Main Installer  
 #  
   
 class InstallError < StandardError; end  
   
   
 module HookUtils  
   
   def run_hook(name)  
     try_run_hook "#{curr_srcdir()}/#{name}" or  
     try_run_hook "#{curr_srcdir()}/#{name}.rb"  
   end  
   
   def try_run_hook(fname)  
     return false unless File.file?(fname)  
     begin  
       instance_eval File.read(fname), fname, 1  
     rescue  
       raise InstallError, "hook #{fname} failed:\n" + $!.message  
     end  
     true  
   end  
   
 end  
   
688    
689    # This module requires: #srcdir_root, #objdir_root, #relpath
690  module HookScriptAPI  module HookScriptAPI
691    
692    def get_config(key)    def get_config(key)
# Line 541  module HookScriptAPI Line 695  module HookScriptAPI
695    
696    alias config get_config    alias config get_config
697    
698      # obsolete: use metaconfig to change configuration
699    def set_config(key, val)    def set_config(key, val)
700      @config[key] = val      @config[key] = val
701    end    end
# Line 549  module HookScriptAPI Line 704  module HookScriptAPI
704    # srcdir/objdir (works only in the package directory)    # srcdir/objdir (works only in the package directory)
705    #    #
706    
   #abstract srcdir_root  
   #abstract objdir_root  
   #abstract relpath  
   
707    def curr_srcdir    def curr_srcdir
708      "#{srcdir_root()}/#{relpath()}"      "#{srcdir_root()}/#{relpath()}"
709    end    end
# Line 574  module HookScriptAPI Line 725  module HookScriptAPI
725    end    end
726        
727    def srcfile?(path)    def srcfile?(path)
728      File.file? srcfile(path)      File.file?(srcfile(path))
729    end    end
730    
731    def srcentries(path = '.')    def srcentries(path = '.')
# Line 600  end Line 751  end
751    
752  class ToplevelInstaller  class ToplevelInstaller
753    
754    Version   = '3.2.4'    Version   = '3.4.0'
755    Copyright = 'Copyright (c) 2000-2004 Minero Aoki'    Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
756    
757    TASKS = [    TASKS = [
758        [ 'all',      'do config, setup, then install' ],
759      [ 'config',   'saves your configurations' ],      [ 'config',   'saves your configurations' ],
760      [ 'show',     'shows current configuration' ],      [ 'show',     'shows current configuration' ],
761      [ 'setup',    'compiles ruby extentions and others' ],      [ 'setup',    'compiles ruby extentions and others' ],
762      [ 'install',  'installs files' ],      [ 'install',  'installs files' ],
763        [ 'test',     'run all tests in test/' ],
764      [ 'clean',    "does `make clean' for each extention" ],      [ 'clean',    "does `make clean' for each extention" ],
765      [ 'distclean',"does `make distclean' for each extention" ]      [ 'distclean',"does `make distclean' for each extention" ]
766    ]    ]
767    
768    def ToplevelInstaller.invoke    def ToplevelInstaller.invoke
769      instance().invoke      config = ConfigTable.new(load_rbconfig())
770        config.load_standard_entries
771        config.load_multipackage_entries if multipackage?
772        config.fixup
773        klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
774        klass.new(File.dirname($0), config).invoke
775      end
776    
777      def ToplevelInstaller.multipackage?
778        File.dir?(File.dirname($0) + '/packages')
779      end
780    
781      def ToplevelInstaller.load_rbconfig
782        if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
783          ARGV.delete(arg)
784          load File.expand_path(arg.split(/=/, 2)[1])
785          $".push 'rbconfig.rb'
786        else
787          require 'rbconfig'
788        end
789        ::Config::CONFIG
790    end    end
791    
792    @singleton = nil    def initialize(ardir_root, config)
793        @ardir = File.expand_path(ardir_root)
794    def ToplevelInstaller.instance      @config = config
795      @singleton ||= new(File.dirname($0))      # cache
796      @singleton      @valid_task_re = nil
797    end    end
798    
799    include MetaConfigAPI    def config(key)
800        @config[key]
   def initialize(ardir_root)  
     @config = nil  
     @options = { 'verbose' => true }  
     @ardir = File.expand_path(ardir_root)  
801    end    end
802    
803    def inspect    def inspect
# Line 637  class ToplevelInstaller Line 806  class ToplevelInstaller
806    
807    def invoke    def invoke
808      run_metaconfigs      run_metaconfigs
809      task = parsearg_global()      case task = parsearg_global()
810      @config = load_config(task)      when nil, 'all'
811      __send__ "parsearg_#{task}"        parsearg_config
812      init_installers        init_installers
813      __send__ "exec_#{task}"        exec_config
814    end        exec_setup
815          exec_install
   def run_metaconfigs  
     eval_file_ifexist "#{@ardir}/metaconfig"  
   end  
   
   def load_config(task)  
     case task  
     when 'config'  
       ConfigTable.new  
     when 'clean', 'distclean'  
       if File.exist?('config.save')  
       then ConfigTable.load  
       else ConfigTable.new  
       end  
816      else      else
817        ConfigTable.load        case task
818          when 'config', 'test'
819            ;
820          when 'clean', 'distclean'
821            @config.load_savefile if File.exist?(@config.savefile)
822          else
823            @config.load_savefile
824          end
825          __send__ "parsearg_#{task}"
826          init_installers
827          __send__ "exec_#{task}"
828      end      end
829    end    end
830      
831      def run_metaconfigs
832        @config.load_script "#{@ardir}/metaconfig"
833      end
834    
835    def init_installers    def init_installers
836      @installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))      @installer = Installer.new(@config, @ardir, File.expand_path('.'))
837    end    end
838    
839    #    #
# Line 687  class ToplevelInstaller Line 857  class ToplevelInstaller
857    #    #
858    
859    def parsearg_global    def parsearg_global
     valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/  
   
860      while arg = ARGV.shift      while arg = ARGV.shift
861        case arg        case arg
862        when /\A\w+\z/        when /\A\w+\z/
863          raise InstallError, "invalid task: #{arg}" unless valid_task =~ arg          setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
864          return arg          return arg
   
865        when '-q', '--quiet'        when '-q', '--quiet'
866          @options['verbose'] = false          @config.verbose = false
867          when '--verbose'
868        when       '--verbose'          @config.verbose = true
869          @options['verbose'] = true        when '--help'
   
       when '-h', '--help'  
870          print_usage $stdout          print_usage $stdout
871          exit 0          exit 0
872          when '--version'
       when '-v', '--version'  
873          puts "#{File.basename($0)} version #{Version}"          puts "#{File.basename($0)} version #{Version}"
874          exit 0          exit 0
         
875        when '--copyright'        when '--copyright'
876          puts Copyright          puts Copyright
877          exit 0          exit 0
   
878        else        else
879          raise InstallError, "unknown global option '#{arg}'"          setup_rb_error "unknown global option '#{arg}'"
880        end        end
881      end      end
882        nil
883      end
884    
885      raise InstallError, <<EOS    def valid_task?(t)
886  No task or global option given.      valid_task_re() =~ t
 Typical installation procedure is:  
     $ ruby #{File.basename($0)} config  
     $ ruby #{File.basename($0)} setup  
     # ruby #{File.basename($0)} install  (may require root privilege)  
 EOS  
887    end    end
888    
889      def valid_task_re
890        @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
891      end
892    
893    def parsearg_no_options    def parsearg_no_options
894      raise InstallError, "#{task}:  unknown options: #{ARGV.join ' '}"\      unless ARGV.empty?
895          unless ARGV.empty?        setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
896        end
897    end    end
898    
899    alias parsearg_show       parsearg_no_options    alias parsearg_show       parsearg_no_options
900    alias parsearg_setup      parsearg_no_options    alias parsearg_setup      parsearg_no_options
901      alias parsearg_test       parsearg_no_options
902    alias parsearg_clean      parsearg_no_options    alias parsearg_clean      parsearg_no_options
903    alias parsearg_distclean  parsearg_no_options    alias parsearg_distclean  parsearg_no_options
904    
905    def parsearg_config    def parsearg_config
906      re = /\A--(#{ConfigTable.keys.join '|'})(?:=(.*))?\z/      evalopt = []
907      @options['config-opt'] = []      set = []
908        @config.config_opt = []
909      while i = ARGV.shift      while i = ARGV.shift
910        if /\A--?\z/ =~ i        if /\A--?\z/ =~ i
911          @options['config-opt'] = ARGV.dup          @config.config_opt = ARGV.dup
912          break          break
913        end        end
914        m = re.match(i) or raise InstallError, "config: unknown option #{i}"        name, value = *@config.parse_opt(i)
915        name, value = m.to_a[1,2]        if @config.value_config?(name)
916        if value          @config[name] = value
         if ConfigTable.bool_config?(name)  
           raise InstallError, "config: --#{name} allows only yes/no for argument"\  
               unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ value  
           value = (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no'  
         end  
917        else        else
918          raise InstallError, "config: --#{name} requires argument"\          evalopt.push [name, value]
             unless ConfigTable.bool_config?(name)  
         value = 'yes'  
919        end        end
920        @config[name] = value        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      end
929    end    end
930    
931    def parsearg_install    def parsearg_install
932      @options['no-harm'] = false      @config.no_harm = false
933      @options['install-prefix'] = ''      @config.install_prefix = ''
934      while a = ARGV.shift      while a = ARGV.shift
935        case a        case a
936        when /\A--no-harm\z/        when '--no-harm'
937          @options['no-harm'] = true          @config.no_harm = true
938        when /\A--prefix=(.*)\z/        when /\A--prefix=/
939          path = $1          path = a.split(/=/, 2)[1]
940          path = File.expand_path(path) unless path[0,1] == '/'          path = File.expand_path(path) unless path[0,1] == '/'
941          @options['install-prefix'] = path          @config.install_prefix = path
942        else        else
943          raise InstallError, "install: unknown option #{a}"          setup_rb_error "install: unknown option #{a}"
944        end        end
945      end      end
946    end    end
# Line 791  EOS Line 955  EOS
955      out.puts "  ruby #{File.basename $0} <global option>"      out.puts "  ruby #{File.basename $0} <global option>"
956      out.puts "  ruby #{File.basename $0} [<global options>] <task> [<task options>]"      out.puts "  ruby #{File.basename $0} [<global options>] <task> [<task options>]"
957    
958      fmt = "  %-20s %s\n"      fmt = "  %-24s %s\n"
959      out.puts      out.puts
960      out.puts 'Global options:'      out.puts 'Global options:'
961      out.printf fmt, '-q,--quiet',   'suppress message outputs'      out.printf fmt, '-q,--quiet',   'suppress message outputs'
962      out.printf fmt, '   --verbose', 'output messages verbosely'      out.printf fmt, '   --verbose', 'output messages verbosely'
963      out.printf fmt, '-h,--help',    'print this message'      out.printf fmt, '   --help',    'print this message'
964      out.printf fmt, '-v,--version', 'print version and quit'      out.printf fmt, '   --version', 'print version and quit'
965      out.printf fmt, '   --copyright',  'print copyright and quit'      out.printf fmt, '   --copyright',  'print copyright and quit'
   
966      out.puts      out.puts
967      out.puts 'Tasks:'      out.puts 'Tasks:'
968      TASKS.each do |name, desc|      TASKS.each do |name, desc|
969        out.printf "  %-10s  %s\n", name, desc        out.printf fmt, name, desc
970      end      end
971    
972        fmt = "  %-24s %s [%s]\n"
973      out.puts      out.puts
974      out.puts 'Options for config:'      out.puts 'Options for CONFIG or ALL:'
975      ConfigTable.each_definition do |name, (default, arg, desc, default2)|      @config.each do |item|
976        out.printf "  %-20s %s [%s]\n",        out.printf fmt, item.help_opt, item.description, item.help_default
                  '--'+ name + (ConfigTable.bool_config?(name) ? '' : '='+arg),  
                  desc,  
                  default2 || default  
977      end      end
978      out.printf "  %-20s %s [%s]\n",      out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
         '--rbconfig=path', 'your rbconfig.rb to load', "running ruby's"  
   
979      out.puts      out.puts
980      out.puts 'Options for install:'      out.puts 'Options for INSTALL:'
981      out.printf "  %-20s %s [%s]\n",      out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
982          '--no-harm', 'only display what to do if given', 'off'      out.printf fmt, '--prefix=path',  'install path prefix', ''
     out.printf "  %-20s %s [%s]\n",  
         '--prefix',  'install path prefix', '$prefix'  
   
983      out.puts      out.puts
984    end    end
985    
# Line 844  EOS Line 1000  EOS
1000      @installer.exec_install      @installer.exec_install
1001    end    end
1002    
1003      def exec_test
1004        @installer.exec_test
1005      end
1006    
1007    def exec_show    def exec_show
1008      ConfigTable.each_name do |k|      @config.each do |i|
1009        v = @config.get_raw(k)        printf "%-20s %s\n", i.name, i.value if i.value?
       if not v or v.empty?  
         v = '(not specified)'  
       end  
       printf "%-10s %s\n", k, v  
1010      end      end
1011    end    end
1012    
# Line 862  EOS Line 1018  EOS
1018      @installer.exec_distclean      @installer.exec_distclean
1019    end    end
1020    
1021  end  end   # class ToplevelInstaller
1022    
1023    
1024  class ToplevelInstallerMulti < ToplevelInstaller  class ToplevelInstallerMulti < ToplevelInstaller
1025    
   include HookUtils  
   include HookScriptAPI  
1026    include FileOperations    include FileOperations
1027    
1028    def initialize(ardir)    def initialize(ardir_root, config)
1029      super      super
1030      @packages = all_dirs_in("#{@ardir}/packages")      @packages = directories_of("#{@ardir}/packages")
1031      raise 'no package exists' if @packages.empty?      raise 'no package exists' if @packages.empty?
1032        @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
1033    end    end
1034    
1035    def run_metaconfigs    def run_metaconfigs
1036      eval_file_ifexist "#{@ardir}/metaconfig"      @config.load_script "#{@ardir}/metaconfig", self
1037      @packages.each do |name|      @packages.each do |name|
1038        eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig"        @config.load_script "#{@ardir}/packages/#{name}/metaconfig"
1039      end      end
1040    end    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    def init_installers
1054      @installers = {}      @installers = {}
1055      @packages.each do |pack|      @packages.each do |pack|
1056        @installers[pack] = Installer.new(@config, @options,        @installers[pack] = Installer.new(@config,
1057                                         "#{@ardir}/packages/#{pack}",                                         "#{@ardir}/packages/#{pack}",
1058                                         "packages/#{pack}")                                         "packages/#{pack}")
1059      end      end
   
1060      with    = extract_selection(config('with'))      with    = extract_selection(config('with'))
1061      without = extract_selection(config('without'))      without = extract_selection(config('without'))
1062      @selected = @installers.keys.select {|name|      @selected = @installers.keys.select {|name|
# Line 903  class ToplevelInstallerMulti < ToplevelI Line 1068  class ToplevelInstallerMulti < ToplevelI
1068    def extract_selection(list)    def extract_selection(list)
1069      a = list.split(/,/)      a = list.split(/,/)
1070      a.each do |name|      a.each do |name|
1071        raise InstallError, "no such package: #{name}" \        setup_rb_error "no such package: #{name}"  unless @installers.key?(name)
               unless @installers.key?(name)  
1072      end      end
1073      a      a
1074    end    end
# Line 917  class ToplevelInstallerMulti < ToplevelI Line 1081  class ToplevelInstallerMulti < ToplevelI
1081    end    end
1082    
1083    #    #
   # multi-package metaconfig API  
   #  
   
   attr_reader :packages  
   
   def declare_packages(list)  
     raise 'package list is empty' if list.empty?  
     list.each do |name|  
       raise "directory packages/#{name} does not exist"\  
               unless File.dir?("#{@ardir}/packages/#{name}")  
     end  
     @packages = list  
   end  
   
   #  
1084    # Task Handlers    # Task Handlers
1085    #    #
1086    
# Line 954  class ToplevelInstallerMulti < ToplevelI Line 1103  class ToplevelInstallerMulti < ToplevelI
1103      run_hook 'post-install'      run_hook 'post-install'
1104    end    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    def exec_clean
1113      rm_f 'config.save'      rm_f @config.savefile
1114      run_hook 'pre-clean'      run_hook 'pre-clean'
1115      each_selected_installers {|inst| inst.exec_clean }      each_selected_installers {|inst| inst.exec_clean }
1116      run_hook 'post-clean'      run_hook 'post-clean'
1117    end    end
1118    
1119    def exec_distclean    def exec_distclean
1120      rm_f 'config.save'      rm_f @config.savefile
1121      run_hook 'pre-distclean'      run_hook 'pre-distclean'
1122      each_selected_installers {|inst| inst.exec_distclean }      each_selected_installers {|inst| inst.exec_distclean }
1123      run_hook 'post-distclean'      run_hook 'post-distclean'
# Line 975  class ToplevelInstallerMulti < ToplevelI Line 1130  class ToplevelInstallerMulti < ToplevelI
1130    def each_selected_installers    def each_selected_installers
1131      Dir.mkdir 'packages' unless File.dir?('packages')      Dir.mkdir 'packages' unless File.dir?('packages')
1132      @selected.each do |pack|      @selected.each do |pack|
1133        $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose']        $stderr.puts "Processing the package `#{pack}' ..." if verbose?
1134        Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")        Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
1135        Dir.chdir "packages/#{pack}"        Dir.chdir "packages/#{pack}"
1136        yield @installers[pack]        yield @installers[pack]
# Line 983  class ToplevelInstallerMulti < ToplevelI Line 1138  class ToplevelInstallerMulti < ToplevelI
1138      end      end
1139    end    end
1140    
1141      def run_hook(id)
1142        @root_installer.run_hook id
1143      end
1144    
1145      # module FileOperations requires this
1146    def verbose?    def verbose?
1147      @options['verbose']      @config.verbose?
1148    end    end
1149    
1150      # module FileOperations requires this
1151    def no_harm?    def no_harm?
1152      @options['no-harm']      @config.no_harm?
1153    end    end
1154    
1155  end  end   # class ToplevelInstallerMulti
1156    
1157    
1158  class Installer  class Installer
1159    
1160    FILETYPES = %w( bin lib ext data )    FILETYPES = %w( bin lib ext data conf man )
1161    
   include HookScriptAPI  
   include HookUtils  
1162    include FileOperations    include FileOperations
1163      include HookScriptAPI
1164    
1165    def initialize(config, opt, srcroot, objroot)    def initialize(config, srcroot, objroot)
1166      @config = config      @config = config
     @options = opt  
1167      @srcdir = File.expand_path(srcroot)      @srcdir = File.expand_path(srcroot)
1168      @objdir = File.expand_path(objroot)      @objdir = File.expand_path(objroot)
1169      @currdir = '.'      @currdir = '.'
# Line 1015  class Installer Line 1174  class Installer
1174    end    end
1175    
1176    #    #
1177    # Hook Script API bases    # Hook Script API base methods
1178    #    #
1179    
1180    def srcdir_root    def srcdir_root
# Line 1031  class Installer Line 1190  class Installer
1190    end    end
1191    
1192    #    #
1193    # configs/options    # Config Access
1194    #    #
1195    
1196    def no_harm?    # module FileOperations requires this
1197      @options['no-harm']    def verbose?
1198        @config.verbose?
1199    end    end
1200    
1201    def verbose?    # module FileOperations requires this
1202      @options['verbose']    def no_harm?
1203        @config.no_harm?
1204    end    end
1205    
1206    def verbose_off    def verbose_off
1207      begin      begin
1208        save, @options['verbose'] = @options['verbose'], false        save, @config.verbose = @config.verbose?, false
1209        yield        yield
1210      ensure      ensure
1211        @options['verbose'] = save        @config.verbose = save
1212      end      end
1213    end    end
1214    
# Line 1065  class Installer Line 1226  class Installer
1226    def config_dir_lib(rel)    def config_dir_lib(rel)
1227    end    end
1228    
1229      def config_dir_man(rel)
1230      end
1231    
1232    def config_dir_ext(rel)    def config_dir_ext(rel)
1233      extconf if extdir?(curr_srcdir())      extconf if extdir?(curr_srcdir())
1234    end    end
1235    
1236    def extconf    def extconf
1237      opt = @options['config-opt'].join(' ')      ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
     command "#{config('ruby-prog')} #{curr_srcdir()}/extconf.rb #{opt}"  
1238    end    end
1239    
1240    def config_dir_data(rel)    def config_dir_data(rel)
1241    end    end
1242    
1243      def config_dir_conf(rel)
1244      end
1245    
1246    #    #
1247    # TASK setup    # TASK setup
1248    #    #
# Line 1086  class Installer Line 1252  class Installer
1252    end    end
1253    
1254    def setup_dir_bin(rel)    def setup_dir_bin(rel)
1255      all_files_in(curr_srcdir()).each do |fname|      files_of(curr_srcdir()).each do |fname|
1256        adjust_shebang "#{curr_srcdir()}/#{fname}"        adjust_shebang "#{curr_srcdir()}/#{fname}"
1257      end      end
1258    end    end
1259    
   # modify: #!/usr/bin/ruby  
   # modify: #! /usr/bin/ruby  
   # modify: #!ruby  
   # not modify: #!/usr/bin/env ruby  
   SHEBANG_RE = /\A\#!\s*\S*ruby\S*/  
   
1260    def adjust_shebang(path)    def adjust_shebang(path)
1261      return if no_harm?      return if no_harm?
   
1262      tmpfile = File.basename(path) + '.tmp'      tmpfile = File.basename(path) + '.tmp'
1263      begin      begin
1264        File.open(path, 'rb') {|r|        File.open(path, 'rb') {|r|
1265            first = r.gets
1266            return unless File.basename(first.sub(/\A\#!/, '').split[0].to_s) == 'ruby'
1267            $stderr.puts "adjusting shebang: #{File.basename(path)}" if verbose?
1268          File.open(tmpfile, 'wb') {|w|          File.open(tmpfile, 'wb') {|w|
1269            first = r.gets            w.print first.sub(/\A\#!\s*\S+/, '#! ' + config('rubypath'))
           return unless SHEBANG_RE =~ first  
   
           $stderr.puts "adjusting shebang: #{File.basename path}" if verbose?  
           w.print first.sub(SHEBANG_RE, '#!' + config('ruby-path'))  
1270            w.write r.read            w.write r.read
1271          }          }
1272        }        }
# Line 1121  class Installer Line 1279  class Installer
1279    def setup_dir_lib(rel)    def setup_dir_lib(rel)
1280    end    end
1281    
1282      def setup_dir_man(rel)
1283      end
1284    
1285    def setup_dir_ext(rel)    def setup_dir_ext(rel)
1286      make if extdir?(curr_srcdir())      make if extdir?(curr_srcdir())
1287    end    end
# Line 1128  class Installer Line 1289  class Installer
1289    def setup_dir_data(rel)    def setup_dir_data(rel)
1290    end    end
1291    
1292      def setup_dir_conf(rel)
1293      end
1294    
1295    #    #
1296    # TASK install    # TASK install
1297    #    #
1298    
1299    def exec_install    def exec_install
1300        rm_f 'InstalledFiles'
1301      exec_task_traverse 'install'      exec_task_traverse 'install'
1302    end    end
1303    
1304    def install_dir_bin(rel)    def install_dir_bin(rel)
1305      install_files collect_filenames_auto(), "#{config('bin-dir')}/#{rel}", 0755      install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
1306    end    end
1307    
1308    def install_dir_lib(rel)    def install_dir_lib(rel)
1309      install_files ruby_scripts(), "#{config('rb-dir')}/#{rel}", 0644      install_files rubyscripts(), "#{config('rbdir')}/#{rel}", 0644
1310    end    end
1311    
1312    def install_dir_ext(rel)    def install_dir_ext(rel)
1313      return unless extdir?(curr_srcdir())      return unless extdir?(curr_srcdir())
1314      install_files ruby_extentions('.'),      install_files rubyextentions('.'),
1315                    "#{config('so-dir')}/#{File.dirname(rel)}",                    "#{config('sodir')}/#{File.dirname(rel)}",
1316                    0555                    0555
1317    end    end
1318    
1319    def install_dir_data(rel)    def install_dir_data(rel)
1320      install_files collect_filenames_auto(), "#{config('data-dir')}/#{rel}", 0644      install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
1321      end
1322    
1323      def install_dir_conf(rel)
1324        # FIXME: should not remove current config files
1325        # (rename previous file to .old/.org)
1326        install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
1327      end
1328    
1329      def install_dir_man(rel)
1330        install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
1331    end    end
1332    
1333    def install_files(list, dest, mode)    def install_files(list, dest, mode)
1334      mkdir_p dest, @options['install-prefix']      mkdir_p dest, @config.install_prefix
1335      list.each do |fname|      list.each do |fname|
1336        install fname, dest, mode, @options['install-prefix']        install fname, dest, mode, @config.install_prefix
1337      end      end
1338    end    end
1339    
1340    def ruby_scripts    def rubyscripts
1341      collect_filenames_auto().select {|n| /\.rb\z/ =~ n }      glob_select(@config.libsrc_pattern, targetfiles())
1342    end    end
1343      
1344      def rubyextentions(dir)
1345        ents = glob_select("*.#{@config.dllext}", targetfiles())
1346        if ents.empty?
1347          setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
1348        end
1349        ents
1350      end
1351    
1352      def targetfiles
1353        mapdir(existfiles() - hookfiles())
1354      end
1355    
1356      def mapdir(ents)
1357        ents.map {|ent|
1358          if File.exist?(ent)
1359          then ent                         # objdir
1360          else "#{curr_srcdir()}/#{ent}"   # srcdir
1361          end
1362        }
1363      end
1364    
1365    # picked up many entries from cvs-1.11.1/src/ignore.c    # picked up many entries from cvs-1.11.1/src/ignore.c
1366    reject_patterns = %w(    JUNK_FILES = %w(
1367      core RCSLOG tags TAGS .make.state      core RCSLOG tags TAGS .make.state
1368      .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb      .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
1369      *~ *.old *.bak *.BAK *.orig *.rej _$* *$      *~ *.old *.bak *.BAK *.orig *.rej _$* *$
1370    
1371      *.org *.in .*      *.org *.in .*
1372    )    )
   mapping = {  
     '.' => '\.',  
     '$' => '\$',  
     '#' => '\#',  
     '*' => '.*'  
   }  
   REJECT_PATTERNS = Regexp.new('\A(?:' +  
                                reject_patterns.map {|pat|  
                                  pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] }  
                                }.join('|') +  
                                ')\z')  
   
   def collect_filenames_auto  
     mapdir((existfiles() - hookfiles()).reject {|fname|  
              REJECT_PATTERNS =~ fname  
            })  
   end  
1373    
1374    def existfiles    def existfiles
1375      all_files_in(curr_srcdir()) | all_files_in('.')      glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
1376    end    end
1377    
1378    def hookfiles    def hookfiles
# Line 1202  class Installer Line 1381  class Installer
1381      }.flatten      }.flatten
1382    end    end
1383    
1384    def mapdir(filelist)    def glob_select(pat, ents)
1385      filelist.map {|fname|      re = globs2re([pat])
1386        if File.exist?(fname)   # objdir      ents.select {|ent| re =~ ent }
         fname  
       else                    # srcdir  
         File.join(curr_srcdir(), fname)  
       end  
     }  
1387    end    end
1388    
1389    def ruby_extentions(dir)    def glob_reject(pats, ents)
1390      _ruby_extentions(dir) or      re = globs2re(pats)
1391          raise InstallError, "no ruby extention exists: 'ruby #{$0} setup' first"      ents.reject {|ent| re =~ ent }
1392    end    end
1393    
1394    DLEXT = /\.#{ ::Config::CONFIG['DLEXT'] }\z/    GLOB2REGEX = {
1395        '.' => '\.',
1396        '$' => '\$',
1397        '#' => '\#',
1398        '*' => '.*'
1399      }
1400    
1401    def _ruby_extentions(dir)    def globs2re(pats)
1402      Dir.open(dir) {|d|      /\A(?:#{
1403        return d.select {|fname| DLEXT =~ fname }        pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
1404      }      })\z/
1405      end
1406    
1407      #
1408      # TASK test
1409      #
1410    
1411      TESTDIR = 'test'
1412    
1413      def exec_test
1414        unless File.directory?('test')
1415          $stderr.puts 'no test in this package' if verbose?
1416          return
1417        end
1418        $stderr.puts 'Running tests...' if verbose?
1419        require 'test/unit'
1420        runner = Test::Unit::AutoRunner.new(true)
1421        runner.to_run << TESTDIR
1422        runner.run
1423    end    end
1424    
1425    #    #
# Line 1231  class Installer Line 1428  class Installer
1428    
1429    def exec_clean    def exec_clean
1430      exec_task_traverse 'clean'      exec_task_traverse 'clean'
1431      rm_f 'config.save'      rm_f @config.savefile
1432      rm_f 'InstalledFiles'      rm_f 'InstalledFiles'
1433    end    end
1434    
# Line 1249  class Installer Line 1446  class Installer
1446    def clean_dir_data(rel)    def clean_dir_data(rel)
1447    end    end
1448    
1449      def clean_dir_conf(rel)
1450      end
1451    
1452    #    #
1453    # TASK distclean    # TASK distclean
1454    #    #
1455    
1456    def exec_distclean    def exec_distclean
1457      exec_task_traverse 'distclean'      exec_task_traverse 'distclean'
1458      rm_f 'config.save'      rm_f @config.savefile
1459      rm_f 'InstalledFiles'      rm_f 'InstalledFiles'
1460    end    end
1461    
# Line 1270  class Installer Line 1470  class Installer
1470      make 'distclean' if File.file?('Makefile')      make 'distclean' if File.file?('Makefile')
1471    end    end
1472    
1473      def distclean_dir_data(rel)
1474      end
1475    
1476      def distclean_dir_conf(rel)
1477      end
1478    
1479    #    #
1480    # lib    # lib
1481    #    #
# Line 1290  class Installer Line 1496  class Installer
1496      dive_into(rel) {      dive_into(rel) {
1497        run_hook "pre-#{task}"        run_hook "pre-#{task}"
1498        __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')        __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1499        all_dirs_in(curr_srcdir()).each do |d|        directories_of(curr_srcdir()).each do |d|
1500          traverse task, "#{rel}/#{d}", mid          traverse task, "#{rel}/#{d}", mid
1501        end        end
1502        run_hook "post-#{task}"        run_hook "post-#{task}"
# Line 1312  class Installer Line 1518  class Installer
1518      @currdir = File.dirname(rel)      @currdir = File.dirname(rel)
1519    end    end
1520    
1521  end    def run_hook(id)
1522        path = [ "#{curr_srcdir()}/#{id}",
1523                 "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
1524        return unless path
1525        begin
1526          instance_eval File.read(path), path, 1
1527        rescue
1528          raise if $DEBUG
1529          setup_rb_error "hook #{path} failed:\n" + $!.message
1530        end
1531      end
1532    
1533    end   # class Installer
1534    
1535    
1536    class SetupError < StandardError; end
1537    
1538    def setup_rb_error(msg)
1539      raise SetupError, msg
1540    end
1541    
1542  if $0 == __FILE__  if $0 == __FILE__
1543    begin    begin
1544      if multipackage_install?      ToplevelInstaller.invoke
1545        ToplevelInstallerMulti.invoke    rescue SetupError
     else  
       ToplevelInstaller.invoke  
     end  
   rescue  
1546      raise if $DEBUG      raise if $DEBUG
1547      $stderr.puts $!.message      $stderr.puts $!.message
1548      $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."      $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."

Legend:
Removed from v.1.3  
changed lines
  Added in v.1.4

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26