# # setup.rb # # Copyright (c) 2000,2001 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU General Public License version 2 or later. # require 'tempfile' if i = ARGV.index(/\A--rbconfig=/) then file = $' ARGV.delete_at(i) require file else require 'rbconfig' end class InstallError < StandardError; end class Installer Version = '2.0.4' Copyright = 'Copyright (c) 2000,2001 Minero Aoki' TASKS = { 'config' => 'save your config configurations', 'setup' => 'compiles extention or else', 'install' => 'installs packages', 'clean' => "does `make clean' for each extention", 'dryrun' => 'does test run', 'show' => 'shows current configuration' } TASK_ORDER = %w( config setup install clean dryrun show ) FILETYPES = %w( bin lib ext share ) SUBPACKAGES = { 'dbd_mysql' => 'Mysql', 'dbd_msql' => 'Msql', 'dbd_interbase' => 'InterBase', 'dbd_oracle' => 'Oracle', 'dbd_db2' => 'DB2', 'dbd_ado' => 'ADO', 'dbd_pg' => 'Pg', 'dbd_proxy' => 'Proxy', 'dbd_odbc' => 'ODBC', 'dbd_sqlrelay' => 'SQLRelay', 'dbd_frontbase' => 'FrontBase' } def initialize( argv ) argv = argv.dup @verbose = true @no_harm = false @config = {} @task = nil @other_args = [] @task = parsearg( argv ) parsearg_TASK @task, argv unless @task == 'config' then load_configs check_packdesig end end attr :config attr :task ### ### arg proc. ### def parsearg( argv ) task_re = /\A(?:#{TASKS.keys.join '|'})\z/ task = nil arg = argv.shift case arg when /\A\w+\z/ unless task_re === arg then raise InstallError, "wrong task: #{arg}" end task = arg when '-h', '--help' print_usage $stdout exit 0 when '-v', '--version' puts "setup.rb version #{Version}" exit 0 when '--copyright' puts Copyright exit 0 else raise InstallError, "unknown global option '#{arg}'" end task end def parsearg_TASK( task, argv ) mid = "parsearg_#{task}" if respond_to? mid, true then __send__ mid, argv else unless argv.empty? then raise InstallError, "#{task}: unknown options: #{argv.join ' '}" end end end def parsearg_config( args ) @config_args = {} re = /\A--(#{CONFIG_ORDER.join '|'})=/ args.each do |i| m = re.match(i) or raise InstallError, "config: unknown option #{i}" @config_args[ m[1] ] = m.post_match.strip end end def parsearg_install( args ) args.each do |i| if i == '--no-harm' then @no_harm = true else raise InstallError, "#{@task}: wrong option #{i}" end end end def parsearg_dryrun( args ) @dryrun_args = args end def print_usage( out ) out.puts out.puts 'Usage:' out.puts ' ruby setup.rb ' out.puts ' ruby setup.rb []' out.puts out.puts 'Tasks:' TASK_ORDER.each do |name| out.printf " %-10s %s\n", name, TASKS[name] end fmt = " %-20s %s\n" out.puts out.puts 'Global options:' out.printf fmt, '-h,--help', 'print this message' out.printf fmt, '-v,--version', 'print version' out.printf fmt, '--copyright', 'print copyright' out.puts out.puts 'Options for config:' CONFIG_ORDER.each do |name| dflt, arg, desc, dflt2 = CONFIG_OPTS[name] dflt = dflt2 || dflt out.printf " %-20s %s [%s]\n", "--#{name}=#{arg}", desc, dflt end out.printf " %-20s %s [%s]\n", '--rbconfig=path', 'your rbconfig.rb to load', "running ruby's" out.puts out.puts 'Options for install:' out.printf " %-20s %s [%s]\n", '--no-harm', 'only display what to do if given', 'off' out.puts out.puts 'This archive includes:' out.print ' ', packages().join(' '), "\n" out.puts end ### ### tasks ### def execute case @task when 'config', 'setup', 'install', 'clean' tryto @task when 'show' do_show when 'dryrun' do_dryrun else raise 'must not happen' end end def tryto( task ) $stderr.printf "entering %s phase...\n", task begin __send__ 'do_' + task rescue $stderr.printf "%s failed\n", task raise end $stderr.printf "%s done.\n", task end ConfigFile = 'config.save' def do_config CONFIG_OPTS.each do |k,v| dflt, vname, desc = v @config[k] = dflt end @config_args.each do |k,v| setconf k, v end save_configs end def do_show CONFIG_ORDER.each do |k| v = @config[k] if not v or v.empty? then v = '(not specified)' end printf "%-10s %s\n", k, v end end def do_setup into_dir( 'bin' ) { foreach_package do Dir.foreach( '.' ) do |fname| next unless File.file? fname add_rubypath fname end end } into_dir( 'ext' ) { foreach_package do clean extconf make end } end def do_install into_dir( 'bin' ) { foreach_package do |targ, *dummy| install_bin end } into_dir( 'lib' ) { into_dir( 'dbd' ) { SUBPACKAGES.each do |package_code, capname| basefile = capname + '.rb' if @with.include?( package_code ) isdir( File.join( @config['rb-dir'], 'DBD', capname ) ) install( basefile, File.join( @config['rb-dir'], 'DBD', capname, basefile ), 0644 ) end end } into_dir( 'dbi' ) { install_rb 'dbi' } install( 'dbi.rb', @config['rb-dir'], 0644 ) } into_dir( 'ext' ) { foreach_package do |targ, *dummy| install_so targ end } into_dir( 'share' ) { foreach_package do |targ, *dummy| install_dat targ end } end def do_clean into_dir( 'ext' ) { foreach_package do clean end } # rmf ConfigFile end def do_dryrun unless dir? 'tmp' then $stderr.puts 'setup.rb: setting up temporaly environment...' @verbose = $DEBUG begin @config['bin-dir'] = isdir(File.expand_path('.'), 'tmp', 'bin') @config['rb-dir'] = isdir(File.expand_path('.'), 'tmp', 'lib') @config['so-dir'] = isdir(File.expand_path('.'), 'tmp', 'ext') @config['data-dir'] = isdir(File.expand_path('.'), 'tmp', 'share') do_install rescue rmrf 'tmp' $stderr.puts '[BUG] setup.rb bug: "dryrun" command failed' raise end end exec @config['ruby-path'], '-I' + File.join('.', 'tmp', 'lib'), '-I' + File.join('.', 'tmp', 'ext'), *@dryrun_args end ### ### lib ### # # config # c = ::Config::CONFIG rubyname = c['ruby_install_name'] major = c['MAJOR'].to_i minor = c['MINOR'].to_i teeny = c['TEENY'].to_i version = "#{major}.#{minor}" arch = c['arch'] bindir = File.join( c['bindir'] ) rubylib = File.join( c['libdir'], 'ruby' ) datadir = File.join( c['datadir'] ) rubypath = File.join( bindir, rubyname ) # >=1.4.4 is new path newpath_p = ((major >= 2) or ((major == 1) and ((minor >= 5) or ((minor == 4) and (teeny >= 4))))) if c['rubylibdir'] then # 1.6.3 < V stdlibdir = c['rubylibdir'] sitelibdir = c['sitelibdir'] elsif newpath_p then stdlibdir = File.join( rubylib, version ) sitelibdir = File.join( rubylib, 'site_ruby', version ) else stdlibdir = File.join( rubylib, version ) sitelibdir = File.join( rubylib, version, 'site_ruby' ) end siterb = sitelibdir siteso = File.join( sitelibdir, arch ) CONFIG_OPTS = { 'bin-dir' => [ bindir, 'path', 'directory to install commands' ], 'rb-dir' => [ siterb, 'path', 'directory to install ruby scripts' ], 'so-dir' => [ siteso, 'path', 'directory to install ruby extentions' ], 'data-dir' => [ datadir, 'path', 'directory to install data' ], 'ruby-path' => [ rubypath, 'path', 'path to ruby for #!' ], 'ruby-prog' => [ rubypath, 'path', 'path to ruby for installation' ], 'make-prog' => [ 'make', 'name', 'make program to compile ruby extentions' ], 'with' => [ '', 'name,name...', 'package name(s) you want to install', 'ALL' ], 'without' => [ '', 'name,name...', 'package name(s) you do not want to install' ] } CONFIG_ORDER = %w( bin-dir rb-dir so-dir ruby-path make-prog with without ) def save_configs File.open( ConfigFile, 'w' ) do |f| @config.each do |k,v| f.printf "%s=%s\n", k, v if v end end end def load_configs File.file? ConfigFile or raise InstallError, 'setup.rb config first' File.foreach( ConfigFile ) do |line| k, v = line.split( '=', 2 ) setconf k.strip, v.strip end end def setconf( k, v ) if CONFIG_OPTS[k][1] == 'path' then @config[k] = File.expand_path(v) else @config[k] = v end end # # packages # def check_packdesig @with = extract_dirs( @config['with'] ) @without = extract_dirs( @config['without'] ) packs = packages (@with + @without).each do |i| if not packs.include? i and not dir? i then raise InstallError, "no such package or directory '#{i}'" end end end def extract_dirs( s ) ret = [] s.split(',').each do |i| if /[\*\?]/ === i then tmp = Dir.glob(i) tmp.delete_if {|d| not dir? d } if tmp.empty? then tmp.push i # causes error else ret.concat tmp end else ret.push i end end ret end def packages ret = [] FILETYPES.each do |type| next unless File.exist? "#{ type }/PATHCONV" foreach_record( "#{type}/PATHCONV" ) do |dir, pack, *dummy| ret.push pack end end SUBPACKAGES.each do |package_code, capname| ret.push( package_code ); end ret.uniq end def foreach_package path = {} foreach_record( './PATHCONV' ) do |dir, pack, targ, topfile, *dummy| path[dir] = [pack, targ, topfile] end base = File.basename( Dir.getwd ) Dir.foreach('.') do |dir| next if dir[0] == ?. next unless dir? dir next if dir == "CVS" path[dir] or raise "abs path for package '#{dir}' not exist" pack, targ, topfile = path[dir] if inclpack pack, "#{base}/#{dir}" then chdir( dir ) { yield targ, topfile } else $stderr.puts "setup.rb: skip #{base}/#{dir}(#{pack}) by user option" end end end def foreach_record( fname ) File.foreach( fname ) do |line| line.strip! next if line.empty? a = line.split(/\s+/) a[2] ||= '.' yield a end end def inclpack( pack, dname ) if @with.empty? then not @without.include? pack and not @without.include? dname else @with.include? pack or @with.include? dname end end # # setup # def add_rubypath( fn, opt = nil ) line = "\#!#{@config['ruby-path']}#{opt ? ' ' + opt : ''}" $stderr.puts %Q if @verbose return if @no_harm tmpf = nil File.open( fn ) do |f| first = f.gets return unless /\A\#!.*ruby/ === first tmpf = Tempfile.open( 'amsetup' ) tmpf.puts line tmpf << first f.each {|i| tmpf << i } tmpf.close end mod = File.stat( fn ).mode tmpf.open File.open( fn, 'w' ) do |wf| tmpf.each {|i| wf << i } end File.chmod mod, fn tmpf.close true end # # install # def install_bin install_all isdir(@config['bin-dir']), 0555 end def install_rb( dir ) install_all isdir(@config['rb-dir'] + '/' + dir), 0644 end def install_dat( dir ) install_all isdir(@config['data-dir'] + '/' + dir), 0644 end def install_all( dir, mode ) Dir.foreach('.') do |fname| next if /\A\./ === fname next unless File.file? fname install fname, dir, mode end end def create_topfile( name, req ) d = isdir(@config['rb-dir']) File.open( "#{d}/#{name}.rb", 'w' ) do |f| f.puts "require '#{name}/#{req}'" end File.chmod 0644, "#{d}/#{name}.rb" end def extconf command "#{@config['ruby-prog']} extconf.rb" end def make command @config['make-prog'] end def clean command @config['make-prog'] + ' clean' if File.file? 'Makefile' end def install_so( dir ) to = isdir(File.expand_path(@config['so-dir'] + '/' + dir)) find_so('.').each do |fn| install fn, to, 0555 end end DLEXT = ::Config::CONFIG['DLEXT'] def find_so( dir = '.' ) fnames = nil Dir.open( dir ) {|d| fnames = d.to_a } exp = /\.#{DLEXT}\z/ arr = fnames.find_all {|fn| exp === fn } arr or raise InstallError, 'no ruby extention exists: have you done "ruby setup.rb setup" ?' end def so_dir?( dn = '.' ) File.file? "#{dn}/MANIFEST" end # # file op. # def into_dir( libn ) return unless dir? libn chdir( libn ) { yield } end def chdir( dn ) curr = Dir.pwd begin Dir.chdir dn yield ensure Dir.chdir curr end end def isdir( dn ) mkpath dn dn end def mkpath( dname ) $stderr.puts "mkdir -p #{dname}" if @verbose return if @no_harm # does not check '/'... it's too abnormal case dirs = dname.split(%r_(?=/)_) if /\A[a-z]:\z/i === dirs[0] then disk = dirs.shift dirs[0] = disk + dirs[0] end dirs.each_index do |idx| path = dirs[0..idx].join('') Dir.mkdir path unless dir? path end end def rmf( fname ) $stderr.puts "rm -f #{fname}" if @verbose return if @no_harm if File.exist? fname or File.symlink? fname then File.chmod 0777, fname File.unlink fname end end def rmrf( dn ) $stderr.puts "rm -rf #{dn}" if @verbose return if @no_harm Dir.chdir dn Dir.foreach('.') do |fn| next if fn == '.' next if fn == '..' if dir? fn then verbose_off { rmrf fn } else verbose_off { rmf fn } end end Dir.chdir '..' Dir.rmdir dn end def verbose_off save, @verbose = @verbose, false yield @verbose = save end def install( from, to, mode ) $stderr.puts "install #{from} #{to}" if @verbose return if @no_harm if dir? to then to = to + '/' + File.basename(from) end str = nil File.open( from, 'rb' ) {|f| str = f.read } if diff? str, to then verbose_off { rmf to if File.exist? to } File.open( to, 'wb' ) {|f| f.write str } File.chmod mode, to end end def diff?( orig, comp ) return true unless File.exist? comp s2 = nil File.open( comp, 'rb' ) {|f| s2 = f.read } orig != s2 end def command( str ) $stderr.puts str if @verbose system str or raise RuntimeError, "'system #{str}' failed" end def dir?( dname ) # for CORRUPTED windows stat() File.directory?(dname[-1,1] == '/' ? dname : dname + '/') end end if $0 == __FILE__ then begin MainInstaller = Installer.new( ARGV ) MainInstaller.execute rescue raise if $DEBUG $stderr.puts $! $stderr.puts 'try "ruby setup.rb --help" for usage' exit 1 end end