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