Chris@909: # Chris@909: # setup.rb Chris@909: # Chris@909: # Copyright (c) 2000-2005 Minero Aoki Chris@909: # Chris@909: # This program is free software. Chris@909: # You can distribute/modify this program under the terms of Chris@909: # the GNU LGPL, Lesser General Public License version 2.1. Chris@909: # Chris@909: Chris@909: unless Enumerable.method_defined?(:map) # Ruby 1.4.6 Chris@909: module Enumerable Chris@909: alias map collect Chris@909: end Chris@909: end Chris@909: Chris@909: unless File.respond_to?(:read) # Ruby 1.6 Chris@909: def File.read(fname) Chris@909: open(fname) {|f| Chris@909: return f.read Chris@909: } Chris@909: end Chris@909: end Chris@909: Chris@909: unless Errno.const_defined?(:ENOTEMPTY) # Windows? Chris@909: module Errno Chris@909: class ENOTEMPTY Chris@909: # We do not raise this exception, implementation is not needed. Chris@909: end Chris@909: end Chris@909: end Chris@909: Chris@909: def File.binread(fname) Chris@909: open(fname, 'rb') {|f| Chris@909: return f.read Chris@909: } Chris@909: end Chris@909: Chris@909: # for corrupted Windows' stat(2) Chris@909: def File.dir?(path) Chris@909: File.directory?((path[-1,1] == '/') ? path : path + '/') Chris@909: end Chris@909: Chris@909: Chris@909: class ConfigTable Chris@909: Chris@909: include Enumerable Chris@909: Chris@909: def initialize(rbconfig) Chris@909: @rbconfig = rbconfig Chris@909: @items = [] Chris@909: @table = {} Chris@909: # options Chris@909: @install_prefix = nil Chris@909: @config_opt = nil Chris@909: @verbose = true Chris@909: @no_harm = false Chris@909: end Chris@909: Chris@909: attr_accessor :install_prefix Chris@909: attr_accessor :config_opt Chris@909: Chris@909: attr_writer :verbose Chris@909: Chris@909: def verbose? Chris@909: @verbose Chris@909: end Chris@909: Chris@909: attr_writer :no_harm Chris@909: Chris@909: def no_harm? Chris@909: @no_harm Chris@909: end Chris@909: Chris@909: def [](key) Chris@909: lookup(key).resolve(self) Chris@909: end Chris@909: Chris@909: def []=(key, val) Chris@909: lookup(key).set val Chris@909: end Chris@909: Chris@909: def names Chris@909: @items.map {|i| i.name } Chris@909: end Chris@909: Chris@909: def each(&block) Chris@909: @items.each(&block) Chris@909: end Chris@909: Chris@909: def key?(name) Chris@909: @table.key?(name) Chris@909: end Chris@909: Chris@909: def lookup(name) Chris@909: @table[name] or setup_rb_error "no such config item: #{name}" Chris@909: end Chris@909: Chris@909: def add(item) Chris@909: @items.push item Chris@909: @table[item.name] = item Chris@909: end Chris@909: Chris@909: def remove(name) Chris@909: item = lookup(name) Chris@909: @items.delete_if {|i| i.name == name } Chris@909: @table.delete_if {|name, i| i.name == name } Chris@909: item Chris@909: end Chris@909: Chris@909: def load_script(path, inst = nil) Chris@909: if File.file?(path) Chris@909: MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path Chris@909: end Chris@909: end Chris@909: Chris@909: def savefile Chris@909: '.config' Chris@909: end Chris@909: Chris@909: def load_savefile Chris@909: begin Chris@909: File.foreach(savefile()) do |line| Chris@909: k, v = *line.split(/=/, 2) Chris@909: self[k] = v.strip Chris@909: end Chris@909: rescue Errno::ENOENT Chris@909: setup_rb_error $!.message + "\n#{File.basename($0)} config first" Chris@909: end Chris@909: end Chris@909: Chris@909: def save Chris@909: @items.each {|i| i.value } Chris@909: File.open(savefile(), 'w') {|f| Chris@909: @items.each do |i| Chris@909: f.printf "%s=%s\n", i.name, i.value if i.value? and i.value Chris@909: end Chris@909: } Chris@909: end Chris@909: Chris@909: def load_standard_entries Chris@909: standard_entries(@rbconfig).each do |ent| Chris@909: add ent Chris@909: end Chris@909: end Chris@909: Chris@909: def standard_entries(rbconfig) Chris@909: c = rbconfig Chris@909: Chris@909: rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT']) Chris@909: Chris@909: major = c['MAJOR'].to_i Chris@909: minor = c['MINOR'].to_i Chris@909: teeny = c['TEENY'].to_i Chris@909: version = "#{major}.#{minor}" Chris@909: Chris@909: # ruby ver. >= 1.4.4? Chris@909: newpath_p = ((major >= 2) or Chris@909: ((major == 1) and Chris@909: ((minor >= 5) or Chris@909: ((minor == 4) and (teeny >= 4))))) Chris@909: Chris@909: if c['rubylibdir'] Chris@909: # V > 1.6.3 Chris@909: libruby = "#{c['prefix']}/lib/ruby" Chris@909: librubyver = c['rubylibdir'] Chris@909: librubyverarch = c['archdir'] Chris@909: siteruby = c['sitedir'] Chris@909: siterubyver = c['sitelibdir'] Chris@909: siterubyverarch = c['sitearchdir'] Chris@909: elsif newpath_p Chris@909: # 1.4.4 <= V <= 1.6.3 Chris@909: libruby = "#{c['prefix']}/lib/ruby" Chris@909: librubyver = "#{c['prefix']}/lib/ruby/#{version}" Chris@909: librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" Chris@909: siteruby = c['sitedir'] Chris@909: siterubyver = "$siteruby/#{version}" Chris@909: siterubyverarch = "$siterubyver/#{c['arch']}" Chris@909: else Chris@909: # V < 1.4.4 Chris@909: libruby = "#{c['prefix']}/lib/ruby" Chris@909: librubyver = "#{c['prefix']}/lib/ruby/#{version}" Chris@909: librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" Chris@909: siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby" Chris@909: siterubyver = siteruby Chris@909: siterubyverarch = "$siterubyver/#{c['arch']}" Chris@909: end Chris@909: parameterize = lambda {|path| Chris@909: path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix') Chris@909: } Chris@909: Chris@909: if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } Chris@909: makeprog = arg.sub(/'/, '').split(/=/, 2)[1] Chris@909: else Chris@909: makeprog = 'make' Chris@909: end Chris@909: Chris@909: [ Chris@909: ExecItem.new('installdirs', 'std/site/home', Chris@909: 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\ Chris@909: {|val, table| Chris@909: case val Chris@909: when 'std' Chris@909: table['rbdir'] = '$librubyver' Chris@909: table['sodir'] = '$librubyverarch' Chris@909: when 'site' Chris@909: table['rbdir'] = '$siterubyver' Chris@909: table['sodir'] = '$siterubyverarch' Chris@909: when 'home' Chris@909: setup_rb_error '$HOME was not set' unless ENV['HOME'] Chris@909: table['prefix'] = ENV['HOME'] Chris@909: table['rbdir'] = '$libdir/ruby' Chris@909: table['sodir'] = '$libdir/ruby' Chris@909: end Chris@909: }, Chris@909: PathItem.new('prefix', 'path', c['prefix'], Chris@909: 'path prefix of target environment'), Chris@909: PathItem.new('bindir', 'path', parameterize.call(c['bindir']), Chris@909: 'the directory for commands'), Chris@909: PathItem.new('libdir', 'path', parameterize.call(c['libdir']), Chris@909: 'the directory for libraries'), Chris@909: PathItem.new('datadir', 'path', parameterize.call(c['datadir']), Chris@909: 'the directory for shared data'), Chris@909: PathItem.new('mandir', 'path', parameterize.call(c['mandir']), Chris@909: 'the directory for man pages'), Chris@909: PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']), Chris@909: 'the directory for system configuration files'), Chris@909: PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']), Chris@909: 'the directory for local state data'), Chris@909: PathItem.new('libruby', 'path', libruby, Chris@909: 'the directory for ruby libraries'), Chris@909: PathItem.new('librubyver', 'path', librubyver, Chris@909: 'the directory for standard ruby libraries'), Chris@909: PathItem.new('librubyverarch', 'path', librubyverarch, Chris@909: 'the directory for standard ruby extensions'), Chris@909: PathItem.new('siteruby', 'path', siteruby, Chris@909: 'the directory for version-independent aux ruby libraries'), Chris@909: PathItem.new('siterubyver', 'path', siterubyver, Chris@909: 'the directory for aux ruby libraries'), Chris@909: PathItem.new('siterubyverarch', 'path', siterubyverarch, Chris@909: 'the directory for aux ruby binaries'), Chris@909: PathItem.new('rbdir', 'path', '$siterubyver', Chris@909: 'the directory for ruby scripts'), Chris@909: PathItem.new('sodir', 'path', '$siterubyverarch', Chris@909: 'the directory for ruby extentions'), Chris@909: PathItem.new('rubypath', 'path', rubypath, Chris@909: 'the path to set to #! line'), Chris@909: ProgramItem.new('rubyprog', 'name', rubypath, Chris@909: 'the ruby program using for installation'), Chris@909: ProgramItem.new('makeprog', 'name', makeprog, Chris@909: 'the make program to compile ruby extentions'), Chris@909: SelectItem.new('shebang', 'all/ruby/never', 'ruby', Chris@909: 'shebang line (#!) editing mode'), Chris@909: BoolItem.new('without-ext', 'yes/no', 'no', Chris@909: 'does not compile/install ruby extentions') Chris@909: ] Chris@909: end Chris@909: private :standard_entries Chris@909: Chris@909: def load_multipackage_entries Chris@909: multipackage_entries().each do |ent| Chris@909: add ent Chris@909: end Chris@909: end Chris@909: Chris@909: def multipackage_entries Chris@909: [ Chris@909: PackageSelectionItem.new('with', 'name,name...', '', 'ALL', Chris@909: 'package names that you want to install'), Chris@909: PackageSelectionItem.new('without', 'name,name...', '', 'NONE', Chris@909: 'package names that you do not want to install') Chris@909: ] Chris@909: end Chris@909: private :multipackage_entries Chris@909: Chris@909: ALIASES = { Chris@909: 'std-ruby' => 'librubyver', Chris@909: 'stdruby' => 'librubyver', Chris@909: 'rubylibdir' => 'librubyver', Chris@909: 'archdir' => 'librubyverarch', Chris@909: 'site-ruby-common' => 'siteruby', # For backward compatibility Chris@909: 'site-ruby' => 'siterubyver', # For backward compatibility Chris@909: 'bin-dir' => 'bindir', Chris@909: 'bin-dir' => 'bindir', Chris@909: 'rb-dir' => 'rbdir', Chris@909: 'so-dir' => 'sodir', Chris@909: 'data-dir' => 'datadir', Chris@909: 'ruby-path' => 'rubypath', Chris@909: 'ruby-prog' => 'rubyprog', Chris@909: 'ruby' => 'rubyprog', Chris@909: 'make-prog' => 'makeprog', Chris@909: 'make' => 'makeprog' Chris@909: } Chris@909: Chris@909: def fixup Chris@909: ALIASES.each do |ali, name| Chris@909: @table[ali] = @table[name] Chris@909: end Chris@909: @items.freeze Chris@909: @table.freeze Chris@909: @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/ Chris@909: end Chris@909: Chris@909: def parse_opt(opt) Chris@909: m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}" Chris@909: m.to_a[1,2] Chris@909: end Chris@909: Chris@909: def dllext Chris@909: @rbconfig['DLEXT'] Chris@909: end Chris@909: Chris@909: def value_config?(name) Chris@909: lookup(name).value? Chris@909: end Chris@909: Chris@909: class Item Chris@909: def initialize(name, template, default, desc) Chris@909: @name = name.freeze Chris@909: @template = template Chris@909: @value = default Chris@909: @default = default Chris@909: @description = desc Chris@909: end Chris@909: Chris@909: attr_reader :name Chris@909: attr_reader :description Chris@909: Chris@909: attr_accessor :default Chris@909: alias help_default default Chris@909: Chris@909: def help_opt Chris@909: "--#{@name}=#{@template}" Chris@909: end Chris@909: Chris@909: def value? Chris@909: true Chris@909: end Chris@909: Chris@909: def value Chris@909: @value Chris@909: end Chris@909: Chris@909: def resolve(table) Chris@909: @value.gsub(%r<\$([^/]+)>) { table[$1] } Chris@909: end Chris@909: Chris@909: def set(val) Chris@909: @value = check(val) Chris@909: end Chris@909: Chris@909: private Chris@909: Chris@909: def check(val) Chris@909: setup_rb_error "config: --#{name} requires argument" unless val Chris@909: val Chris@909: end Chris@909: end Chris@909: Chris@909: class BoolItem < Item Chris@909: def config_type Chris@909: 'bool' Chris@909: end Chris@909: Chris@909: def help_opt Chris@909: "--#{@name}" Chris@909: end Chris@909: Chris@909: private Chris@909: Chris@909: def check(val) Chris@909: return 'yes' unless val Chris@909: case val Chris@909: when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes' Chris@909: when /\An(o)?\z/i, /\Af(alse)\z/i then 'no' Chris@909: else Chris@909: setup_rb_error "config: --#{@name} accepts only yes/no for argument" Chris@909: end Chris@909: end Chris@909: end Chris@909: Chris@909: class PathItem < Item Chris@909: def config_type Chris@909: 'path' Chris@909: end Chris@909: Chris@909: private Chris@909: Chris@909: def check(path) Chris@909: setup_rb_error "config: --#{@name} requires argument" unless path Chris@909: path[0,1] == '$' ? path : File.expand_path(path) Chris@909: end Chris@909: end Chris@909: Chris@909: class ProgramItem < Item Chris@909: def config_type Chris@909: 'program' Chris@909: end Chris@909: end Chris@909: Chris@909: class SelectItem < Item Chris@909: def initialize(name, selection, default, desc) Chris@909: super Chris@909: @ok = selection.split('/') Chris@909: end Chris@909: Chris@909: def config_type Chris@909: 'select' Chris@909: end Chris@909: Chris@909: private Chris@909: Chris@909: def check(val) Chris@909: unless @ok.include?(val.strip) Chris@909: setup_rb_error "config: use --#{@name}=#{@template} (#{val})" Chris@909: end Chris@909: val.strip Chris@909: end Chris@909: end Chris@909: Chris@909: class ExecItem < Item Chris@909: def initialize(name, selection, desc, &block) Chris@909: super name, selection, nil, desc Chris@909: @ok = selection.split('/') Chris@909: @action = block Chris@909: end Chris@909: Chris@909: def config_type Chris@909: 'exec' Chris@909: end Chris@909: Chris@909: def value? Chris@909: false Chris@909: end Chris@909: Chris@909: def resolve(table) Chris@909: setup_rb_error "$#{name()} wrongly used as option value" Chris@909: end Chris@909: Chris@909: undef set Chris@909: Chris@909: def evaluate(val, table) Chris@909: v = val.strip.downcase Chris@909: unless @ok.include?(v) Chris@909: setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})" Chris@909: end Chris@909: @action.call v, table Chris@909: end Chris@909: end Chris@909: Chris@909: class PackageSelectionItem < Item Chris@909: def initialize(name, template, default, help_default, desc) Chris@909: super name, template, default, desc Chris@909: @help_default = help_default Chris@909: end Chris@909: Chris@909: attr_reader :help_default Chris@909: Chris@909: def config_type Chris@909: 'package' Chris@909: end Chris@909: Chris@909: private Chris@909: Chris@909: def check(val) Chris@909: unless File.dir?("packages/#{val}") Chris@909: setup_rb_error "config: no such package: #{val}" Chris@909: end Chris@909: val Chris@909: end Chris@909: end Chris@909: Chris@909: class MetaConfigEnvironment Chris@909: def initialize(config, installer) Chris@909: @config = config Chris@909: @installer = installer Chris@909: end Chris@909: Chris@909: def config_names Chris@909: @config.names Chris@909: end Chris@909: Chris@909: def config?(name) Chris@909: @config.key?(name) Chris@909: end Chris@909: Chris@909: def bool_config?(name) Chris@909: @config.lookup(name).config_type == 'bool' Chris@909: end Chris@909: Chris@909: def path_config?(name) Chris@909: @config.lookup(name).config_type == 'path' Chris@909: end Chris@909: Chris@909: def value_config?(name) Chris@909: @config.lookup(name).config_type != 'exec' Chris@909: end Chris@909: Chris@909: def add_config(item) Chris@909: @config.add item Chris@909: end Chris@909: Chris@909: def add_bool_config(name, default, desc) Chris@909: @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc) Chris@909: end Chris@909: Chris@909: def add_path_config(name, default, desc) Chris@909: @config.add PathItem.new(name, 'path', default, desc) Chris@909: end Chris@909: Chris@909: def set_config_default(name, default) Chris@909: @config.lookup(name).default = default Chris@909: end Chris@909: Chris@909: def remove_config(name) Chris@909: @config.remove(name) Chris@909: end Chris@909: Chris@909: # For only multipackage Chris@909: def packages Chris@909: raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer Chris@909: @installer.packages Chris@909: end Chris@909: Chris@909: # For only multipackage Chris@909: def declare_packages(list) Chris@909: raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer Chris@909: @installer.packages = list Chris@909: end Chris@909: end Chris@909: Chris@909: end # class ConfigTable Chris@909: Chris@909: Chris@909: # This module requires: #verbose?, #no_harm? Chris@909: module FileOperations Chris@909: Chris@909: def mkdir_p(dirname, prefix = nil) Chris@909: dirname = prefix + File.expand_path(dirname) if prefix Chris@909: $stderr.puts "mkdir -p #{dirname}" if verbose? Chris@909: return if no_harm? Chris@909: Chris@909: # Does not check '/', it's too abnormal. Chris@909: dirs = File.expand_path(dirname).split(%r<(?=/)>) Chris@909: if /\A[a-z]:\z/i =~ dirs[0] Chris@909: disk = dirs.shift Chris@909: dirs[0] = disk + dirs[0] Chris@909: end Chris@909: dirs.each_index do |idx| Chris@909: path = dirs[0..idx].join('') Chris@909: Dir.mkdir path unless File.dir?(path) Chris@909: end Chris@909: end Chris@909: Chris@909: def rm_f(path) Chris@909: $stderr.puts "rm -f #{path}" if verbose? Chris@909: return if no_harm? Chris@909: force_remove_file path Chris@909: end Chris@909: Chris@909: def rm_rf(path) Chris@909: $stderr.puts "rm -rf #{path}" if verbose? Chris@909: return if no_harm? Chris@909: remove_tree path Chris@909: end Chris@909: Chris@909: def remove_tree(path) Chris@909: if File.symlink?(path) Chris@909: remove_file path Chris@909: elsif File.dir?(path) Chris@909: remove_tree0 path Chris@909: else Chris@909: force_remove_file path Chris@909: end Chris@909: end Chris@909: Chris@909: def remove_tree0(path) Chris@909: Dir.foreach(path) do |ent| Chris@909: next if ent == '.' Chris@909: next if ent == '..' Chris@909: entpath = "#{path}/#{ent}" Chris@909: if File.symlink?(entpath) Chris@909: remove_file entpath Chris@909: elsif File.dir?(entpath) Chris@909: remove_tree0 entpath Chris@909: else Chris@909: force_remove_file entpath Chris@909: end Chris@909: end Chris@909: begin Chris@909: Dir.rmdir path Chris@909: rescue Errno::ENOTEMPTY Chris@909: # directory may not be empty Chris@909: end Chris@909: end Chris@909: Chris@909: def move_file(src, dest) Chris@909: force_remove_file dest Chris@909: begin Chris@909: File.rename src, dest Chris@909: rescue Chris@909: File.open(dest, 'wb') {|f| Chris@909: f.write File.binread(src) Chris@909: } Chris@909: File.chmod File.stat(src).mode, dest Chris@909: File.unlink src Chris@909: end Chris@909: end Chris@909: Chris@909: def force_remove_file(path) Chris@909: begin Chris@909: remove_file path Chris@909: rescue Chris@909: end Chris@909: end Chris@909: Chris@909: def remove_file(path) Chris@909: File.chmod 0777, path Chris@909: File.unlink path Chris@909: end Chris@909: Chris@909: def install(from, dest, mode, prefix = nil) Chris@909: $stderr.puts "install #{from} #{dest}" if verbose? Chris@909: return if no_harm? Chris@909: Chris@909: realdest = prefix ? prefix + File.expand_path(dest) : dest Chris@909: realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest) Chris@909: str = File.binread(from) Chris@909: if diff?(str, realdest) Chris@909: verbose_off { Chris@909: rm_f realdest if File.exist?(realdest) Chris@909: } Chris@909: File.open(realdest, 'wb') {|f| Chris@909: f.write str Chris@909: } Chris@909: File.chmod mode, realdest Chris@909: Chris@909: File.open("#{objdir_root()}/InstalledFiles", 'a') {|f| Chris@909: if prefix Chris@909: f.puts realdest.sub(prefix, '') Chris@909: else Chris@909: f.puts realdest Chris@909: end Chris@909: } Chris@909: end Chris@909: end Chris@909: Chris@909: def diff?(new_content, path) Chris@909: return true unless File.exist?(path) Chris@909: new_content != File.binread(path) Chris@909: end Chris@909: Chris@909: def command(*args) Chris@909: $stderr.puts args.join(' ') if verbose? Chris@909: system(*args) or raise RuntimeError, Chris@909: "system(#{args.map{|a| a.inspect }.join(' ')}) failed" Chris@909: end Chris@909: Chris@909: def ruby(*args) Chris@909: command config('rubyprog'), *args Chris@909: end Chris@909: Chris@909: def make(task = nil) Chris@909: command(*[config('makeprog'), task].compact) Chris@909: end Chris@909: Chris@909: def extdir?(dir) Chris@909: File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb") Chris@909: end Chris@909: Chris@909: def files_of(dir) Chris@909: Dir.open(dir) {|d| Chris@909: return d.select {|ent| File.file?("#{dir}/#{ent}") } Chris@909: } Chris@909: end Chris@909: Chris@909: DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn ) Chris@909: Chris@909: def directories_of(dir) Chris@909: Dir.open(dir) {|d| Chris@909: return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT Chris@909: } Chris@909: end Chris@909: Chris@909: end Chris@909: Chris@909: Chris@909: # This module requires: #srcdir_root, #objdir_root, #relpath Chris@909: module HookScriptAPI Chris@909: Chris@909: def get_config(key) Chris@909: @config[key] Chris@909: end Chris@909: Chris@909: alias config get_config Chris@909: Chris@909: # obsolete: use metaconfig to change configuration Chris@909: def set_config(key, val) Chris@909: @config[key] = val Chris@909: end Chris@909: Chris@909: # Chris@909: # srcdir/objdir (works only in the package directory) Chris@909: # Chris@909: Chris@909: def curr_srcdir Chris@909: "#{srcdir_root()}/#{relpath()}" Chris@909: end Chris@909: Chris@909: def curr_objdir Chris@909: "#{objdir_root()}/#{relpath()}" Chris@909: end Chris@909: Chris@909: def srcfile(path) Chris@909: "#{curr_srcdir()}/#{path}" Chris@909: end Chris@909: Chris@909: def srcexist?(path) Chris@909: File.exist?(srcfile(path)) Chris@909: end Chris@909: Chris@909: def srcdirectory?(path) Chris@909: File.dir?(srcfile(path)) Chris@909: end Chris@909: Chris@909: def srcfile?(path) Chris@909: File.file?(srcfile(path)) Chris@909: end Chris@909: Chris@909: def srcentries(path = '.') Chris@909: Dir.open("#{curr_srcdir()}/#{path}") {|d| Chris@909: return d.to_a - %w(. ..) Chris@909: } Chris@909: end Chris@909: Chris@909: def srcfiles(path = '.') Chris@909: srcentries(path).select {|fname| Chris@909: File.file?(File.join(curr_srcdir(), path, fname)) Chris@909: } Chris@909: end Chris@909: Chris@909: def srcdirectories(path = '.') Chris@909: srcentries(path).select {|fname| Chris@909: File.dir?(File.join(curr_srcdir(), path, fname)) Chris@909: } Chris@909: end Chris@909: Chris@909: end Chris@909: Chris@909: Chris@909: class ToplevelInstaller Chris@909: Chris@909: Version = '3.4.1' Chris@909: Copyright = 'Copyright (c) 2000-2005 Minero Aoki' Chris@909: Chris@909: TASKS = [ Chris@909: [ 'all', 'do config, setup, then install' ], Chris@909: [ 'config', 'saves your configurations' ], Chris@909: [ 'show', 'shows current configuration' ], Chris@909: [ 'setup', 'compiles ruby extentions and others' ], Chris@909: [ 'install', 'installs files' ], Chris@909: [ 'test', 'run all tests in test/' ], Chris@909: [ 'clean', "does `make clean' for each extention" ], Chris@909: [ 'distclean',"does `make distclean' for each extention" ] Chris@909: ] Chris@909: Chris@909: def ToplevelInstaller.invoke Chris@909: config = ConfigTable.new(load_rbconfig()) Chris@909: config.load_standard_entries Chris@909: config.load_multipackage_entries if multipackage? Chris@909: config.fixup Chris@909: klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller) Chris@909: klass.new(File.dirname($0), config).invoke Chris@909: end Chris@909: Chris@909: def ToplevelInstaller.multipackage? Chris@909: File.dir?(File.dirname($0) + '/packages') Chris@909: end Chris@909: Chris@909: def ToplevelInstaller.load_rbconfig Chris@909: if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg } Chris@909: ARGV.delete(arg) Chris@909: load File.expand_path(arg.split(/=/, 2)[1]) Chris@909: $".push 'rbconfig.rb' Chris@909: else Chris@909: require 'rbconfig' Chris@909: end Chris@909: ::Config::CONFIG Chris@909: end Chris@909: Chris@909: def initialize(ardir_root, config) Chris@909: @ardir = File.expand_path(ardir_root) Chris@909: @config = config Chris@909: # cache Chris@909: @valid_task_re = nil Chris@909: end Chris@909: Chris@909: def config(key) Chris@909: @config[key] Chris@909: end Chris@909: Chris@909: def inspect Chris@909: "#<#{self.class} #{__id__()}>" Chris@909: end Chris@909: Chris@909: def invoke Chris@909: run_metaconfigs Chris@909: case task = parsearg_global() Chris@909: when nil, 'all' Chris@909: parsearg_config Chris@909: init_installers Chris@909: exec_config Chris@909: exec_setup Chris@909: exec_install Chris@909: else Chris@909: case task Chris@909: when 'config', 'test' Chris@909: ; Chris@909: when 'clean', 'distclean' Chris@909: @config.load_savefile if File.exist?(@config.savefile) Chris@909: else Chris@909: @config.load_savefile Chris@909: end Chris@909: __send__ "parsearg_#{task}" Chris@909: init_installers Chris@909: __send__ "exec_#{task}" Chris@909: end Chris@909: end Chris@909: Chris@909: def run_metaconfigs Chris@909: @config.load_script "#{@ardir}/metaconfig" Chris@909: end Chris@909: Chris@909: def init_installers Chris@909: @installer = Installer.new(@config, @ardir, File.expand_path('.')) Chris@909: end Chris@909: Chris@909: # Chris@909: # Hook Script API bases Chris@909: # Chris@909: Chris@909: def srcdir_root Chris@909: @ardir Chris@909: end Chris@909: Chris@909: def objdir_root Chris@909: '.' Chris@909: end Chris@909: Chris@909: def relpath Chris@909: '.' Chris@909: end Chris@909: Chris@909: # Chris@909: # Option Parsing Chris@909: # Chris@909: Chris@909: def parsearg_global Chris@909: while arg = ARGV.shift Chris@909: case arg Chris@909: when /\A\w+\z/ Chris@909: setup_rb_error "invalid task: #{arg}" unless valid_task?(arg) Chris@909: return arg Chris@909: when '-q', '--quiet' Chris@909: @config.verbose = false Chris@909: when '--verbose' Chris@909: @config.verbose = true Chris@909: when '--help' Chris@909: print_usage $stdout Chris@909: exit 0 Chris@909: when '--version' Chris@909: puts "#{File.basename($0)} version #{Version}" Chris@909: exit 0 Chris@909: when '--copyright' Chris@909: puts Copyright Chris@909: exit 0 Chris@909: else Chris@909: setup_rb_error "unknown global option '#{arg}'" Chris@909: end Chris@909: end Chris@909: nil Chris@909: end Chris@909: Chris@909: def valid_task?(t) Chris@909: valid_task_re() =~ t Chris@909: end Chris@909: Chris@909: def valid_task_re Chris@909: @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/ Chris@909: end Chris@909: Chris@909: def parsearg_no_options Chris@909: unless ARGV.empty? Chris@909: task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1) Chris@909: setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}" Chris@909: end Chris@909: end Chris@909: Chris@909: alias parsearg_show parsearg_no_options Chris@909: alias parsearg_setup parsearg_no_options Chris@909: alias parsearg_test parsearg_no_options Chris@909: alias parsearg_clean parsearg_no_options Chris@909: alias parsearg_distclean parsearg_no_options Chris@909: Chris@909: def parsearg_config Chris@909: evalopt = [] Chris@909: set = [] Chris@909: @config.config_opt = [] Chris@909: while i = ARGV.shift Chris@909: if /\A--?\z/ =~ i Chris@909: @config.config_opt = ARGV.dup Chris@909: break Chris@909: end Chris@909: name, value = *@config.parse_opt(i) Chris@909: if @config.value_config?(name) Chris@909: @config[name] = value Chris@909: else Chris@909: evalopt.push [name, value] Chris@909: end Chris@909: set.push name Chris@909: end Chris@909: evalopt.each do |name, value| Chris@909: @config.lookup(name).evaluate value, @config Chris@909: end Chris@909: # Check if configuration is valid Chris@909: set.each do |n| Chris@909: @config[n] if @config.value_config?(n) Chris@909: end Chris@909: end Chris@909: Chris@909: def parsearg_install Chris@909: @config.no_harm = false Chris@909: @config.install_prefix = '' Chris@909: while a = ARGV.shift Chris@909: case a Chris@909: when '--no-harm' Chris@909: @config.no_harm = true Chris@909: when /\A--prefix=/ Chris@909: path = a.split(/=/, 2)[1] Chris@909: path = File.expand_path(path) unless path[0,1] == '/' Chris@909: @config.install_prefix = path Chris@909: else Chris@909: setup_rb_error "install: unknown option #{a}" Chris@909: end Chris@909: end Chris@909: end Chris@909: Chris@909: def print_usage(out) Chris@909: out.puts 'Typical Installation Procedure:' Chris@909: out.puts " $ ruby #{File.basename $0} config" Chris@909: out.puts " $ ruby #{File.basename $0} setup" Chris@909: out.puts " # ruby #{File.basename $0} install (may require root privilege)" Chris@909: out.puts Chris@909: out.puts 'Detailed Usage:' Chris@909: out.puts " ruby #{File.basename $0} " Chris@909: out.puts " ruby #{File.basename $0} [] []" Chris@909: Chris@909: fmt = " %-24s %s\n" Chris@909: out.puts Chris@909: out.puts 'Global options:' Chris@909: out.printf fmt, '-q,--quiet', 'suppress message outputs' Chris@909: out.printf fmt, ' --verbose', 'output messages verbosely' Chris@909: out.printf fmt, ' --help', 'print this message' Chris@909: out.printf fmt, ' --version', 'print version and quit' Chris@909: out.printf fmt, ' --copyright', 'print copyright and quit' Chris@909: out.puts Chris@909: out.puts 'Tasks:' Chris@909: TASKS.each do |name, desc| Chris@909: out.printf fmt, name, desc Chris@909: end Chris@909: Chris@909: fmt = " %-24s %s [%s]\n" Chris@909: out.puts Chris@909: out.puts 'Options for CONFIG or ALL:' Chris@909: @config.each do |item| Chris@909: out.printf fmt, item.help_opt, item.description, item.help_default Chris@909: end Chris@909: out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's" Chris@909: out.puts Chris@909: out.puts 'Options for INSTALL:' Chris@909: out.printf fmt, '--no-harm', 'only display what to do if given', 'off' Chris@909: out.printf fmt, '--prefix=path', 'install path prefix', '' Chris@909: out.puts Chris@909: end Chris@909: Chris@909: # Chris@909: # Task Handlers Chris@909: # Chris@909: Chris@909: def exec_config Chris@909: @installer.exec_config Chris@909: @config.save # must be final Chris@909: end Chris@909: Chris@909: def exec_setup Chris@909: @installer.exec_setup Chris@909: end Chris@909: Chris@909: def exec_install Chris@909: @installer.exec_install Chris@909: end Chris@909: Chris@909: def exec_test Chris@909: @installer.exec_test Chris@909: end Chris@909: Chris@909: def exec_show Chris@909: @config.each do |i| Chris@909: printf "%-20s %s\n", i.name, i.value if i.value? Chris@909: end Chris@909: end Chris@909: Chris@909: def exec_clean Chris@909: @installer.exec_clean Chris@909: end Chris@909: Chris@909: def exec_distclean Chris@909: @installer.exec_distclean Chris@909: end Chris@909: Chris@909: end # class ToplevelInstaller Chris@909: Chris@909: Chris@909: class ToplevelInstallerMulti < ToplevelInstaller Chris@909: Chris@909: include FileOperations Chris@909: Chris@909: def initialize(ardir_root, config) Chris@909: super Chris@909: @packages = directories_of("#{@ardir}/packages") Chris@909: raise 'no package exists' if @packages.empty? Chris@909: @root_installer = Installer.new(@config, @ardir, File.expand_path('.')) Chris@909: end Chris@909: Chris@909: def run_metaconfigs Chris@909: @config.load_script "#{@ardir}/metaconfig", self Chris@909: @packages.each do |name| Chris@909: @config.load_script "#{@ardir}/packages/#{name}/metaconfig" Chris@909: end Chris@909: end Chris@909: Chris@909: attr_reader :packages Chris@909: Chris@909: def packages=(list) Chris@909: raise 'package list is empty' if list.empty? Chris@909: list.each do |name| Chris@909: raise "directory packages/#{name} does not exist"\ Chris@909: unless File.dir?("#{@ardir}/packages/#{name}") Chris@909: end Chris@909: @packages = list Chris@909: end Chris@909: Chris@909: def init_installers Chris@909: @installers = {} Chris@909: @packages.each do |pack| Chris@909: @installers[pack] = Installer.new(@config, Chris@909: "#{@ardir}/packages/#{pack}", Chris@909: "packages/#{pack}") Chris@909: end Chris@909: with = extract_selection(config('with')) Chris@909: without = extract_selection(config('without')) Chris@909: @selected = @installers.keys.select {|name| Chris@909: (with.empty? or with.include?(name)) \ Chris@909: and not without.include?(name) Chris@909: } Chris@909: end Chris@909: Chris@909: def extract_selection(list) Chris@909: a = list.split(/,/) Chris@909: a.each do |name| Chris@909: setup_rb_error "no such package: #{name}" unless @installers.key?(name) Chris@909: end Chris@909: a Chris@909: end Chris@909: Chris@909: def print_usage(f) Chris@909: super Chris@909: f.puts 'Inluded packages:' Chris@909: f.puts ' ' + @packages.sort.join(' ') Chris@909: f.puts Chris@909: end Chris@909: Chris@909: # Chris@909: # Task Handlers Chris@909: # Chris@909: Chris@909: def exec_config Chris@909: run_hook 'pre-config' Chris@909: each_selected_installers {|inst| inst.exec_config } Chris@909: run_hook 'post-config' Chris@909: @config.save # must be final Chris@909: end Chris@909: Chris@909: def exec_setup Chris@909: run_hook 'pre-setup' Chris@909: each_selected_installers {|inst| inst.exec_setup } Chris@909: run_hook 'post-setup' Chris@909: end Chris@909: Chris@909: def exec_install Chris@909: run_hook 'pre-install' Chris@909: each_selected_installers {|inst| inst.exec_install } Chris@909: run_hook 'post-install' Chris@909: end Chris@909: Chris@909: def exec_test Chris@909: run_hook 'pre-test' Chris@909: each_selected_installers {|inst| inst.exec_test } Chris@909: run_hook 'post-test' Chris@909: end Chris@909: Chris@909: def exec_clean Chris@909: rm_f @config.savefile Chris@909: run_hook 'pre-clean' Chris@909: each_selected_installers {|inst| inst.exec_clean } Chris@909: run_hook 'post-clean' Chris@909: end Chris@909: Chris@909: def exec_distclean Chris@909: rm_f @config.savefile Chris@909: run_hook 'pre-distclean' Chris@909: each_selected_installers {|inst| inst.exec_distclean } Chris@909: run_hook 'post-distclean' Chris@909: end Chris@909: Chris@909: # Chris@909: # lib Chris@909: # Chris@909: Chris@909: def each_selected_installers Chris@909: Dir.mkdir 'packages' unless File.dir?('packages') Chris@909: @selected.each do |pack| Chris@909: $stderr.puts "Processing the package `#{pack}' ..." if verbose? Chris@909: Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") Chris@909: Dir.chdir "packages/#{pack}" Chris@909: yield @installers[pack] Chris@909: Dir.chdir '../..' Chris@909: end Chris@909: end Chris@909: Chris@909: def run_hook(id) Chris@909: @root_installer.run_hook id Chris@909: end Chris@909: Chris@909: # module FileOperations requires this Chris@909: def verbose? Chris@909: @config.verbose? Chris@909: end Chris@909: Chris@909: # module FileOperations requires this Chris@909: def no_harm? Chris@909: @config.no_harm? Chris@909: end Chris@909: Chris@909: end # class ToplevelInstallerMulti Chris@909: Chris@909: Chris@909: class Installer Chris@909: Chris@909: FILETYPES = %w( bin lib ext data conf man ) Chris@909: Chris@909: include FileOperations Chris@909: include HookScriptAPI Chris@909: Chris@909: def initialize(config, srcroot, objroot) Chris@909: @config = config Chris@909: @srcdir = File.expand_path(srcroot) Chris@909: @objdir = File.expand_path(objroot) Chris@909: @currdir = '.' Chris@909: end Chris@909: Chris@909: def inspect Chris@909: "#<#{self.class} #{File.basename(@srcdir)}>" Chris@909: end Chris@909: Chris@909: def noop(rel) Chris@909: end Chris@909: Chris@909: # Chris@909: # Hook Script API base methods Chris@909: # Chris@909: Chris@909: def srcdir_root Chris@909: @srcdir Chris@909: end Chris@909: Chris@909: def objdir_root Chris@909: @objdir Chris@909: end Chris@909: Chris@909: def relpath Chris@909: @currdir Chris@909: end Chris@909: Chris@909: # Chris@909: # Config Access Chris@909: # Chris@909: Chris@909: # module FileOperations requires this Chris@909: def verbose? Chris@909: @config.verbose? Chris@909: end Chris@909: Chris@909: # module FileOperations requires this Chris@909: def no_harm? Chris@909: @config.no_harm? Chris@909: end Chris@909: Chris@909: def verbose_off Chris@909: begin Chris@909: save, @config.verbose = @config.verbose?, false Chris@909: yield Chris@909: ensure Chris@909: @config.verbose = save Chris@909: end Chris@909: end Chris@909: Chris@909: # Chris@909: # TASK config Chris@909: # Chris@909: Chris@909: def exec_config Chris@909: exec_task_traverse 'config' Chris@909: end Chris@909: Chris@909: alias config_dir_bin noop Chris@909: alias config_dir_lib noop Chris@909: Chris@909: def config_dir_ext(rel) Chris@909: extconf if extdir?(curr_srcdir()) Chris@909: end Chris@909: Chris@909: alias config_dir_data noop Chris@909: alias config_dir_conf noop Chris@909: alias config_dir_man noop Chris@909: Chris@909: def extconf Chris@909: ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt Chris@909: end Chris@909: Chris@909: # Chris@909: # TASK setup Chris@909: # Chris@909: Chris@909: def exec_setup Chris@909: exec_task_traverse 'setup' Chris@909: end Chris@909: Chris@909: def setup_dir_bin(rel) Chris@909: files_of(curr_srcdir()).each do |fname| Chris@909: update_shebang_line "#{curr_srcdir()}/#{fname}" Chris@909: end Chris@909: end Chris@909: Chris@909: alias setup_dir_lib noop Chris@909: Chris@909: def setup_dir_ext(rel) Chris@909: make if extdir?(curr_srcdir()) Chris@909: end Chris@909: Chris@909: alias setup_dir_data noop Chris@909: alias setup_dir_conf noop Chris@909: alias setup_dir_man noop Chris@909: Chris@909: def update_shebang_line(path) Chris@909: return if no_harm? Chris@909: return if config('shebang') == 'never' Chris@909: old = Shebang.load(path) Chris@909: if old Chris@909: $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 Chris@909: new = new_shebang(old) Chris@909: return if new.to_s == old.to_s Chris@909: else Chris@909: return unless config('shebang') == 'all' Chris@909: new = Shebang.new(config('rubypath')) Chris@909: end Chris@909: $stderr.puts "updating shebang: #{File.basename(path)}" if verbose? Chris@909: open_atomic_writer(path) {|output| Chris@909: File.open(path, 'rb') {|f| Chris@909: f.gets if old # discard Chris@909: output.puts new.to_s Chris@909: output.print f.read Chris@909: } Chris@909: } Chris@909: end Chris@909: Chris@909: def new_shebang(old) Chris@909: if /\Aruby/ =~ File.basename(old.cmd) Chris@909: Shebang.new(config('rubypath'), old.args) Chris@909: elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby' Chris@909: Shebang.new(config('rubypath'), old.args[1..-1]) Chris@909: else Chris@909: return old unless config('shebang') == 'all' Chris@909: Shebang.new(config('rubypath')) Chris@909: end Chris@909: end Chris@909: Chris@909: def open_atomic_writer(path, &block) Chris@909: tmpfile = File.basename(path) + '.tmp' Chris@909: begin Chris@909: File.open(tmpfile, 'wb', &block) Chris@909: File.rename tmpfile, File.basename(path) Chris@909: ensure Chris@909: File.unlink tmpfile if File.exist?(tmpfile) Chris@909: end Chris@909: end Chris@909: Chris@909: class Shebang Chris@909: def Shebang.load(path) Chris@909: line = nil Chris@909: File.open(path) {|f| Chris@909: line = f.gets Chris@909: } Chris@909: return nil unless /\A#!/ =~ line Chris@909: parse(line) Chris@909: end Chris@909: Chris@909: def Shebang.parse(line) Chris@909: cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ') Chris@909: new(cmd, args) Chris@909: end Chris@909: Chris@909: def initialize(cmd, args = []) Chris@909: @cmd = cmd Chris@909: @args = args Chris@909: end Chris@909: Chris@909: attr_reader :cmd Chris@909: attr_reader :args Chris@909: Chris@909: def to_s Chris@909: "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}") Chris@909: end Chris@909: end Chris@909: Chris@909: # Chris@909: # TASK install Chris@909: # Chris@909: Chris@909: def exec_install Chris@909: rm_f 'InstalledFiles' Chris@909: exec_task_traverse 'install' Chris@909: end Chris@909: Chris@909: def install_dir_bin(rel) Chris@909: install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755 Chris@909: end Chris@909: Chris@909: def install_dir_lib(rel) Chris@909: install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644 Chris@909: end Chris@909: Chris@909: def install_dir_ext(rel) Chris@909: return unless extdir?(curr_srcdir()) Chris@909: install_files rubyextentions('.'), Chris@909: "#{config('sodir')}/#{File.dirname(rel)}", Chris@909: 0555 Chris@909: end Chris@909: Chris@909: def install_dir_data(rel) Chris@909: install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644 Chris@909: end Chris@909: Chris@909: def install_dir_conf(rel) Chris@909: # FIXME: should not remove current config files Chris@909: # (rename previous file to .old/.org) Chris@909: install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644 Chris@909: end Chris@909: Chris@909: def install_dir_man(rel) Chris@909: install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644 Chris@909: end Chris@909: Chris@909: def install_files(list, dest, mode) Chris@909: mkdir_p dest, @config.install_prefix Chris@909: list.each do |fname| Chris@909: install fname, dest, mode, @config.install_prefix Chris@909: end Chris@909: end Chris@909: Chris@909: def libfiles Chris@909: glob_reject(%w(*.y *.output), targetfiles()) Chris@909: end Chris@909: Chris@909: def rubyextentions(dir) Chris@909: ents = glob_select("*.#{@config.dllext}", targetfiles()) Chris@909: if ents.empty? Chris@909: setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first" Chris@909: end Chris@909: ents Chris@909: end Chris@909: Chris@909: def targetfiles Chris@909: mapdir(existfiles() - hookfiles()) Chris@909: end Chris@909: Chris@909: def mapdir(ents) Chris@909: ents.map {|ent| Chris@909: if File.exist?(ent) Chris@909: then ent # objdir Chris@909: else "#{curr_srcdir()}/#{ent}" # srcdir Chris@909: end Chris@909: } Chris@909: end Chris@909: Chris@909: # picked up many entries from cvs-1.11.1/src/ignore.c Chris@909: JUNK_FILES = %w( Chris@909: core RCSLOG tags TAGS .make.state Chris@909: .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb Chris@909: *~ *.old *.bak *.BAK *.orig *.rej _$* *$ Chris@909: Chris@909: *.org *.in .* Chris@909: ) Chris@909: Chris@909: def existfiles Chris@909: glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.'))) Chris@909: end Chris@909: Chris@909: def hookfiles Chris@909: %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt| Chris@909: %w( config setup install clean ).map {|t| sprintf(fmt, t) } Chris@909: }.flatten Chris@909: end Chris@909: Chris@909: def glob_select(pat, ents) Chris@909: re = globs2re([pat]) Chris@909: ents.select {|ent| re =~ ent } Chris@909: end Chris@909: Chris@909: def glob_reject(pats, ents) Chris@909: re = globs2re(pats) Chris@909: ents.reject {|ent| re =~ ent } Chris@909: end Chris@909: Chris@909: GLOB2REGEX = { Chris@909: '.' => '\.', Chris@909: '$' => '\$', Chris@909: '#' => '\#', Chris@909: '*' => '.*' Chris@909: } Chris@909: Chris@909: def globs2re(pats) Chris@909: /\A(?:#{ Chris@909: pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|') Chris@909: })\z/ Chris@909: end Chris@909: Chris@909: # Chris@909: # TASK test Chris@909: # Chris@909: Chris@909: TESTDIR = 'test' Chris@909: Chris@909: def exec_test Chris@909: unless File.directory?('test') Chris@909: $stderr.puts 'no test in this package' if verbose? Chris@909: return Chris@909: end Chris@909: $stderr.puts 'Running tests...' if verbose? Chris@909: begin Chris@909: require 'test/unit' Chris@909: rescue LoadError Chris@909: setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.' Chris@909: end Chris@909: runner = Test::Unit::AutoRunner.new(true) Chris@909: runner.to_run << TESTDIR Chris@909: runner.run Chris@909: end Chris@909: Chris@909: # Chris@909: # TASK clean Chris@909: # Chris@909: Chris@909: def exec_clean Chris@909: exec_task_traverse 'clean' Chris@909: rm_f @config.savefile Chris@909: rm_f 'InstalledFiles' Chris@909: end Chris@909: Chris@909: alias clean_dir_bin noop Chris@909: alias clean_dir_lib noop Chris@909: alias clean_dir_data noop Chris@909: alias clean_dir_conf noop Chris@909: alias clean_dir_man noop Chris@909: Chris@909: def clean_dir_ext(rel) Chris@909: return unless extdir?(curr_srcdir()) Chris@909: make 'clean' if File.file?('Makefile') Chris@909: end Chris@909: Chris@909: # Chris@909: # TASK distclean Chris@909: # Chris@909: Chris@909: def exec_distclean Chris@909: exec_task_traverse 'distclean' Chris@909: rm_f @config.savefile Chris@909: rm_f 'InstalledFiles' Chris@909: end Chris@909: Chris@909: alias distclean_dir_bin noop Chris@909: alias distclean_dir_lib noop Chris@909: Chris@909: def distclean_dir_ext(rel) Chris@909: return unless extdir?(curr_srcdir()) Chris@909: make 'distclean' if File.file?('Makefile') Chris@909: end Chris@909: Chris@909: alias distclean_dir_data noop Chris@909: alias distclean_dir_conf noop Chris@909: alias distclean_dir_man noop Chris@909: Chris@909: # Chris@909: # Traversing Chris@909: # Chris@909: Chris@909: def exec_task_traverse(task) Chris@909: run_hook "pre-#{task}" Chris@909: FILETYPES.each do |type| Chris@909: if type == 'ext' and config('without-ext') == 'yes' Chris@909: $stderr.puts 'skipping ext/* by user option' if verbose? Chris@909: next Chris@909: end Chris@909: traverse task, type, "#{task}_dir_#{type}" Chris@909: end Chris@909: run_hook "post-#{task}" Chris@909: end Chris@909: Chris@909: def traverse(task, rel, mid) Chris@909: dive_into(rel) { Chris@909: run_hook "pre-#{task}" Chris@909: __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '') Chris@909: directories_of(curr_srcdir()).each do |d| Chris@909: traverse task, "#{rel}/#{d}", mid Chris@909: end Chris@909: run_hook "post-#{task}" Chris@909: } Chris@909: end Chris@909: Chris@909: def dive_into(rel) Chris@909: return unless File.dir?("#{@srcdir}/#{rel}") Chris@909: Chris@909: dir = File.basename(rel) Chris@909: Dir.mkdir dir unless File.dir?(dir) Chris@909: prevdir = Dir.pwd Chris@909: Dir.chdir dir Chris@909: $stderr.puts '---> ' + rel if verbose? Chris@909: @currdir = rel Chris@909: yield Chris@909: Dir.chdir prevdir Chris@909: $stderr.puts '<--- ' + rel if verbose? Chris@909: @currdir = File.dirname(rel) Chris@909: end Chris@909: Chris@909: def run_hook(id) Chris@909: path = [ "#{curr_srcdir()}/#{id}", Chris@909: "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) } Chris@909: return unless path Chris@909: begin Chris@909: instance_eval File.read(path), path, 1 Chris@909: rescue Chris@909: raise if $DEBUG Chris@909: setup_rb_error "hook #{path} failed:\n" + $!.message Chris@909: end Chris@909: end Chris@909: Chris@909: end # class Installer Chris@909: Chris@909: Chris@909: class SetupError < StandardError; end Chris@909: Chris@909: def setup_rb_error(msg) Chris@909: raise SetupError, msg Chris@909: end Chris@909: Chris@909: if $0 == __FILE__ Chris@909: begin Chris@909: ToplevelInstaller.invoke Chris@909: rescue SetupError Chris@909: raise if $DEBUG Chris@909: $stderr.puts $!.message Chris@909: $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." Chris@909: exit 1 Chris@909: end Chris@909: end