Newer
Older
# Copyright (c) 2000-2005 Minero Aoki
# This program is free software.
# You can distribute/modify this program under the terms of
# the GNU LGPL, Lesser General Public License version 2.1.
unless Enumerable.method_defined?(:map) # Ruby 1.4.6
module Enumerable
unless File.respond_to?(:read) # Ruby 1.6
def File.read(fname)
open(fname) {|f|
return f.read
}
unless Errno.const_defined?(:ENOTEMPTY) # Windows?
module Errno
class ENOTEMPTY
# We do not raise this exception, implementation is not needed.
def File.binread(fname)
open(fname, 'rb') {|f|
return f.read
}
# for corrupted Windows' stat(2)
def File.dir?(path)
File.directory?((path[-1,1] == '/') ? path : path + '/')
include Enumerable
def initialize(rbconfig)
@rbconfig = rbconfig
@items = []
@table = {}
# options
@install_prefix = nil
@config_opt = nil
@verbose = true
@no_harm = false
end
attr_accessor :install_prefix
attr_accessor :config_opt
attr_writer :verbose
def verbose?
@verbose
end
attr_writer :no_harm
def no_harm?
@no_harm
def [](key)
lookup(key).resolve(self)
def []=(key, val)
lookup(key).set val
def names
@items.map {|i| i.name }
def each(&block)
@items.each(&block)
def key?(name)
@table.key?(name)
def lookup(name)
@table[name] or setup_rb_error "no such config item: #{name}"
def add(item)
@items.push item
@table[item.name] = item
def remove(name)
item = lookup(name)
@items.delete_if {|i| i.name == name }
@table.delete_if {|name, i| i.name == name }
item
def load_script(path, inst = nil)
if File.file?(path)
MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
end
def savefile
'.config'
def load_savefile
begin
File.foreach(savefile()) do |line|
k, v = *line.split(/=/, 2)
self[k] = v.strip
rescue Errno::ENOENT
setup_rb_error $!.message + "\n#{File.basename($0)} config first"
def save
@items.each {|i| i.value }
File.open(savefile(), 'w') {|f|
@items.each do |i|
f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
end
}
def load_standard_entries
standard_entries(@rbconfig).each do |ent|
add ent
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
def standard_entries(rbconfig)
c = rbconfig
rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
major = c['MAJOR'].to_i
minor = c['MINOR'].to_i
teeny = c['TEENY'].to_i
version = "#{major}.#{minor}"
# ruby ver. >= 1.4.4?
newpath_p = ((major >= 2) or
((major == 1) and
((minor >= 5) or
((minor == 4) and (teeny >= 4)))))
if c['rubylibdir']
# V > 1.6.3
libruby = "#{c['prefix']}/lib/ruby"
librubyver = c['rubylibdir']
librubyverarch = c['archdir']
siteruby = c['sitedir']
siterubyver = c['sitelibdir']
siterubyverarch = c['sitearchdir']
elsif newpath_p
# 1.4.4 <= V <= 1.6.3
libruby = "#{c['prefix']}/lib/ruby"
librubyver = "#{c['prefix']}/lib/ruby/#{version}"
librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
siteruby = c['sitedir']
siterubyver = "$siteruby/#{version}"
siterubyverarch = "$siterubyver/#{c['arch']}"
else
# V < 1.4.4
libruby = "#{c['prefix']}/lib/ruby"
librubyver = "#{c['prefix']}/lib/ruby/#{version}"
librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
siterubyver = siteruby
siterubyverarch = "$siterubyver/#{c['arch']}"
end
parameterize = lambda {|path|
path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
makeprog = 'make'
end
[
ExecItem.new('installdirs', 'std/site/home',
'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
{|val, table|
case val
when 'std'
table['rbdir'] = '$librubyver'
table['sodir'] = '$librubyverarch'
when 'site'
table['rbdir'] = '$siterubyver'
table['sodir'] = '$siterubyverarch'
when 'home'
setup_rb_error '$HOME was not set' unless ENV['HOME']
table['prefix'] = ENV['HOME']
table['rbdir'] = '$libdir/ruby'
table['sodir'] = '$libdir/ruby'
end
},
PathItem.new('prefix', 'path', c['prefix'],
'path prefix of target environment'),
PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
'the directory for commands'),
PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
'the directory for libraries'),
PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
'the directory for shared data'),
PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
'the directory for man pages'),
PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
'the directory for system configuration files'),
PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
'the directory for local state data'),
PathItem.new('libruby', 'path', libruby,
'the directory for ruby libraries'),
PathItem.new('librubyver', 'path', librubyver,
'the directory for standard ruby libraries'),
PathItem.new('librubyverarch', 'path', librubyverarch,
'the directory for standard ruby extensions'),
PathItem.new('siteruby', 'path', siteruby,
'the directory for version-independent aux ruby libraries'),
PathItem.new('siterubyver', 'path', siterubyver,
'the directory for aux ruby libraries'),
PathItem.new('siterubyverarch', 'path', siterubyverarch,
'the directory for aux ruby binaries'),
PathItem.new('rbdir', 'path', '$siterubyver',
'the directory for ruby scripts'),
PathItem.new('sodir', 'path', '$siterubyverarch',
'the directory for ruby extentions'),
PathItem.new('rubypath', 'path', rubypath,
'the path to set to #! line'),
ProgramItem.new('rubyprog', 'name', rubypath,
'the ruby program using for installation'),
ProgramItem.new('makeprog', 'name', makeprog,
'the make program to compile ruby extentions'),
SelectItem.new('shebang', 'all/ruby/never', 'ruby',
'shebang line (#!) editing mode'),
BoolItem.new('without-ext', 'yes/no', 'no',
'does not compile/install ruby extentions')
]
end
private :standard_entries
def load_multipackage_entries
multipackage_entries().each do |ent|
add ent
end
end
def multipackage_entries
[
PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
'package names that you want to install'),
PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
'package names that you do not want to install')
]
end
private :multipackage_entries
ALIASES = {
'std-ruby' => 'librubyver',
'stdruby' => 'librubyver',
'rubylibdir' => 'librubyver',
'archdir' => 'librubyverarch',
'site-ruby-common' => 'siteruby', # For backward compatibility
'site-ruby' => 'siterubyver', # For backward compatibility
'bin-dir' => 'bindir',
'bin-dir' => 'bindir',
'rb-dir' => 'rbdir',
'so-dir' => 'sodir',
'data-dir' => 'datadir',
'ruby-path' => 'rubypath',
'ruby-prog' => 'rubyprog',
'ruby' => 'rubyprog',
'make-prog' => 'makeprog',
'make' => 'makeprog'
}
def fixup
ALIASES.each do |ali, name|
@table[ali] = @table[name]
@items.freeze
@table.freeze
@options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
def parse_opt(opt)
m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
m.to_a[1,2]
def dllext
@rbconfig['DLEXT']
def value_config?(name)
lookup(name).value?
class Item
def initialize(name, template, default, desc)
@name = name.freeze
@template = template
@value = default
@default = default
@description = desc
end
attr_reader :name
attr_reader :description
attr_accessor :default
alias help_default default
def help_opt
"--#{@name}=#{@template}"
end
def value?
true
end
def value
@value
end
def resolve(table)
@value.gsub(%r<\$([^/]+)>) { table[$1] }
end
def set(val)
@value = check(val)
end
private
def check(val)
setup_rb_error "config: --#{name} requires argument" unless val
val
end
class BoolItem < Item
def config_type
'bool'
end
def help_opt
"--#{@name}"
end
private
def check(val)
return 'yes' unless val
case val
when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
when /\An(o)?\z/i, /\Af(alse)\z/i then 'no'
else
setup_rb_error "config: --#{@name} accepts only yes/no for argument"
end
end
class PathItem < Item
def config_type
'path'
end
private
def check(path)
setup_rb_error "config: --#{@name} requires argument" unless path
path[0,1] == '$' ? path : File.expand_path(path)
end
class ProgramItem < Item
def config_type
'program'
end
class SelectItem < Item
def initialize(name, selection, default, desc)
super
@ok = selection.split('/')
end
def config_type
'select'
end
private
def check(val)
unless @ok.include?(val.strip)
setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
end
val.strip
end
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
class ExecItem < Item
def initialize(name, selection, desc, &block)
super name, selection, nil, desc
@ok = selection.split('/')
@action = block
end
def config_type
'exec'
end
def value?
false
end
def resolve(table)
setup_rb_error "$#{name()} wrongly used as option value"
end
undef set
def evaluate(val, table)
v = val.strip.downcase
unless @ok.include?(v)
setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
end
@action.call v, table
end
class PackageSelectionItem < Item
def initialize(name, template, default, help_default, desc)
super name, template, default, desc
@help_default = help_default
end
attr_reader :help_default
def config_type
'package'
end
private
def check(val)
unless File.dir?("packages/#{val}")
setup_rb_error "config: no such package: #{val}"
end
val
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
class MetaConfigEnvironment
def initialize(config, installer)
@config = config
@installer = installer
end
def config_names
@config.names
end
def config?(name)
@config.key?(name)
end
def bool_config?(name)
@config.lookup(name).config_type == 'bool'
end
def path_config?(name)
@config.lookup(name).config_type == 'path'
end
def value_config?(name)
@config.lookup(name).config_type != 'exec'
end
def add_config(item)
@config.add item
end
def add_bool_config(name, default, desc)
@config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
end
def add_path_config(name, default, desc)
@config.add PathItem.new(name, 'path', default, desc)
end
def set_config_default(name, default)
@config.lookup(name).default = default
end
def remove_config(name)
@config.remove(name)
end
# For only multipackage
def packages
raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
@installer.packages
end
# For only multipackage
def declare_packages(list)
raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
@installer.packages = list
end
end # class ConfigTable
# This module requires: #verbose?, #no_harm?
def mkdir_p(dirname, prefix = nil)
dirname = prefix + File.expand_path(dirname) if prefix
$stderr.puts "mkdir -p #{dirname}" if verbose?
# Does not check '/', it's too abnormal.
dirs = File.expand_path(dirname).split(%r<(?=/)>)
if /\A[a-z]:\z/i =~ dirs[0]
disk = dirs.shift
dirs[0] = disk + dirs[0]
end
dirs.each_index do |idx|
path = dirs[0..idx].join('')
Dir.mkdir path unless File.dir?(path)
def rm_f(path)
$stderr.puts "rm -f #{path}" if verbose?
force_remove_file path
def rm_rf(path)
$stderr.puts "rm -rf #{path}" if verbose?
remove_tree path
end
def remove_tree(path)
if File.symlink?(path)
remove_file path
elsif File.dir?(path)
remove_tree0 path
else
force_remove_file path
end
end
def remove_tree0(path)
Dir.foreach(path) do |ent|
next if ent == '.'
next if ent == '..'
entpath = "#{path}/#{ent}"
if File.symlink?(entpath)
remove_file entpath
elsif File.dir?(entpath)
remove_tree0 entpath
force_remove_file entpath
begin
Dir.rmdir path
rescue Errno::ENOTEMPTY
# directory may not be empty
end
def move_file(src, dest)
force_remove_file dest
File.rename src, dest
File.open(dest, 'wb') {|f|
f.write File.binread(src)
}
File.unlink src
end
end
def force_remove_file(path)
begin
remove_file path
rescue
def remove_file(path)
File.chmod 0777, path
File.unlink path
end
def install(from, dest, mode, prefix = nil)
$stderr.puts "install #{from} #{dest}" if verbose?
return if no_harm?
realdest = prefix ? prefix + File.expand_path(dest) : dest
realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
str = File.binread(from)
if diff?(str, realdest)
rm_f realdest if File.exist?(realdest)
}
File.open(realdest, 'wb') {|f|
f.write str
File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
if prefix
f.puts realdest.sub(prefix, '')
else
f.puts realdest
end
}
def diff?(new_content, path)
return true unless File.exist?(path)
new_content != File.binread(path)
def command(*args)
$stderr.puts args.join(' ') if verbose?
system(*args) or raise RuntimeError,
"system(#{args.map{|a| a.inspect }.join(' ')}) failed"
def ruby(*args)
command config('rubyprog'), *args
end
def make(task = nil)
command(*[config('makeprog'), task].compact)
def extdir?(dir)
File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
def files_of(dir)
Dir.open(dir) {|d|
return d.select {|ent| File.file?("#{dir}/#{ent}") }
DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
def directories_of(dir)
Dir.open(dir) {|d|
return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
# This module requires: #srcdir_root, #objdir_root, #relpath
module HookScriptAPI
def get_config(key)
@config[key]
end
alias config get_config
# obsolete: use metaconfig to change configuration
def set_config(key, val)
# srcdir/objdir (works only in the package directory)
"#{srcdir_root()}/#{relpath()}"
"#{objdir_root()}/#{relpath()}"
def srcfile(path)
"#{curr_srcdir()}/#{path}"
def srcexist?(path)
File.exist?(srcfile(path))
def srcdirectory?(path)
File.dir?(srcfile(path))
def srcfile?(path)
File.file?(srcfile(path))
def srcentries(path = '.')
Dir.open("#{curr_srcdir()}/#{path}") {|d|
return d.to_a - %w(. ..)
def srcfiles(path = '.')
File.file?(File.join(curr_srcdir(), path, fname))
def srcdirectories(path = '.')
File.dir?(File.join(curr_srcdir(), path, fname))
class ToplevelInstaller
Version = '3.4.1'
Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
TASKS = [
[ 'all', 'do config, setup, then install' ],
[ 'config', 'saves your configurations' ],
[ 'show', 'shows current configuration' ],
[ 'setup', 'compiles ruby extentions and others' ],
[ 'install', 'installs files' ],
[ 'test', 'run all tests in test/' ],
[ 'clean', "does `make clean' for each extention" ],
[ 'distclean',"does `make distclean' for each extention" ]
]
def ToplevelInstaller.invoke
config = ConfigTable.new(load_rbconfig())
config.load_standard_entries
config.load_multipackage_entries if multipackage?
config.fixup
klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
klass.new(File.dirname($0), config).invoke
def ToplevelInstaller.multipackage?
File.dir?(File.dirname($0) + '/packages')
def ToplevelInstaller.load_rbconfig
if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
ARGV.delete(arg)
load File.expand_path(arg.split(/=/, 2)[1])
$".push 'rbconfig.rb'
else
require 'rbconfig'
end
::Config::CONFIG
def initialize(ardir_root, config)
@ardir = File.expand_path(ardir_root)
@config = config
# cache
@valid_task_re = nil
def config(key)
@config[key]
def inspect
"#<#{self.class} #{__id__()}>"
def invoke
run_metaconfigs
case task = parsearg_global()
when nil, 'all'
parsearg_config
init_installers
exec_config
exec_setup
exec_install
else
case task
when 'config', 'test'
;
when 'clean', 'distclean'
@config.load_savefile if File.exist?(@config.savefile)
else
@config.load_savefile
end
__send__ "parsearg_#{task}"
init_installers
__send__ "exec_#{task}"
def run_metaconfigs
@config.load_script "#{@ardir}/metaconfig"
def init_installers
@installer = Installer.new(@config, @ardir, File.expand_path('.'))
# Hook Script API bases
def srcdir_root
@ardir
def objdir_root
'.'
def relpath
'.'
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
#
# Option Parsing
#
def parsearg_global
while arg = ARGV.shift
case arg
when /\A\w+\z/
setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
return arg
when '-q', '--quiet'
@config.verbose = false
when '--verbose'
@config.verbose = true
when '--help'
print_usage $stdout
exit 0
when '--version'
puts "#{File.basename($0)} version #{Version}"
exit 0
when '--copyright'
puts Copyright
exit 0
else
setup_rb_error "unknown global option '#{arg}'"
end
end
nil
def valid_task?(t)
valid_task_re() =~ t
def valid_task_re
@valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
end
def parsearg_no_options
unless ARGV.empty?
task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
alias parsearg_show parsearg_no_options
alias parsearg_setup parsearg_no_options
alias parsearg_test parsearg_no_options
alias parsearg_clean parsearg_no_options
alias parsearg_distclean parsearg_no_options
def parsearg_config
evalopt = []
set = []
@config.config_opt = []
while i = ARGV.shift
if /\A--?\z/ =~ i
@config.config_opt = ARGV.dup
break
end
name, value = *@config.parse_opt(i)
if @config.value_config?(name)
@config[name] = value
else
evalopt.push [name, value]
end
set.push name
end
evalopt.each do |name, value|
@config.lookup(name).evaluate value, @config
end
# Check if configuration is valid
set.each do |n|
@config[n] if @config.value_config?(n)
def parsearg_install
@config.no_harm = false
@config.install_prefix = ''
while a = ARGV.shift
case a
when '--no-harm'
@config.no_harm = true
when /\A--prefix=/
path = a.split(/=/, 2)[1]
path = File.expand_path(path) unless path[0,1] == '/'
@config.install_prefix = path
else
setup_rb_error "install: unknown option #{a}"
end
end
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
def print_usage(out)
out.puts 'Typical Installation Procedure:'
out.puts " $ ruby #{File.basename $0} config"
out.puts " $ ruby #{File.basename $0} setup"
out.puts " # ruby #{File.basename $0} install (may require root privilege)"
out.puts
out.puts 'Detailed Usage:'
out.puts " ruby #{File.basename $0} <global option>"
out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
fmt = " %-24s %s\n"
out.puts
out.puts 'Global options:'
out.printf fmt, '-q,--quiet', 'suppress message outputs'
out.printf fmt, ' --verbose', 'output messages verbosely'
out.printf fmt, ' --help', 'print this message'
out.printf fmt, ' --version', 'print version and quit'
out.printf fmt, ' --copyright', 'print copyright and quit'
out.puts
out.puts 'Tasks:'
TASKS.each do |name, desc|
out.printf fmt, name, desc
end
fmt = " %-24s %s [%s]\n"
out.puts
out.puts 'Options for CONFIG or ALL:'
@config.each do |item|
out.printf fmt, item.help_opt, item.description, item.help_default
end
out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
out.puts
out.puts 'Options for INSTALL:'
out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
out.printf fmt, '--prefix=path', 'install path prefix', ''
out.puts
#
# Task Handlers
#
def exec_config
@installer.exec_config
@config.save # must be final
def exec_setup
@installer.exec_setup
def exec_install
@installer.exec_install