1.1 --- a/README Sat May 30 05:20:36 2009 -0400
1.2 +++ b/README Sat May 30 05:34:21 2009 -0400
1.3 @@ -54,8 +54,14 @@
1.4 # install wirble via RubyGems
1.5 sudo gem install wirble
1.6
1.7 -If you don't have RubyGems, you can also install the Wirble library
1.8 -this:
1.9 +If you don't have RubyGems, you can also install the Wirble using
1.10 +setup.rb:
1.11 +
1.12 + # install wirble using setup.rb
1.13 + ruby ./setup.rb config && ruby ./setup.rb setup
1.14 + sudo ruby ./setup.rb install
1.15 +
1.16 +Or, install Wirble by hand using sudo:
1.17
1.18 # install wirble to library directory
1.19 sudo cp -v wirble.rb $(ruby -e 'puts $LOAD_PATH[0]')
1.20 @@ -80,19 +86,25 @@
1.21
1.22 Using Wirble
1.23 ============
1.24 +A sample ~/.irbrc is available in the file "_irbrc". If you just want
1.25 +to get Wirble up quickly, copy that to your home directory and be done
1.26 +with it. Otherwise. read on:
1.27 +
1.28 Using Wirble is easy: just add the following lines to your ~/.irbrc
1.29 file:
1.30
1.31 - # load rubygems and wirble
1.32 - require 'rubygems' rescue nil
1.33 - require 'wirble'
1.34 + begin
1.35 + require 'wirble'
1.36
1.37 - # load wirble
1.38 - Wirble.init
1.39 + # init wirble
1.40 + Wirble.init
1.41 + rescue LoadError => err
1.42 + $stderr.puts "Couldn't load Wirble: #{err}"
1.43 + end
1.44
1.45 A lot of people really don't like colors, so I've kept the color
1.46 disabled by default. To enable it, add the following bit to your
1.47 -~/.irbrc file after "Wirble.init":
1.48 +~/.irbrc file immediately after the call to "Wirble.init":
1.49
1.50 # enable color
1.51 Wirble.colorize
1.52 @@ -100,8 +112,15 @@
1.53 If you want to terrify your grandmother and impress your buddies, your
1.54 entire ~/.irbrc can also be written like so:
1.55
1.56 - %w{rubygems wirble}.each { |lib| require lib rescue nil }
1.57 - %w{init colorize}.each { |str| Wirble.send(str.intern) }
1.58 + %w{rubygems wirble}.each do |lib|
1.59 + begin
1.60 + require lib
1.61 + rescue LoadError => err
1.62 + $stderr.puts "Couldn't load #{lib}: #{err}"
1.63 + end
1.64 + end
1.65 +
1.66 + %w{init colorize}.each { |str| Wirble.send(str) }
1.67
1.68 Configuring Wirble
1.69 ==================
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/_irbrc Sat May 30 05:34:21 2009 -0400
2.3 @@ -0,0 +1,27 @@
2.4 +######################################################################
2.5 +# _irbrc - Sample .irbrc file to enable Wirble. #
2.6 +######################################################################
2.7 +
2.8 +#
2.9 +# Uncomment the block below if you want to load RubyGems in Irb.
2.10 +#
2.11 +
2.12 +# begin
2.13 +# require 'rubygems'
2.14 +# rescue LoadError => err
2.15 +# warn "Couldn't load RubyGems: #{err}"
2.16 +# end
2.17 +
2.18 +begin
2.19 + # load and initialize wirble
2.20 + require 'wirble'
2.21 + Wirble.init
2.22 +
2.23 + #
2.24 + # Uncomment the line below to enable Wirble colors.
2.25 + #
2.26 +
2.27 + # Wirble.colorize
2.28 +rescue LoadError => err
2.29 + warn "Couldn't load Wirble: #{err}"
2.30 +end
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/setup.rb Sat May 30 05:34:21 2009 -0400
3.3 @@ -0,0 +1,1596 @@
3.4 +#
3.5 +# setup.rb
3.6 +#
3.7 +# Copyright (c) 2000-2006 Minero Aoki
3.8 +#
3.9 +# This program is free software.
3.10 +# You can distribute/modify this program under the terms of
3.11 +# the GNU LGPL, Lesser General Public License version 2.1.
3.12 +#
3.13 +
3.14 +unless Enumerable.method_defined?(:map) # Ruby 1.4.6
3.15 + module Enumerable
3.16 + alias map collect
3.17 + end
3.18 +end
3.19 +
3.20 +unless File.respond_to?(:read) # Ruby 1.6
3.21 + def File.read(fname)
3.22 + open(fname) {|f|
3.23 + return f.read
3.24 + }
3.25 + end
3.26 +end
3.27 +
3.28 +unless Errno.const_defined?(:ENOTEMPTY) # Windows?
3.29 + module Errno
3.30 + class ENOTEMPTY
3.31 + # We do not raise this exception, implementation is not needed.
3.32 + end
3.33 + end
3.34 +end
3.35 +
3.36 +def File.binread(fname)
3.37 + open(fname, 'rb') {|f|
3.38 + return f.read
3.39 + }
3.40 +end
3.41 +
3.42 +# for corrupted Windows' stat(2)
3.43 +def File.dir?(path)
3.44 + File.directory?((path[-1,1] == '/') ? path : path + '/')
3.45 +end
3.46 +
3.47 +
3.48 +class ConfigTable
3.49 +
3.50 + include Enumerable
3.51 +
3.52 + def initialize(rbconfig)
3.53 + @rbconfig = rbconfig
3.54 + @items = []
3.55 + @table = {}
3.56 + # options
3.57 + @install_prefix = nil
3.58 + @config_opt = nil
3.59 + @verbose = true
3.60 + @no_harm = false
3.61 + end
3.62 +
3.63 + attr_accessor :install_prefix
3.64 + attr_accessor :config_opt
3.65 +
3.66 + attr_writer :verbose
3.67 +
3.68 + def verbose?
3.69 + @verbose
3.70 + end
3.71 +
3.72 + attr_writer :no_harm
3.73 +
3.74 + def no_harm?
3.75 + @no_harm
3.76 + end
3.77 +
3.78 + def [](key)
3.79 + lookup(key).resolve(self)
3.80 + end
3.81 +
3.82 + def []=(key, val)
3.83 + lookup(key).set val
3.84 + end
3.85 +
3.86 + def names
3.87 + @items.map {|i| i.name }
3.88 + end
3.89 +
3.90 + def each(&block)
3.91 + @items.each(&block)
3.92 + end
3.93 +
3.94 + def key?(name)
3.95 + @table.key?(name)
3.96 + end
3.97 +
3.98 + def lookup(name)
3.99 + @table[name] or setup_rb_error "no such config item: #{name}"
3.100 + end
3.101 +
3.102 + def add(item)
3.103 + @items.push item
3.104 + @table[item.name] = item
3.105 + end
3.106 +
3.107 + def remove(name)
3.108 + item = lookup(name)
3.109 + @items.delete_if {|i| i.name == name }
3.110 + @table.delete_if {|name, i| i.name == name }
3.111 + item
3.112 + end
3.113 +
3.114 + def load_script(path, inst = nil)
3.115 + if File.file?(path)
3.116 + MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
3.117 + end
3.118 + end
3.119 +
3.120 + def savefile
3.121 + '.config'
3.122 + end
3.123 +
3.124 + def load_savefile
3.125 + begin
3.126 + File.foreach(savefile()) do |line|
3.127 + k, v = *line.split(/=/, 2)
3.128 + self[k] = v.strip
3.129 + end
3.130 + rescue Errno::ENOENT
3.131 + setup_rb_error $!.message + "\n#{File.basename($0)} config first"
3.132 + end
3.133 + end
3.134 +
3.135 + def save
3.136 + @items.each {|i| i.value }
3.137 + File.open(savefile(), 'w') {|f|
3.138 + @items.each do |i|
3.139 + f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
3.140 + end
3.141 + }
3.142 + end
3.143 +
3.144 + def load_standard_entries
3.145 + standard_entries(@rbconfig).each do |ent|
3.146 + add ent
3.147 + end
3.148 + end
3.149 +
3.150 + def standard_entries(rbconfig)
3.151 + c = rbconfig
3.152 +
3.153 + rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
3.154 +
3.155 + major = c['MAJOR'].to_i
3.156 + minor = c['MINOR'].to_i
3.157 + teeny = c['TEENY'].to_i
3.158 + version = "#{major}.#{minor}"
3.159 +
3.160 + # ruby ver. >= 1.4.4?
3.161 + newpath_p = ((major >= 2) or
3.162 + ((major == 1) and
3.163 + ((minor >= 5) or
3.164 + ((minor == 4) and (teeny >= 4)))))
3.165 +
3.166 + if c['rubylibdir']
3.167 + # V > 1.6.3
3.168 + libruby = "#{c['prefix']}/lib/ruby"
3.169 + librubyver = c['rubylibdir']
3.170 + librubyverarch = c['archdir']
3.171 + siteruby = c['sitedir']
3.172 + siterubyver = c['sitelibdir']
3.173 + siterubyverarch = c['sitearchdir']
3.174 + elsif newpath_p
3.175 + # 1.4.4 <= V <= 1.6.3
3.176 + libruby = "#{c['prefix']}/lib/ruby"
3.177 + librubyver = "#{c['prefix']}/lib/ruby/#{version}"
3.178 + librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
3.179 + siteruby = c['sitedir']
3.180 + siterubyver = "$siteruby/#{version}"
3.181 + siterubyverarch = "$siterubyver/#{c['arch']}"
3.182 + else
3.183 + # V < 1.4.4
3.184 + libruby = "#{c['prefix']}/lib/ruby"
3.185 + librubyver = "#{c['prefix']}/lib/ruby/#{version}"
3.186 + librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
3.187 + siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
3.188 + siterubyver = siteruby
3.189 + siterubyverarch = "$siterubyver/#{c['arch']}"
3.190 + end
3.191 + parameterize = lambda {|path|
3.192 + path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
3.193 + }
3.194 +
3.195 + if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
3.196 + makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
3.197 + else
3.198 + makeprog = 'make'
3.199 + end
3.200 +
3.201 + [
3.202 + ExecItem.new('installdirs', 'std/site/home',
3.203 + 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
3.204 + {|val, table|
3.205 + case val
3.206 + when 'std'
3.207 + table['rbdir'] = '$librubyver'
3.208 + table['sodir'] = '$librubyverarch'
3.209 + when 'site'
3.210 + table['rbdir'] = '$siterubyver'
3.211 + table['sodir'] = '$siterubyverarch'
3.212 + when 'home'
3.213 + setup_rb_error '$HOME was not set' unless ENV['HOME']
3.214 + table['prefix'] = ENV['HOME']
3.215 + table['rbdir'] = '$libdir/ruby'
3.216 + table['sodir'] = '$libdir/ruby'
3.217 + end
3.218 + },
3.219 + PathItem.new('prefix', 'path', c['prefix'],
3.220 + 'path prefix of target environment'),
3.221 + PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
3.222 + 'the directory for commands'),
3.223 + PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
3.224 + 'the directory for libraries'),
3.225 + PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
3.226 + 'the directory for shared data'),
3.227 + PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
3.228 + 'the directory for man pages'),
3.229 + PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
3.230 + 'the directory for system configuration files'),
3.231 + PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
3.232 + 'the directory for local state data'),
3.233 + PathItem.new('libruby', 'path', libruby,
3.234 + 'the directory for ruby libraries'),
3.235 + PathItem.new('librubyver', 'path', librubyver,
3.236 + 'the directory for standard ruby libraries'),
3.237 + PathItem.new('librubyverarch', 'path', librubyverarch,
3.238 + 'the directory for standard ruby extensions'),
3.239 + PathItem.new('siteruby', 'path', siteruby,
3.240 + 'the directory for version-independent aux ruby libraries'),
3.241 + PathItem.new('siterubyver', 'path', siterubyver,
3.242 + 'the directory for aux ruby libraries'),
3.243 + PathItem.new('siterubyverarch', 'path', siterubyverarch,
3.244 + 'the directory for aux ruby binaries'),
3.245 + PathItem.new('rbdir', 'path', '$siterubyver',
3.246 + 'the directory for ruby scripts'),
3.247 + PathItem.new('sodir', 'path', '$siterubyverarch',
3.248 + 'the directory for ruby extentions'),
3.249 + PathItem.new('rubypath', 'path', rubypath,
3.250 + 'the path to set to #! line'),
3.251 + ProgramItem.new('rubyprog', 'name', rubypath,
3.252 + 'the ruby program using for installation'),
3.253 + ProgramItem.new('makeprog', 'name', makeprog,
3.254 + 'the make program to compile ruby extentions'),
3.255 + SelectItem.new('shebang', 'all/ruby/never', 'ruby',
3.256 + 'shebang line (#!) editing mode'),
3.257 + BoolItem.new('without-ext', 'yes/no', 'no',
3.258 + 'does not compile/install ruby extentions')
3.259 + ]
3.260 + end
3.261 + private :standard_entries
3.262 +
3.263 + def load_multipackage_entries
3.264 + multipackage_entries().each do |ent|
3.265 + add ent
3.266 + end
3.267 + end
3.268 +
3.269 + def multipackage_entries
3.270 + [
3.271 + PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
3.272 + 'package names that you want to install'),
3.273 + PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
3.274 + 'package names that you do not want to install')
3.275 + ]
3.276 + end
3.277 + private :multipackage_entries
3.278 +
3.279 + ALIASES = {
3.280 + 'std-ruby' => 'librubyver',
3.281 + 'stdruby' => 'librubyver',
3.282 + 'rubylibdir' => 'librubyver',
3.283 + 'archdir' => 'librubyverarch',
3.284 + 'site-ruby-common' => 'siteruby', # For backward compatibility
3.285 + 'site-ruby' => 'siterubyver', # For backward compatibility
3.286 + 'bin-dir' => 'bindir',
3.287 + 'bin-dir' => 'bindir',
3.288 + 'rb-dir' => 'rbdir',
3.289 + 'so-dir' => 'sodir',
3.290 + 'data-dir' => 'datadir',
3.291 + 'ruby-path' => 'rubypath',
3.292 + 'ruby-prog' => 'rubyprog',
3.293 + 'ruby' => 'rubyprog',
3.294 + 'make-prog' => 'makeprog',
3.295 + 'make' => 'makeprog'
3.296 + }
3.297 +
3.298 + def fixup
3.299 + ALIASES.each do |ali, name|
3.300 + @table[ali] = @table[name]
3.301 + end
3.302 + end
3.303 +
3.304 + def options_re
3.305 + /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
3.306 + end
3.307 +
3.308 + def parse_opt(opt)
3.309 + m = options_re().match(opt) or setup_rb_error "config: unknown option #{opt}"
3.310 + m.to_a[1,2]
3.311 + end
3.312 +
3.313 + def dllext
3.314 + @rbconfig['DLEXT']
3.315 + end
3.316 +
3.317 + def value_config?(name)
3.318 + lookup(name).value?
3.319 + end
3.320 +
3.321 + class Item
3.322 + def initialize(name, template, default, desc)
3.323 + @name = name.freeze
3.324 + @template = template
3.325 + @value = default
3.326 + @default = default
3.327 + @description = desc
3.328 + end
3.329 +
3.330 + attr_reader :name
3.331 + attr_reader :description
3.332 +
3.333 + attr_accessor :default
3.334 + alias help_default default
3.335 +
3.336 + def help_opt
3.337 + "--#{@name}=#{@template}"
3.338 + end
3.339 +
3.340 + def value?
3.341 + true
3.342 + end
3.343 +
3.344 + def value
3.345 + @value
3.346 + end
3.347 +
3.348 + def resolve(table)
3.349 + @value.gsub(%r<\$([^/]+)>) { table[$1] }
3.350 + end
3.351 +
3.352 + def set(val)
3.353 + @value = check(val)
3.354 + end
3.355 +
3.356 + private
3.357 +
3.358 + def check(val)
3.359 + setup_rb_error "config: --#{name} requires argument" unless val
3.360 + val
3.361 + end
3.362 + end
3.363 +
3.364 + class BoolItem < Item
3.365 + def config_type
3.366 + 'bool'
3.367 + end
3.368 +
3.369 + def help_opt
3.370 + "--#{@name}"
3.371 + end
3.372 +
3.373 + private
3.374 +
3.375 + def check(val)
3.376 + return 'yes' unless val
3.377 + case val
3.378 + when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
3.379 + when /\An(o)?\z/i, /\Af(alse)\z/i then 'no'
3.380 + else
3.381 + setup_rb_error "config: --#{@name} accepts only yes/no for argument"
3.382 + end
3.383 + end
3.384 + end
3.385 +
3.386 + class PathItem < Item
3.387 + def config_type
3.388 + 'path'
3.389 + end
3.390 +
3.391 + private
3.392 +
3.393 + def check(path)
3.394 + setup_rb_error "config: --#{@name} requires argument" unless path
3.395 + path[0,1] == '$' ? path : File.expand_path(path)
3.396 + end
3.397 + end
3.398 +
3.399 + class ProgramItem < Item
3.400 + def config_type
3.401 + 'program'
3.402 + end
3.403 + end
3.404 +
3.405 + class SelectItem < Item
3.406 + def initialize(name, selection, default, desc)
3.407 + super
3.408 + @ok = selection.split('/')
3.409 + end
3.410 +
3.411 + def config_type
3.412 + 'select'
3.413 + end
3.414 +
3.415 + private
3.416 +
3.417 + def check(val)
3.418 + unless @ok.include?(val.strip)
3.419 + setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
3.420 + end
3.421 + val.strip
3.422 + end
3.423 + end
3.424 +
3.425 + class ExecItem < Item
3.426 + def initialize(name, selection, desc, &block)
3.427 + super name, selection, nil, desc
3.428 + @ok = selection.split('/')
3.429 + @action = block
3.430 + end
3.431 +
3.432 + def config_type
3.433 + 'exec'
3.434 + end
3.435 +
3.436 + def value?
3.437 + false
3.438 + end
3.439 +
3.440 + def resolve(table)
3.441 + setup_rb_error "$#{name()} wrongly used as option value"
3.442 + end
3.443 +
3.444 + undef set
3.445 +
3.446 + def evaluate(val, table)
3.447 + v = val.strip.downcase
3.448 + unless @ok.include?(v)
3.449 + setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
3.450 + end
3.451 + @action.call v, table
3.452 + end
3.453 + end
3.454 +
3.455 + class PackageSelectionItem < Item
3.456 + def initialize(name, template, default, help_default, desc)
3.457 + super name, template, default, desc
3.458 + @help_default = help_default
3.459 + end
3.460 +
3.461 + attr_reader :help_default
3.462 +
3.463 + def config_type
3.464 + 'package'
3.465 + end
3.466 +
3.467 + private
3.468 +
3.469 + def check(val)
3.470 + unless File.dir?("packages/#{val}")
3.471 + setup_rb_error "config: no such package: #{val}"
3.472 + end
3.473 + val
3.474 + end
3.475 + end
3.476 +
3.477 + class MetaConfigEnvironment
3.478 + def initialize(config, installer)
3.479 + @config = config
3.480 + @installer = installer
3.481 + end
3.482 +
3.483 + def config_names
3.484 + @config.names
3.485 + end
3.486 +
3.487 + def config?(name)
3.488 + @config.key?(name)
3.489 + end
3.490 +
3.491 + def bool_config?(name)
3.492 + @config.lookup(name).config_type == 'bool'
3.493 + end
3.494 +
3.495 + def path_config?(name)
3.496 + @config.lookup(name).config_type == 'path'
3.497 + end
3.498 +
3.499 + def value_config?(name)
3.500 + @config.lookup(name).config_type != 'exec'
3.501 + end
3.502 +
3.503 + def add_config(item)
3.504 + @config.add item
3.505 + end
3.506 +
3.507 + def add_bool_config(name, default, desc)
3.508 + @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
3.509 + end
3.510 +
3.511 + def add_path_config(name, default, desc)
3.512 + @config.add PathItem.new(name, 'path', default, desc)
3.513 + end
3.514 +
3.515 + def set_config_default(name, default)
3.516 + @config.lookup(name).default = default
3.517 + end
3.518 +
3.519 + def remove_config(name)
3.520 + @config.remove(name)
3.521 + end
3.522 +
3.523 + # For only multipackage
3.524 + def packages
3.525 + raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
3.526 + @installer.packages
3.527 + end
3.528 +
3.529 + # For only multipackage
3.530 + def declare_packages(list)
3.531 + raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
3.532 + @installer.packages = list
3.533 + end
3.534 + end
3.535 +
3.536 +end # class ConfigTable
3.537 +
3.538 +
3.539 +# This module requires: #verbose?, #no_harm?
3.540 +module FileOperations
3.541 +
3.542 + def mkdir_p(dirname, prefix = nil)
3.543 + dirname = prefix + File.expand_path(dirname) if prefix
3.544 + $stderr.puts "mkdir -p #{dirname}" if verbose?
3.545 + return if no_harm?
3.546 +
3.547 + # Does not check '/', it's too abnormal.
3.548 + dirs = File.expand_path(dirname).split(%r<(?=/)>)
3.549 + if /\A[a-z]:\z/i =~ dirs[0]
3.550 + disk = dirs.shift
3.551 + dirs[0] = disk + dirs[0]
3.552 + end
3.553 + dirs.each_index do |idx|
3.554 + path = dirs[0..idx].join('')
3.555 + Dir.mkdir path unless File.dir?(path)
3.556 + end
3.557 + end
3.558 +
3.559 + def rm_f(path)
3.560 + $stderr.puts "rm -f #{path}" if verbose?
3.561 + return if no_harm?
3.562 + force_remove_file path
3.563 + end
3.564 +
3.565 + def rm_rf(path)
3.566 + $stderr.puts "rm -rf #{path}" if verbose?
3.567 + return if no_harm?
3.568 + remove_tree path
3.569 + end
3.570 +
3.571 + def remove_tree(path)
3.572 + if File.symlink?(path)
3.573 + remove_file path
3.574 + elsif File.dir?(path)
3.575 + remove_tree0 path
3.576 + else
3.577 + force_remove_file path
3.578 + end
3.579 + end
3.580 +
3.581 + def remove_tree0(path)
3.582 + Dir.foreach(path) do |ent|
3.583 + next if ent == '.'
3.584 + next if ent == '..'
3.585 + entpath = "#{path}/#{ent}"
3.586 + if File.symlink?(entpath)
3.587 + remove_file entpath
3.588 + elsif File.dir?(entpath)
3.589 + remove_tree0 entpath
3.590 + else
3.591 + force_remove_file entpath
3.592 + end
3.593 + end
3.594 + begin
3.595 + Dir.rmdir path
3.596 + rescue Errno::ENOTEMPTY
3.597 + # directory may not be empty
3.598 + end
3.599 + end
3.600 +
3.601 + def move_file(src, dest)
3.602 + force_remove_file dest
3.603 + begin
3.604 + File.rename src, dest
3.605 + rescue
3.606 + File.open(dest, 'wb') {|f|
3.607 + f.write File.binread(src)
3.608 + }
3.609 + File.chmod File.stat(src).mode, dest
3.610 + File.unlink src
3.611 + end
3.612 + end
3.613 +
3.614 + def force_remove_file(path)
3.615 + begin
3.616 + remove_file path
3.617 + rescue
3.618 + end
3.619 + end
3.620 +
3.621 + def remove_file(path)
3.622 + File.chmod 0777, path
3.623 + File.unlink path
3.624 + end
3.625 +
3.626 + def install(from, dest, mode, prefix = nil)
3.627 + $stderr.puts "install #{from} #{dest}" if verbose?
3.628 + return if no_harm?
3.629 +
3.630 + realdest = prefix ? prefix + File.expand_path(dest) : dest
3.631 + realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
3.632 + str = File.binread(from)
3.633 + if diff?(str, realdest)
3.634 + verbose_off {
3.635 + rm_f realdest if File.exist?(realdest)
3.636 + }
3.637 + File.open(realdest, 'wb') {|f|
3.638 + f.write str
3.639 + }
3.640 + File.chmod mode, realdest
3.641 +
3.642 + File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
3.643 + if prefix
3.644 + f.puts realdest.sub(prefix, '')
3.645 + else
3.646 + f.puts realdest
3.647 + end
3.648 + }
3.649 + end
3.650 + end
3.651 +
3.652 + def diff?(new_content, path)
3.653 + return true unless File.exist?(path)
3.654 + new_content != File.binread(path)
3.655 + end
3.656 +
3.657 + def command(*args)
3.658 + $stderr.puts args.join(' ') if verbose?
3.659 + system(*args) or raise RuntimeError,
3.660 + "system(#{args.map{|a| a.inspect }.join(' ')}) failed"
3.661 + end
3.662 +
3.663 + def ruby(*args)
3.664 + command config('rubyprog'), *args
3.665 + end
3.666 +
3.667 + def make(task = nil)
3.668 + command(*[config('makeprog'), task].compact)
3.669 + end
3.670 +
3.671 + def extdir?(dir)
3.672 + File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
3.673 + end
3.674 +
3.675 + def files_of(dir)
3.676 + Dir.open(dir) {|d|
3.677 + return d.select {|ent| File.file?("#{dir}/#{ent}") }
3.678 + }
3.679 + end
3.680 +
3.681 + DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
3.682 +
3.683 + def directories_of(dir)
3.684 + Dir.open(dir) {|d|
3.685 + return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
3.686 + }
3.687 + end
3.688 +
3.689 +end
3.690 +
3.691 +
3.692 +# This module requires: #srcdir_root, #objdir_root, #relpath
3.693 +module HookScriptAPI
3.694 +
3.695 + def get_config(key)
3.696 + @config[key]
3.697 + end
3.698 +
3.699 + alias config get_config
3.700 +
3.701 + # obsolete: use metaconfig to change configuration
3.702 + def set_config(key, val)
3.703 + @config[key] = val
3.704 + end
3.705 +
3.706 + #
3.707 + # srcdir/objdir (works only in the package directory)
3.708 + #
3.709 +
3.710 + def curr_srcdir
3.711 + "#{srcdir_root()}/#{relpath()}"
3.712 + end
3.713 +
3.714 + def curr_objdir
3.715 + "#{objdir_root()}/#{relpath()}"
3.716 + end
3.717 +
3.718 + def srcfile(path)
3.719 + "#{curr_srcdir()}/#{path}"
3.720 + end
3.721 +
3.722 + def srcexist?(path)
3.723 + File.exist?(srcfile(path))
3.724 + end
3.725 +
3.726 + def srcdirectory?(path)
3.727 + File.dir?(srcfile(path))
3.728 + end
3.729 +
3.730 + def srcfile?(path)
3.731 + File.file?(srcfile(path))
3.732 + end
3.733 +
3.734 + def srcentries(path = '.')
3.735 + Dir.open("#{curr_srcdir()}/#{path}") {|d|
3.736 + return d.to_a - %w(. ..)
3.737 + }
3.738 + end
3.739 +
3.740 + def srcfiles(path = '.')
3.741 + srcentries(path).select {|fname|
3.742 + File.file?(File.join(curr_srcdir(), path, fname))
3.743 + }
3.744 + end
3.745 +
3.746 + def srcdirectories(path = '.')
3.747 + srcentries(path).select {|fname|
3.748 + File.dir?(File.join(curr_srcdir(), path, fname))
3.749 + }
3.750 + end
3.751 +
3.752 +end
3.753 +
3.754 +
3.755 +class ToplevelInstaller
3.756 +
3.757 + Version = '3.4.1'
3.758 + Copyright = 'Copyright (c) 2000-2006 Minero Aoki'
3.759 +
3.760 + TASKS = [
3.761 + [ 'all', 'do config, setup, then install' ],
3.762 + [ 'config', 'saves your configurations' ],
3.763 + [ 'show', 'shows current configuration' ],
3.764 + [ 'setup', 'compiles ruby extentions and others' ],
3.765 + [ 'install', 'installs files' ],
3.766 + [ 'test', 'run all tests in test/' ],
3.767 + [ 'clean', "does `make clean' for each extention" ],
3.768 + [ 'distclean',"does `make distclean' for each extention" ]
3.769 + ]
3.770 +
3.771 + def ToplevelInstaller.invoke
3.772 + config = ConfigTable.new(load_rbconfig())
3.773 + config.load_standard_entries
3.774 + config.load_multipackage_entries if multipackage?
3.775 + config.fixup
3.776 + klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
3.777 + klass.new(File.dirname($0), config).invoke
3.778 + end
3.779 +
3.780 + def ToplevelInstaller.multipackage?
3.781 + File.dir?(File.dirname($0) + '/packages')
3.782 + end
3.783 +
3.784 + def ToplevelInstaller.load_rbconfig
3.785 + if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
3.786 + ARGV.delete(arg)
3.787 + load File.expand_path(arg.split(/=/, 2)[1])
3.788 + $".push 'rbconfig.rb'
3.789 + else
3.790 + require 'rbconfig'
3.791 + end
3.792 + ::Config::CONFIG
3.793 + end
3.794 +
3.795 + def initialize(ardir_root, config)
3.796 + @ardir = File.expand_path(ardir_root)
3.797 + @config = config
3.798 + # cache
3.799 + @valid_task_re = nil
3.800 + end
3.801 +
3.802 + def config(key)
3.803 + @config[key]
3.804 + end
3.805 +
3.806 + def inspect
3.807 + "#<#{self.class} #{__id__()}>"
3.808 + end
3.809 +
3.810 + def invoke
3.811 + run_metaconfigs
3.812 + case task = parsearg_global()
3.813 + when nil, 'all'
3.814 + parsearg_config
3.815 + init_installers
3.816 + exec_config
3.817 + exec_setup
3.818 + exec_install
3.819 + else
3.820 + case task
3.821 + when 'config', 'test'
3.822 + ;
3.823 + when 'clean', 'distclean'
3.824 + @config.load_savefile if File.exist?(@config.savefile)
3.825 + else
3.826 + @config.load_savefile
3.827 + end
3.828 + __send__ "parsearg_#{task}"
3.829 + init_installers
3.830 + __send__ "exec_#{task}"
3.831 + end
3.832 + end
3.833 +
3.834 + def run_metaconfigs
3.835 + @config.load_script "#{@ardir}/metaconfig"
3.836 + end
3.837 +
3.838 + def init_installers
3.839 + @installer = Installer.new(@config, @ardir, File.expand_path('.'))
3.840 + end
3.841 +
3.842 + #
3.843 + # Hook Script API bases
3.844 + #
3.845 +
3.846 + def srcdir_root
3.847 + @ardir
3.848 + end
3.849 +
3.850 + def objdir_root
3.851 + '.'
3.852 + end
3.853 +
3.854 + def relpath
3.855 + '.'
3.856 + end
3.857 +
3.858 + #
3.859 + # Option Parsing
3.860 + #
3.861 +
3.862 + def parsearg_global
3.863 + while arg = ARGV.shift
3.864 + case arg
3.865 + when /\A\w+\z/
3.866 + setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
3.867 + return arg
3.868 + when '-q', '--quiet'
3.869 + @config.verbose = false
3.870 + when '--verbose'
3.871 + @config.verbose = true
3.872 + when '--help'
3.873 + print_usage $stdout
3.874 + exit 0
3.875 + when '--version'
3.876 + puts "#{File.basename($0)} version #{Version}"
3.877 + exit 0
3.878 + when '--copyright'
3.879 + puts Copyright
3.880 + exit 0
3.881 + else
3.882 + setup_rb_error "unknown global option '#{arg}'"
3.883 + end
3.884 + end
3.885 + nil
3.886 + end
3.887 +
3.888 + def valid_task?(t)
3.889 + valid_task_re() =~ t
3.890 + end
3.891 +
3.892 + def valid_task_re
3.893 + @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
3.894 + end
3.895 +
3.896 + def parsearg_no_options
3.897 + unless ARGV.empty?
3.898 + task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
3.899 + setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
3.900 + end
3.901 + end
3.902 +
3.903 + alias parsearg_show parsearg_no_options
3.904 + alias parsearg_setup parsearg_no_options
3.905 + alias parsearg_test parsearg_no_options
3.906 + alias parsearg_clean parsearg_no_options
3.907 + alias parsearg_distclean parsearg_no_options
3.908 +
3.909 + def parsearg_config
3.910 + evalopt = []
3.911 + set = []
3.912 + @config.config_opt = []
3.913 + while i = ARGV.shift
3.914 + if /\A--?\z/ =~ i
3.915 + @config.config_opt = ARGV.dup
3.916 + break
3.917 + end
3.918 + name, value = *@config.parse_opt(i)
3.919 + if @config.value_config?(name)
3.920 + @config[name] = value
3.921 + else
3.922 + evalopt.push [name, value]
3.923 + end
3.924 + set.push name
3.925 + end
3.926 + evalopt.each do |name, value|
3.927 + @config.lookup(name).evaluate value, @config
3.928 + end
3.929 + # Check if configuration is valid
3.930 + set.each do |n|
3.931 + @config[n] if @config.value_config?(n)
3.932 + end
3.933 + end
3.934 +
3.935 + def parsearg_install
3.936 + @config.no_harm = false
3.937 + @config.install_prefix = ''
3.938 + while a = ARGV.shift
3.939 + case a
3.940 + when '--no-harm'
3.941 + @config.no_harm = true
3.942 + when /\A--prefix=/
3.943 + path = a.split(/=/, 2)[1]
3.944 + path = File.expand_path(path) unless path[0,1] == '/'
3.945 + @config.install_prefix = path
3.946 + else
3.947 + setup_rb_error "install: unknown option #{a}"
3.948 + end
3.949 + end
3.950 + end
3.951 +
3.952 + def print_usage(out)
3.953 + out.puts 'Typical Installation Procedure:'
3.954 + out.puts " $ ruby #{File.basename $0} config"
3.955 + out.puts " $ ruby #{File.basename $0} setup"
3.956 + out.puts " # ruby #{File.basename $0} install (may require root privilege)"
3.957 + out.puts
3.958 + out.puts 'Detailed Usage:'
3.959 + out.puts " ruby #{File.basename $0} <global option>"
3.960 + out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
3.961 +
3.962 + fmt = " %-24s %s\n"
3.963 + out.puts
3.964 + out.puts 'Global options:'
3.965 + out.printf fmt, '-q,--quiet', 'suppress message outputs'
3.966 + out.printf fmt, ' --verbose', 'output messages verbosely'
3.967 + out.printf fmt, ' --help', 'print this message'
3.968 + out.printf fmt, ' --version', 'print version and quit'
3.969 + out.printf fmt, ' --copyright', 'print copyright and quit'
3.970 + out.puts
3.971 + out.puts 'Tasks:'
3.972 + TASKS.each do |name, desc|
3.973 + out.printf fmt, name, desc
3.974 + end
3.975 +
3.976 + fmt = " %-24s %s [%s]\n"
3.977 + out.puts
3.978 + out.puts 'Options for CONFIG or ALL:'
3.979 + @config.each do |item|
3.980 + out.printf fmt, item.help_opt, item.description, item.help_default
3.981 + end
3.982 + out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
3.983 + out.puts
3.984 + out.puts 'Options for INSTALL:'
3.985 + out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
3.986 + out.printf fmt, '--prefix=path', 'install path prefix', ''
3.987 + out.puts
3.988 + end
3.989 +
3.990 + #
3.991 + # Task Handlers
3.992 + #
3.993 +
3.994 + def exec_config
3.995 + @installer.exec_config
3.996 + @config.save # must be final
3.997 + end
3.998 +
3.999 + def exec_setup
3.1000 + @installer.exec_setup
3.1001 + end
3.1002 +
3.1003 + def exec_install
3.1004 + @installer.exec_install
3.1005 + end
3.1006 +
3.1007 + def exec_test
3.1008 + @installer.exec_test
3.1009 + end
3.1010 +
3.1011 + def exec_show
3.1012 + @config.each do |i|
3.1013 + printf "%-20s %s\n", i.name, i.value if i.value?
3.1014 + end
3.1015 + end
3.1016 +
3.1017 + def exec_clean
3.1018 + @installer.exec_clean
3.1019 + end
3.1020 +
3.1021 + def exec_distclean
3.1022 + @installer.exec_distclean
3.1023 + end
3.1024 +
3.1025 +end # class ToplevelInstaller
3.1026 +
3.1027 +
3.1028 +class ToplevelInstallerMulti < ToplevelInstaller
3.1029 +
3.1030 + include FileOperations
3.1031 +
3.1032 + def initialize(ardir_root, config)
3.1033 + super
3.1034 + @packages = directories_of("#{@ardir}/packages")
3.1035 + raise 'no package exists' if @packages.empty?
3.1036 + @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
3.1037 + end
3.1038 +
3.1039 + def run_metaconfigs
3.1040 + @config.load_script "#{@ardir}/metaconfig", self
3.1041 + @packages.each do |name|
3.1042 + @config.load_script "#{@ardir}/packages/#{name}/metaconfig"
3.1043 + end
3.1044 + end
3.1045 +
3.1046 + attr_reader :packages
3.1047 +
3.1048 + def packages=(list)
3.1049 + raise 'package list is empty' if list.empty?
3.1050 + list.each do |name|
3.1051 + raise "directory packages/#{name} does not exist"\
3.1052 + unless File.dir?("#{@ardir}/packages/#{name}")
3.1053 + end
3.1054 + @packages = list
3.1055 + end
3.1056 +
3.1057 + def init_installers
3.1058 + @installers = {}
3.1059 + @packages.each do |pack|
3.1060 + @installers[pack] = Installer.new(@config,
3.1061 + "#{@ardir}/packages/#{pack}",
3.1062 + "packages/#{pack}")
3.1063 + end
3.1064 + with = extract_selection(config('with'))
3.1065 + without = extract_selection(config('without'))
3.1066 + @selected = @installers.keys.select {|name|
3.1067 + (with.empty? or with.include?(name)) \
3.1068 + and not without.include?(name)
3.1069 + }
3.1070 + end
3.1071 +
3.1072 + def extract_selection(list)
3.1073 + a = list.split(/,/)
3.1074 + a.each do |name|
3.1075 + setup_rb_error "no such package: #{name}" unless @installers.key?(name)
3.1076 + end
3.1077 + a
3.1078 + end
3.1079 +
3.1080 + def print_usage(f)
3.1081 + super
3.1082 + f.puts 'Inluded packages:'
3.1083 + f.puts ' ' + @packages.sort.join(' ')
3.1084 + f.puts
3.1085 + end
3.1086 +
3.1087 + #
3.1088 + # Task Handlers
3.1089 + #
3.1090 +
3.1091 + def exec_config
3.1092 + run_hook 'pre-config'
3.1093 + each_selected_installers {|inst| inst.exec_config }
3.1094 + run_hook 'post-config'
3.1095 + @config.save # must be final
3.1096 + end
3.1097 +
3.1098 + def exec_setup
3.1099 + run_hook 'pre-setup'
3.1100 + each_selected_installers {|inst| inst.exec_setup }
3.1101 + run_hook 'post-setup'
3.1102 + end
3.1103 +
3.1104 + def exec_install
3.1105 + run_hook 'pre-install'
3.1106 + each_selected_installers {|inst| inst.exec_install }
3.1107 + run_hook 'post-install'
3.1108 + end
3.1109 +
3.1110 + def exec_test
3.1111 + run_hook 'pre-test'
3.1112 + each_selected_installers {|inst| inst.exec_test }
3.1113 + run_hook 'post-test'
3.1114 + end
3.1115 +
3.1116 + def exec_clean
3.1117 + rm_f @config.savefile
3.1118 + run_hook 'pre-clean'
3.1119 + each_selected_installers {|inst| inst.exec_clean }
3.1120 + run_hook 'post-clean'
3.1121 + end
3.1122 +
3.1123 + def exec_distclean
3.1124 + rm_f @config.savefile
3.1125 + run_hook 'pre-distclean'
3.1126 + each_selected_installers {|inst| inst.exec_distclean }
3.1127 + run_hook 'post-distclean'
3.1128 + end
3.1129 +
3.1130 + #
3.1131 + # lib
3.1132 + #
3.1133 +
3.1134 + def each_selected_installers
3.1135 + Dir.mkdir 'packages' unless File.dir?('packages')
3.1136 + @selected.each do |pack|
3.1137 + $stderr.puts "Processing the package `#{pack}' ..." if verbose?
3.1138 + Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
3.1139 + Dir.chdir "packages/#{pack}"
3.1140 + yield @installers[pack]
3.1141 + Dir.chdir '../..'
3.1142 + end
3.1143 + end
3.1144 +
3.1145 + def run_hook(id)
3.1146 + @root_installer.run_hook id
3.1147 + end
3.1148 +
3.1149 + # module FileOperations requires this
3.1150 + def verbose?
3.1151 + @config.verbose?
3.1152 + end
3.1153 +
3.1154 + # module FileOperations requires this
3.1155 + def no_harm?
3.1156 + @config.no_harm?
3.1157 + end
3.1158 +
3.1159 +end # class ToplevelInstallerMulti
3.1160 +
3.1161 +
3.1162 +class Installer
3.1163 +
3.1164 + FILETYPES = %w( bin lib ext data conf man )
3.1165 +
3.1166 + include FileOperations
3.1167 + include HookScriptAPI
3.1168 +
3.1169 + def initialize(config, srcroot, objroot)
3.1170 + @config = config
3.1171 + @srcdir = File.expand_path(srcroot)
3.1172 + @objdir = File.expand_path(objroot)
3.1173 + @currdir = '.'
3.1174 + end
3.1175 +
3.1176 + def inspect
3.1177 + "#<#{self.class} #{File.basename(@srcdir)}>"
3.1178 + end
3.1179 +
3.1180 + def noop(rel)
3.1181 + end
3.1182 +
3.1183 + #
3.1184 + # Hook Script API base methods
3.1185 + #
3.1186 +
3.1187 + def srcdir_root
3.1188 + @srcdir
3.1189 + end
3.1190 +
3.1191 + def objdir_root
3.1192 + @objdir
3.1193 + end
3.1194 +
3.1195 + def relpath
3.1196 + @currdir
3.1197 + end
3.1198 +
3.1199 + #
3.1200 + # Config Access
3.1201 + #
3.1202 +
3.1203 + # module FileOperations requires this
3.1204 + def verbose?
3.1205 + @config.verbose?
3.1206 + end
3.1207 +
3.1208 + # module FileOperations requires this
3.1209 + def no_harm?
3.1210 + @config.no_harm?
3.1211 + end
3.1212 +
3.1213 + def verbose_off
3.1214 + begin
3.1215 + save, @config.verbose = @config.verbose?, false
3.1216 + yield
3.1217 + ensure
3.1218 + @config.verbose = save
3.1219 + end
3.1220 + end
3.1221 +
3.1222 + #
3.1223 + # TASK config
3.1224 + #
3.1225 +
3.1226 + def exec_config
3.1227 + exec_task_traverse 'config'
3.1228 + end
3.1229 +
3.1230 + alias config_dir_bin noop
3.1231 + alias config_dir_lib noop
3.1232 +
3.1233 + def config_dir_ext(rel)
3.1234 + extconf if extdir?(curr_srcdir())
3.1235 + end
3.1236 +
3.1237 + alias config_dir_data noop
3.1238 + alias config_dir_conf noop
3.1239 + alias config_dir_man noop
3.1240 +
3.1241 + def extconf
3.1242 + ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
3.1243 + end
3.1244 +
3.1245 + #
3.1246 + # TASK setup
3.1247 + #
3.1248 +
3.1249 + def exec_setup
3.1250 + exec_task_traverse 'setup'
3.1251 + end
3.1252 +
3.1253 + def setup_dir_bin(rel)
3.1254 + files_of(curr_srcdir()).each do |fname|
3.1255 + update_shebang_line "#{curr_srcdir()}/#{fname}"
3.1256 + end
3.1257 + end
3.1258 +
3.1259 + alias setup_dir_lib noop
3.1260 +
3.1261 + def setup_dir_ext(rel)
3.1262 + make if extdir?(curr_srcdir())
3.1263 + end
3.1264 +
3.1265 + alias setup_dir_data noop
3.1266 + alias setup_dir_conf noop
3.1267 + alias setup_dir_man noop
3.1268 +
3.1269 + def update_shebang_line(path)
3.1270 + return if no_harm?
3.1271 + return if config('shebang') == 'never'
3.1272 + old = Shebang.load(path)
3.1273 + if old
3.1274 + $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
3.1275 + new = new_shebang(old)
3.1276 + return if new.to_s == old.to_s
3.1277 + else
3.1278 + return unless config('shebang') == 'all'
3.1279 + new = Shebang.new(config('rubypath'))
3.1280 + end
3.1281 + $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
3.1282 + open_atomic_writer(path) {|output|
3.1283 + File.open(path, 'rb') {|f|
3.1284 + f.gets if old # discard
3.1285 + output.puts new.to_s
3.1286 + output.print f.read
3.1287 + }
3.1288 + }
3.1289 + end
3.1290 +
3.1291 + def new_shebang(old)
3.1292 + if /\Aruby/ =~ File.basename(old.cmd)
3.1293 + Shebang.new(config('rubypath'), old.args)
3.1294 + elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
3.1295 + Shebang.new(config('rubypath'), old.args[1..-1])
3.1296 + else
3.1297 + return old unless config('shebang') == 'all'
3.1298 + Shebang.new(config('rubypath'))
3.1299 + end
3.1300 + end
3.1301 +
3.1302 + def open_atomic_writer(path, &block)
3.1303 + tmpfile = File.basename(path) + '.tmp'
3.1304 + begin
3.1305 + File.open(tmpfile, 'wb', &block)
3.1306 + File.rename tmpfile, File.basename(path)
3.1307 + ensure
3.1308 + File.unlink tmpfile if File.exist?(tmpfile)
3.1309 + end
3.1310 + end
3.1311 +
3.1312 + class Shebang
3.1313 + def Shebang.load(path)
3.1314 + line = nil
3.1315 + File.open(path) {|f|
3.1316 + line = f.gets
3.1317 + }
3.1318 + return nil unless /\A#!/ =~ line
3.1319 + parse(line)
3.1320 + end
3.1321 +
3.1322 + def Shebang.parse(line)
3.1323 + cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
3.1324 + new(cmd, args)
3.1325 + end
3.1326 +
3.1327 + def initialize(cmd, args = [])
3.1328 + @cmd = cmd
3.1329 + @args = args
3.1330 + end
3.1331 +
3.1332 + attr_reader :cmd
3.1333 + attr_reader :args
3.1334 +
3.1335 + def to_s
3.1336 + "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
3.1337 + end
3.1338 + end
3.1339 +
3.1340 + #
3.1341 + # TASK install
3.1342 + #
3.1343 +
3.1344 + def exec_install
3.1345 + rm_f 'InstalledFiles'
3.1346 + exec_task_traverse 'install'
3.1347 + end
3.1348 +
3.1349 + def install_dir_bin(rel)
3.1350 + install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755, strip_ext?
3.1351 + end
3.1352 +
3.1353 + def strip_ext?
3.1354 + /mswin|mingw/ !~ RUBY_PLATFORM
3.1355 + end
3.1356 +
3.1357 + def install_dir_lib(rel)
3.1358 + install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
3.1359 + end
3.1360 +
3.1361 + def install_dir_ext(rel)
3.1362 + return unless extdir?(curr_srcdir())
3.1363 + install_files rubyextentions('.'),
3.1364 + "#{config('sodir')}/#{File.dirname(rel)}",
3.1365 + 0555
3.1366 + end
3.1367 +
3.1368 + def install_dir_data(rel)
3.1369 + install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
3.1370 + end
3.1371 +
3.1372 + def install_dir_conf(rel)
3.1373 + # FIXME: should not remove current config files
3.1374 + # (rename previous file to .old/.org)
3.1375 + install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
3.1376 + end
3.1377 +
3.1378 + def install_dir_man(rel)
3.1379 + install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
3.1380 + end
3.1381 +
3.1382 + def install_files(list, dest, mode, stripext = false)
3.1383 + mkdir_p dest, @config.install_prefix
3.1384 + list.each do |fname|
3.1385 + if stripext
3.1386 + install fname, "#{dest}/#{File.basename(fname, '.*')}",
3.1387 + mode, @config.install_prefix
3.1388 + else
3.1389 + install fname, dest, mode, @config.install_prefix
3.1390 + end
3.1391 + end
3.1392 + end
3.1393 +
3.1394 + def libfiles
3.1395 + glob_reject(%w(*.y *.output), targetfiles())
3.1396 + end
3.1397 +
3.1398 + def rubyextentions(dir)
3.1399 + ents = glob_select("*.#{@config.dllext}", targetfiles())
3.1400 + if ents.empty?
3.1401 + setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
3.1402 + end
3.1403 + ents
3.1404 + end
3.1405 +
3.1406 + def targetfiles
3.1407 + mapdir(existfiles() - hookfiles())
3.1408 + end
3.1409 +
3.1410 + def mapdir(ents)
3.1411 + ents.map {|ent|
3.1412 + if File.exist?(ent)
3.1413 + then ent # objdir
3.1414 + else "#{curr_srcdir()}/#{ent}" # srcdir
3.1415 + end
3.1416 + }
3.1417 + end
3.1418 +
3.1419 + # picked up many entries from cvs-1.11.1/src/ignore.c
3.1420 + JUNK_FILES = %w(
3.1421 + core RCSLOG tags TAGS .make.state
3.1422 + .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
3.1423 + *~ *.old *.bak *.BAK *.orig *.rej _$* *$
3.1424 +
3.1425 + *.org *.in .*
3.1426 + )
3.1427 +
3.1428 + def existfiles
3.1429 + glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
3.1430 + end
3.1431 +
3.1432 + def hookfiles
3.1433 + %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
3.1434 + %w( config setup install clean distclean ).map {|t| sprintf(fmt, t) }
3.1435 + }.flatten
3.1436 + end
3.1437 +
3.1438 + def glob_select(pat, ents)
3.1439 + re = globs2re([pat])
3.1440 + ents.select {|ent| re =~ ent }
3.1441 + end
3.1442 +
3.1443 + def glob_reject(pats, ents)
3.1444 + re = globs2re(pats)
3.1445 + ents.reject {|ent| re =~ ent }
3.1446 + end
3.1447 +
3.1448 + GLOB2REGEX = {
3.1449 + '.' => '\.',
3.1450 + '$' => '\$',
3.1451 + '#' => '\#',
3.1452 + '*' => '.*'
3.1453 + }
3.1454 +
3.1455 + def globs2re(pats)
3.1456 + /\A(?:#{
3.1457 + pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
3.1458 + })\z/
3.1459 + end
3.1460 +
3.1461 + #
3.1462 + # TASK test
3.1463 + #
3.1464 +
3.1465 + TESTDIR = 'test'
3.1466 +
3.1467 + def exec_test
3.1468 + unless File.directory?('test')
3.1469 + $stderr.puts 'no test in this package' if verbose?
3.1470 + return
3.1471 + end
3.1472 + $stderr.puts 'Running tests...' if verbose?
3.1473 + begin
3.1474 + require 'test/unit'
3.1475 + rescue LoadError
3.1476 + setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.'
3.1477 + end
3.1478 + runner = Test::Unit::AutoRunner.new(true)
3.1479 + runner.to_run << TESTDIR
3.1480 + runner.run
3.1481 + end
3.1482 +
3.1483 + #
3.1484 + # TASK clean
3.1485 + #
3.1486 +
3.1487 + def exec_clean
3.1488 + exec_task_traverse 'clean'
3.1489 + rm_f @config.savefile
3.1490 + rm_f 'InstalledFiles'
3.1491 + end
3.1492 +
3.1493 + alias clean_dir_bin noop
3.1494 + alias clean_dir_lib noop
3.1495 + alias clean_dir_data noop
3.1496 + alias clean_dir_conf noop
3.1497 + alias clean_dir_man noop
3.1498 +
3.1499 + def clean_dir_ext(rel)
3.1500 + return unless extdir?(curr_srcdir())
3.1501 + make 'clean' if File.file?('Makefile')
3.1502 + end
3.1503 +
3.1504 + #
3.1505 + # TASK distclean
3.1506 + #
3.1507 +
3.1508 + def exec_distclean
3.1509 + exec_task_traverse 'distclean'
3.1510 + rm_f @config.savefile
3.1511 + rm_f 'InstalledFiles'
3.1512 + end
3.1513 +
3.1514 + alias distclean_dir_bin noop
3.1515 + alias distclean_dir_lib noop
3.1516 +
3.1517 + def distclean_dir_ext(rel)
3.1518 + return unless extdir?(curr_srcdir())
3.1519 + make 'distclean' if File.file?('Makefile')
3.1520 + end
3.1521 +
3.1522 + alias distclean_dir_data noop
3.1523 + alias distclean_dir_conf noop
3.1524 + alias distclean_dir_man noop
3.1525 +
3.1526 + #
3.1527 + # Traversing
3.1528 + #
3.1529 +
3.1530 + def exec_task_traverse(task)
3.1531 + run_hook "pre-#{task}"
3.1532 + FILETYPES.each do |type|
3.1533 + if type == 'ext' and config('without-ext') == 'yes'
3.1534 + $stderr.puts 'skipping ext/* by user option' if verbose?
3.1535 + next
3.1536 + end
3.1537 + traverse task, type, "#{task}_dir_#{type}"
3.1538 + end
3.1539 + run_hook "post-#{task}"
3.1540 + end
3.1541 +
3.1542 + def traverse(task, rel, mid)
3.1543 + dive_into(rel) {
3.1544 + run_hook "pre-#{task}"
3.1545 + __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
3.1546 + directories_of(curr_srcdir()).each do |d|
3.1547 + traverse task, "#{rel}/#{d}", mid
3.1548 + end
3.1549 + run_hook "post-#{task}"
3.1550 + }
3.1551 + end
3.1552 +
3.1553 + def dive_into(rel)
3.1554 + return unless File.dir?("#{@srcdir}/#{rel}")
3.1555 +
3.1556 + dir = File.basename(rel)
3.1557 + Dir.mkdir dir unless File.dir?(dir)
3.1558 + prevdir = Dir.pwd
3.1559 + Dir.chdir dir
3.1560 + $stderr.puts '---> ' + rel if verbose?
3.1561 + @currdir = rel
3.1562 + yield
3.1563 + Dir.chdir prevdir
3.1564 + $stderr.puts '<--- ' + rel if verbose?
3.1565 + @currdir = File.dirname(rel)
3.1566 + end
3.1567 +
3.1568 + def run_hook(id)
3.1569 + path = [ "#{curr_srcdir()}/#{id}",
3.1570 + "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
3.1571 + return unless path
3.1572 + $stderr.puts "invoking hook script #{path}" if verbose?
3.1573 + begin
3.1574 + instance_eval File.read(path), path, 1
3.1575 + rescue
3.1576 + raise if $DEBUG
3.1577 + setup_rb_error "hook #{path} failed:\n" + $!.message
3.1578 + end
3.1579 + end
3.1580 +
3.1581 +end # class Installer
3.1582 +
3.1583 +
3.1584 +class SetupError < StandardError; end
3.1585 +
3.1586 +def setup_rb_error(msg)
3.1587 + raise SetupError, msg
3.1588 +end
3.1589 +
3.1590 +if $0 == __FILE__
3.1591 + begin
3.1592 + ToplevelInstaller.invoke
3.1593 + rescue SetupError
3.1594 + raise if $DEBUG
3.1595 + $stderr.puts $!.message
3.1596 + $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
3.1597 + exit 1
3.1598 + end
3.1599 +end