Skip to content
Snippets Groups Projects
install.rb 35.3 KiB
Newer Older
  • Learn to ignore specific revisions
  •   def exec_test
        @installer.exec_test
    
      def exec_show
        @config.each do |i|
          printf "%-20s %s\n", i.name, i.value if i.value?
        end
      end
    
      def exec_clean
        @installer.exec_clean
    
      def exec_distclean
        @installer.exec_distclean
      end
    
    end   # class ToplevelInstaller
    
    
    class ToplevelInstallerMulti < ToplevelInstaller
    
      include FileOperations
    
      def initialize(ardir_root, config)
        super
        @packages = directories_of("#{@ardir}/packages")
        raise 'no package exists' if @packages.empty?
        @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
    
      def run_metaconfigs
        @config.load_script "#{@ardir}/metaconfig", self
        @packages.each do |name|
          @config.load_script "#{@ardir}/packages/#{name}/metaconfig"
        end
    
      attr_reader :packages
    
      def packages=(list)
        raise 'package list is empty' if list.empty?
        list.each do |name|
          raise "directory packages/#{name} does not exist"\
                  unless File.dir?("#{@ardir}/packages/#{name}")
        end
        @packages = list
    
      def init_installers
        @installers = {}
        @packages.each do |pack|
          @installers[pack] = Installer.new(@config,
                                           "#{@ardir}/packages/#{pack}",
                                           "packages/#{pack}")
        end
        with    = extract_selection(config('with'))
        without = extract_selection(config('without'))
        @selected = @installers.keys.select {|name|
                      (with.empty? or with.include?(name)) \
                          and not without.include?(name)
                    }
      end
    
      def extract_selection(list)
        a = list.split(/,/)
        a.each do |name|
          setup_rb_error "no such package: #{name}"  unless @installers.key?(name)
        end
        a
    
      def print_usage(f)
        super
        f.puts 'Inluded packages:'
        f.puts '  ' + @packages.sort.join(' ')
        f.puts
    
      def exec_config
        run_hook 'pre-config'
        each_selected_installers {|inst| inst.exec_config }
        run_hook 'post-config'
        @config.save   # must be final
      end
    
      def exec_setup
        run_hook 'pre-setup'
        each_selected_installers {|inst| inst.exec_setup }
        run_hook 'post-setup'
    
      def exec_install
        run_hook 'pre-install'
        each_selected_installers {|inst| inst.exec_install }
        run_hook 'post-install'
    
      def exec_test
        run_hook 'pre-test'
        each_selected_installers {|inst| inst.exec_test }
        run_hook 'post-test'
    
      def exec_clean
        rm_f @config.savefile
        run_hook 'pre-clean'
        each_selected_installers {|inst| inst.exec_clean }
        run_hook 'post-clean'
      end
    
      def exec_distclean
        rm_f @config.savefile
        run_hook 'pre-distclean'
        each_selected_installers {|inst| inst.exec_distclean }
        run_hook 'post-distclean'
    
      def each_selected_installers
        Dir.mkdir 'packages' unless File.dir?('packages')
        @selected.each do |pack|
          $stderr.puts "Processing the package `#{pack}' ..." if verbose?
          Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
          Dir.chdir "packages/#{pack}"
          yield @installers[pack]
          Dir.chdir '../..'
    
      def run_hook(id)
        @root_installer.run_hook id
    
      # module FileOperations requires this
      def verbose?
        @config.verbose?
    
      # module FileOperations requires this
      def no_harm?
        @config.no_harm?
      end
    
    end   # class ToplevelInstallerMulti
    
    
    class Installer
    
      FILETYPES = %w( bin lib ext data conf man )
    
      include FileOperations
      include HookScriptAPI
    
      def initialize(config, srcroot, objroot)
        @config = config
        @srcdir = File.expand_path(srcroot)
        @objdir = File.expand_path(objroot)
        @currdir = '.'
    
      def inspect
        "#<#{self.class} #{File.basename(@srcdir)}>"
    
      # module FileOperations requires this
      def verbose?
        @config.verbose?
      end
    
      # module FileOperations requires this
      def no_harm?
        @config.no_harm?
    
      def verbose_off
        begin
          save, @config.verbose = @config.verbose?, false
          yield
        ensure
          @config.verbose = save
        end
      end
    
      def exec_config
        exec_task_traverse 'config'
      end
    
      alias config_dir_bin noop
      alias config_dir_lib noop
    
      def config_dir_ext(rel)
        extconf if extdir?(curr_srcdir())
    
      alias config_dir_data noop
      alias config_dir_conf noop
      alias config_dir_man noop
    
      def extconf
        ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
      end
    
      #
      # TASK setup
      #
    
      def exec_setup
        exec_task_traverse 'setup'
      end
    
      def setup_dir_bin(rel)
        files_of(curr_srcdir()).each do |fname|
          update_shebang_line "#{curr_srcdir()}/#{fname}"
        end
    
      alias setup_dir_lib noop
    
      def setup_dir_ext(rel)
        make if extdir?(curr_srcdir())
      end
    
      alias setup_dir_data noop
      alias setup_dir_conf noop
      alias setup_dir_man noop
    
      def update_shebang_line(path)
        return if no_harm?
        return if config('shebang') == 'never'
        old = Shebang.load(path)
        if old
          $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
          new = new_shebang(old)
          return if new.to_s == old.to_s
        else
          return unless config('shebang') == 'all'
          new = Shebang.new(config('rubypath'))
        end
        $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
        open_atomic_writer(path) {|output|
          File.open(path, 'rb') {|f|
            f.gets if old   # discard
            output.puts new.to_s
            output.print f.read
          }
        }
      end
    
      def new_shebang(old)
        if /\Aruby/ =~ File.basename(old.cmd)
          Shebang.new(config('rubypath'), old.args)
        elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
          Shebang.new(config('rubypath'), old.args[1..-1])
    
          return old unless config('shebang') == 'all'
          Shebang.new(config('rubypath'))
    
      def open_atomic_writer(path, &block)
        tmpfile = File.basename(path) + '.tmp'
    
          File.open(tmpfile, 'wb', &block)
          File.rename tmpfile, File.basename(path)
        ensure
          File.unlink tmpfile if File.exist?(tmpfile)
        end
      end
    
      class Shebang
        def Shebang.load(path)
          line = nil
          File.open(path) {|f|
            line = f.gets
          }
          return nil unless /\A#!/ =~ line
          parse(line)
        end
    
        def Shebang.parse(line)
          cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
          new(cmd, args)
        end
    
        def initialize(cmd, args = [])
          @cmd = cmd
          @args = args
        end
    
        attr_reader :cmd
        attr_reader :args
    
        def to_s
          "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
    
      def exec_install
        rm_f 'InstalledFiles'
        exec_task_traverse 'install'
      end
    
      def install_dir_bin(rel)
        install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
      end
    
      def install_dir_lib(rel)
        install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
      end
    
      def install_dir_ext(rel)
        return unless extdir?(curr_srcdir())
        install_files rubyextentions('.'),
                      "#{config('sodir')}/#{File.dirname(rel)}",
                      0555
      end
    
      def install_dir_data(rel)
        install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
      end
    
      def install_dir_conf(rel)
        # FIXME: should not remove current config files
        # (rename previous file to .old/.org)
        install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
      end
    
      def install_dir_man(rel)
        install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
      end
    
      def install_files(list, dest, mode)
        mkdir_p dest, @config.install_prefix
        list.each do |fname|
          install fname, dest, mode, @config.install_prefix
        end
    
      def libfiles
        glob_reject(%w(*.y *.output), targetfiles())
      end
    
      def rubyextentions(dir)
        ents = glob_select("*.#{@config.dllext}", targetfiles())
        if ents.empty?
          setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
    
      def targetfiles
        mapdir(existfiles() - hookfiles())
      end
    
      def mapdir(ents)
        ents.map {|ent|
          if File.exist?(ent)
          then ent                         # objdir
          else "#{curr_srcdir()}/#{ent}"   # srcdir
    
      # picked up many entries from cvs-1.11.1/src/ignore.c
      JUNK_FILES = %w( 
        core RCSLOG tags TAGS .make.state
        .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
        *~ *.old *.bak *.BAK *.orig *.rej _$* *$
    
        *.org *.in .*
      )
    
      def existfiles
        glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
    
      def hookfiles
        %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
          %w( config setup install clean ).map {|t| sprintf(fmt, t) }
        }.flatten
      end
    
      def glob_select(pat, ents)
        re = globs2re([pat])
        ents.select {|ent| re =~ ent }
      end
    
      def glob_reject(pats, ents)
        re = globs2re(pats)
        ents.reject {|ent| re =~ ent }
      end
    
      GLOB2REGEX = {
        '.' => '\.',
        '$' => '\$',
        '#' => '\#',
        '*' => '.*'
      }
    
      def globs2re(pats)
        /\A(?:#{
          pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
        })\z/
      end
    
      #
      # TASK test
      #
    
      TESTDIR = 'test'
    
      def exec_test
        unless File.directory?('test')
          $stderr.puts 'no test in this package' if verbose?
          return
        end
        $stderr.puts 'Running tests...' if verbose?
        begin
          require 'test/unit'
        rescue LoadError
          setup_rb_error 'test/unit cannot loaded.  You need Ruby 1.8 or later to invoke this task.'
    
        runner = Test::Unit::AutoRunner.new(true)
        runner.to_run << TESTDIR
        runner.run
      end
    
      def exec_clean
        exec_task_traverse 'clean'
        rm_f @config.savefile
        rm_f 'InstalledFiles'
      end
    
      alias clean_dir_bin noop
      alias clean_dir_lib noop
      alias clean_dir_data noop
      alias clean_dir_conf noop
      alias clean_dir_man noop
    
      def clean_dir_ext(rel)
        return unless extdir?(curr_srcdir())
        make 'clean' if File.file?('Makefile')
    
      def exec_distclean
        exec_task_traverse 'distclean'
        rm_f @config.savefile
        rm_f 'InstalledFiles'
      end
    
      alias distclean_dir_bin noop
      alias distclean_dir_lib noop
    
      def distclean_dir_ext(rel)
        return unless extdir?(curr_srcdir())
        make 'distclean' if File.file?('Makefile')
    
      alias distclean_dir_data noop
      alias distclean_dir_conf noop
      alias distclean_dir_man noop
    
    
      def exec_task_traverse(task)
        run_hook "pre-#{task}"
        FILETYPES.each do |type|
          if type == 'ext' and config('without-ext') == 'yes'
            $stderr.puts 'skipping ext/* by user option' if verbose?
            next
    
          traverse task, type, "#{task}_dir_#{type}"
    
      def traverse(task, rel, mid)
        dive_into(rel) {
          run_hook "pre-#{task}"
          __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
          directories_of(curr_srcdir()).each do |d|
            traverse task, "#{rel}/#{d}", mid
          end
          run_hook "post-#{task}"
        }
      end
    
      def dive_into(rel)
        return unless File.dir?("#{@srcdir}/#{rel}")
    
        dir = File.basename(rel)
        Dir.mkdir dir unless File.dir?(dir)
        prevdir = Dir.pwd
        Dir.chdir dir
        $stderr.puts '---> ' + rel if verbose?
        @currdir = rel
        yield
        Dir.chdir prevdir
        $stderr.puts '<--- ' + rel if verbose?
        @currdir = File.dirname(rel)
      end
    
      def run_hook(id)
        path = [ "#{curr_srcdir()}/#{id}",
                 "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
        return unless path
        begin
          instance_eval File.read(path), path, 1
        rescue
          raise if $DEBUG
          setup_rb_error "hook #{path} failed:\n" + $!.message
        end
      end
    
    end   # class Installer
    
    
    class SetupError < StandardError; end
    
    def setup_rb_error(msg)
      raise SetupError, msg
    end
    
    
    if $0 == __FILE__
      begin
        ToplevelInstaller.invoke
    
        raise if $DEBUG
        $stderr.puts $!.message
        $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
        exit 1
      end
    end