diff options
Diffstat (limited to 'install')
| -rw-r--r-- | install | 3088 | 
1 files changed, 3088 insertions, 0 deletions
| diff --git a/install b/install new file mode 100644 index 00000000..5858a748 --- /dev/null +++ b/install @@ -0,0 +1,3088 @@ +#!/usr/bin/env ruby + +# install - Monolithic rant script, autogenerated by rant-import 0.5.8. +# +# Copyright (C) 2005 Stefan Lang <langstefan@gmx.at> +# +# 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. + + +require 'getoptlong' + + +require 'rbconfig' + +unless Process::Status.method_defined?(:success?) # new in 1.8.2 +    class Process::Status +        def success?; exitstatus == 0; end +    end +end +unless Regexp.respond_to? :union # new in 1.8.1 +    def Regexp.union(*patterns) +        return /(?!)/ if patterns.empty? +        Regexp.new(patterns.join("|")) +    end +end +if RUBY_VERSION < "1.8.2" +    class Array +        undef_method :flatten, :flatten! +        def flatten +            cp = self.dup +            cp.flatten! +            cp +        end +        def flatten! +            res = [] +            flattened = false +            self.each { |e| +                if e.respond_to? :to_ary +                    res.concat(e.to_ary) +                    flattened = true +                else +                    res << e +                end +            } +            if flattened +                replace(res) +                flatten! +                self +            end +        end +    end +end + +class String +    def _rant_sub_ext(ext, new_ext = nil) +        if new_ext +            self.sub(/#{Regexp.escape ext}$/, new_ext) +        else +            self.sub(/(\.[^.]*$)|$/, ".#{ext}") +        end +    end +end + +module Rant +    VERSION = '0.5.8' + +    @__rant_no_value__ = Object.new.freeze +    def self.__rant_no_value__ +	@__rant_no_value__ +    end + +    module Env +        OS = ::Config::CONFIG['target'] +        RUBY = ::Config::CONFIG['ruby_install_name'] +        RUBY_BINDIR = ::Config::CONFIG['bindir'] +        RUBY_EXE = File.join(RUBY_BINDIR, RUBY + ::Config::CONFIG["EXEEXT"]) + +        @@zip_bin = false +        @@tar_bin = false + +        if OS =~ /mswin/i +            def on_windows?; true; end +        else +            def on_windows?; false; end +        end + +        def have_zip? +            if @@zip_bin == false +                @@zip_bin = find_bin "zip" +            end +            !@@zip_bin.nil? +        end +        def have_tar? +            if @@tar_bin == false +                @@tar_bin = find_bin "tar" +            end +            !@@tar_bin.nil? +        end +        def pathes +            path = ENV[on_windows? ? "Path" : "PATH"] +            return [] unless path +            path.split(on_windows? ? ";" : ":") +        end +        def find_bin bin_name +            if on_windows? +                bin_name_exe = nil +                if bin_name !~ /\.[^\.]{1,3}$/i +                    bin_name_exe = bin_name + ".exe" +                end +                pathes.each { |dir| +                    file = File.join(dir, bin_name) +                    return file if test(?f, file) +                    if bin_name_exe +                        file = File.join(dir, bin_name_exe) +                        return file if test(?f, file) +                    end +                } +            else +                pathes.each { |dir| +                    file = File.join(dir, bin_name) +                    return file if test(?x, file) +                } +            end +            nil +        end +        def shell_path path +            if on_windows? +                path = path.tr("/", "\\") +                if path.include? ' ' +                    '"' + path + '"' +                else +                    path +                end +            else +                if path.include? ' ' +                    "'" + path + "'" +                else +                    path +                end +            end +        end +        extend self +    end # module Env + +    module Sys +	def sp(arg) +            if arg.respond_to? :to_ary +                arg.to_ary.map{ |e| sp e }.join(' ') +            else +                _escaped_path arg +            end +	end +        def escape(arg) +            if arg.respond_to? :to_ary +                arg.to_ary.map{ |e| escape e }.join(' ') +            else +                _escaped arg +            end +        end +        if Env.on_windows? +            def _escaped_path(path) +		_escaped(path.to_s.tr("/", "\\")) +            end +            def _escaped(arg) +		sarg = arg.to_s +		return sarg unless sarg.include?(" ") +		sarg << "\\" if sarg[-1].chr == "\\" +                "\"#{sarg}\"" +            end +            def regular_filename(fn) +                fn.to_str.tr("\\", "/").gsub(%r{/{2,}}, "/") +            end +        else +            def _escaped_path(path) +                path.to_s.gsub(/(?=\s)/, "\\") +            end +            alias _escaped _escaped_path +            def regular_filename(fn) +                fn.to_str.gsub(%r{/{2,}}, "/") +            end +        end +        private :_escaped_path +        private :_escaped +	def split_all(path) +            names = regular_filename(path).split(%r{/}) +            names[0] = "/" if names[0] && names[0].empty? +            names +	end +        extend self +    end # module Sys + + +    ROOT_RANTFILE = "root.rant" +    SUB_RANTFILE = "sub.rant" +    RANTFILES = [ "Rantfile", "rantfile", ROOT_RANTFILE ] +     +    CODE_IMPORTS = [] +     +    class RantAbortException < StandardError +    end + +    class RantDoneException < StandardError +    end + +    class Error < StandardError +    end + +    module Generators +    end + +    module RantVar + +	class Error < Rant::Error +	end + +	class ConstraintError < Error + +	    attr_reader :constraint, :val + +	    def initialize(constraint, val, msg = nil) +		@msg = msg +		@constraint = constraint +		@val = val +	    end + +	    def message +		val_desc = @val.inspect +		val_desc[7..-1] = "..." if val_desc.length > 10 +		"#{val_desc} doesn't match constraint: #@constraint" +	    end +	end + +	class NotAConstraintFactoryError < Error +	    attr_reader :obj +	    def initialize(obj, msg = nil) +		@msg = msg +		@obj = obj +	    end +	    def message +		obj_desc = @obj.inspect +		obj_desc[7..-1] = "..." if obj_desc.length > 10 +		"#{obj_desc} is not a valid constraint factory" +	    end +	end + +	class InvalidVidError < Error +	    def initialize(vid, msg = nil) +		@msg = msg +		@vid = vid +	    end +	    def message +		vid_desc = @vid.inspect +		vid_desc[7..-1] = "..." if vid_desc.length > 10 +		"#{vid_desc} is not a valid var identifier" +	    end +	end + +	class InvalidConstraintError < Error +	end + +	class QueryError < Error +	end + +	class Space + +	    @@env_ref = Object.new + +	    def initialize +		@store = {} +		@constraints = {} +	    end + +	    def query(*args, &block) +		case args.size +		when 0 +		    raise QueryError, "no arguments", caller +		when 1 +		    arg = args.first +		    if Hash === arg +			if arg.size == 1 +			    arg.each { |k,v| +				self[k] = v if self[k].nil? +			    } +			    self +			else +			    init_all arg +			end +		    else +			self[arg] +		    end +		when 2, 3 +		    vid, cf, val = *args +                    constrain vid, +                        get_factory(cf).rant_constraint +		    self[vid] = val if val +		else +		    raise QueryError, "too many arguments" +		end +	    end + +	    def restrict vid, ct, *ct_args +		if vid.respond_to? :to_ary +		    vid.to_ary.each { |v| restrict(v, ct, *ct_args) } +		else +		    constrain vid, +			get_factory(ct).rant_constraint(*ct_args) +		end +		self +	    end + +	    def get_factory id +		if String === id || Symbol === id +                    id = Constraints.const_get(id) rescue nil +		end +		unless id.respond_to? :rant_constraint +                    raise NotAConstraintFactoryError.new(id), caller +		end +		id +	    end +	    private :get_factory + +	    def [](vid) +		vid = RantVar.valid_vid vid +		val = @store[vid] +		val.equal?(@@env_ref) ? ENV[vid] : val +	    end + +	    def []=(vid, val) +		vid = RantVar.valid_vid(vid) +		c = @constraints[vid] +		if @store[vid] == @@env_ref +		    ENV[vid] = c ? c.filter(val) : val +		else +		    @store[vid] = c ? c.filter(val) : val +		end +	    end + +	    def env(*vars) +		vars.flatten.each { |var| +		    vid = RantVar.valid_vid(var) +		    cur_val = @store[vid] +		    next if cur_val == @@env_ref +		    ENV[vid] = cur_val unless cur_val.nil? +		    @store[vid] = @@env_ref +		} +		nil +	    end + +	    def set_all hash +		unless Hash === hash +		    raise QueryError, +			"set_all argument has to be a hash" +		end +		hash.each_pair { |k, v| +		    self[k] = v +		} +	    end + +	    def init_all hash +		unless Hash === hash +		    raise QueryError, +			"init_all argument has to be a hash" +		end +		hash.each_pair { |k, v| +		    self[k] = v if self[k].nil? +		} +	    end + +	    def constrain vid, constraint +		vid = RantVar.valid_vid(vid) +		unless RantVar.valid_constraint? constraint +		    raise InvalidConstraintError, constraint +		end +		@constraints[vid] = constraint +		if @store.member? vid +		    begin +			val = @store[vid] +			@store[vid] = constraint.filter(@store[vid]) +		    rescue +			@store[vid] = constraint.default +			raise ConstraintError.new(constraint, val) +		    end +		else +		    @store[vid] = constraint.default +		end +	    end + +	    def has_var?(vid) +		!self[vid].nil? +	    end + +            def _set(vid, val) #:nodoc: +                @store[vid] = val +            end + +            def _get(vid) #:nodoc: +                @store[vid] +            end + +            def _init(vid, val) #:nodoc: +                @store[vid] ||= val +            end + +	end	# class Space + +	module Constraint +	    def matches? val +		filter val +		true +	    rescue +		return false +	    end +	end + +	def valid_vid(obj) +	    case obj +	    when String: obj +	    when Symbol: obj.to_s +	    else +		if obj.respond_to? :to_str +		    obj.to_str +		else +		    raise InvalidVidError.new(obj) +		end +	    end +	end +	 +	def valid_constraint?(obj) +	    obj.respond_to?(:filter) && +		obj.respond_to?(:matches?) && +		obj.respond_to?(:default) +	end + +	module_function :valid_constraint?, :valid_vid + +	module Constraints +	    class AutoList +		include Constraint +		class << self +		    alias rant_constraint new +		end +		def filter(val) +		    if val.respond_to? :to_ary +			val.to_ary +		    elsif val.nil? +			raise ConstraintError.new(self, val) +		    else +			[val] +		    end +		end +		def default +		    [] +		end +		def to_s +		    "list or single, non-nil value" +		end +	    end +        end # module Constraints +    end # module RantVar +end # module Rant + + +require 'fileutils' + + +module Rant +    def FileList(arg) +        if arg.respond_to?(:to_rant_filelist) +            arg.to_rant_filelist +        elsif arg.respond_to?(:to_ary) +            FileList.new(arg.to_ary) +        else +            raise TypeError, +                "cannot convert #{arg.class} into Rant::FileList" +        end +    end +    module_function :FileList +    class FileList +        include Enumerable + +        ESC_SEPARATOR = Regexp.escape(File::SEPARATOR) +        ESC_ALT_SEPARATOR = File::ALT_SEPARATOR ? +            Regexp.escape(File::ALT_SEPARATOR) : nil + +        class << self +            def [](*patterns) +                new.hide_dotfiles.include(*patterns) +            end +            def glob(*patterns) +                fl = new.hide_dotfiles.ignore(".", "..").include(*patterns) +                if block_given? then yield fl else fl end +            end +            def glob_all(*patterns) +                fl = new.ignore(".", "..").include(*patterns) +                if block_given? then yield fl else fl end +            end +        end + +        def initialize(store = []) +            @pending = false +            @def_glob_dotfiles = true +            @items = store +            @ignore_rx = nil +            @keep = {} +            @actions = [] +        end +        alias _object_dup dup +        private :_object_dup +        def dup +            c = _object_dup +            c.items = @items.dup +            c.actions = @actions.dup +            c.ignore_rx = @ignore_rx.dup if @ignore_rx +            c.instance_variable_set(:@keep, @keep.dup) +            c +        end +        def copy +            c = _object_dup +            c.items = @items.map { |entry| entry.dup } +            c.actions = @actions.dup +            c.ignore_rx = @ignore_rx.dup if @ignore_rx +            h_keep = {} +            @keep.each_key { |entry| h_keep[entry] = true } +            c.instance_variable_set(:@keep, h_keep) +            c +        end +        def glob_dotfiles? +            @def_glob_dotfiles +        end +        def glob_dotfiles=(flag) +            @def_glob_dotfiles = flag ? true : false +        end +        def hide_dotfiles +            @def_glob_dotfiles = false +            self +        end +        def glob_dotfiles +            @def_glob_dotfiles = true +            self +        end + +        protected +        attr_accessor :actions, :items +        attr_accessor :pending +        attr_accessor :ignore_rx + +        public +        def each(&block) +            resolve if @pending +            @items.each(&block) +            self +        end +        def to_ary +            resolve if @pending +            @items +        end +        alias to_a to_ary +        alias entries to_ary    # entries: defined in Enumerable +        def to_rant_filelist +            self +        end +        def +(other) +            if other.respond_to? :to_rant_filelist +                c = other.to_rant_filelist.dup +                c.actions.concat(@actions) +                c.items.concat(@items) +                c.pending = !c.actions.empty? +                c +            elsif other.respond_to? :to_ary +                c = dup +                c.actions << +                    [:apply_ary_method_1, :concat, other.to_ary.dup] +                c.pending = true +                c +            else +                raise TypeError, +                    "cannot add #{other.class} to Rant::FileList" +            end +        end +        def <<(file) +            @actions << [:apply_ary_method_1, :push, file] +            @keep[file] = true +            @pending = true +            self +        end +        def keep(entry) +            @keep[entry] = true +            @items << entry +            self +        end +        def concat(ary) +            if @pending +                ary = ary.to_ary.dup +                @actions << [:apply_ary_method_1, :concat, ary] +            else +                ix = ignore_rx and ary = ary.to_ary.reject { |f| f =~ ix } +                @items.concat(ary) +            end +            self +        end +        def size +            resolve if @pending +            @items.size +        end +        alias length size +        def empty? +            resolve if @pending +            @items.empty? +        end +        def join(sep = ' ') +            resolve if @pending +            @items.join(sep) +        end +        def pop +            resolve if @pending +            @items.pop +        end +        def push(entry) +            resolve if @pending +            @items.push(entry) if entry !~ ignore_rx +            self +        end +        def shift +            resolve if @pending +            @items.shift +        end +        def unshift(entry) +            resolve if @pending +            @items.unshift(entry) if entry !~ ignore_rx +            self +        end +if Object.method_defined?(:fcall) || Object.method_defined?(:funcall) # in Ruby 1.9 like __send__ +        @@__send_private__ = Object.method_defined?(:fcall) ? :fcall : :funcall +        def resolve +            @pending = false +            @actions.each{ |action| self.__send__(@@__send_private__, *action) }.clear +            ix = ignore_rx +            if ix +                @items.reject! { |f| f =~ ix && !@keep[f] } +            end +            self +        end +else +        def resolve +            @pending = false +            @actions.each{ |action| self.__send__(*action) }.clear +            ix = ignore_rx +            if ix +                @items.reject! { |f| f =~ ix && !@keep[f] } +            end +            self +        end +end +        def include(*pats) +            @def_glob_dotfiles ? glob_all(*pats) : glob_unix(*pats) +        end +        alias glob include +        def glob_unix(*patterns) +            patterns.flatten.each { |pat| +                @actions << [:apply_glob_unix, pat] +            } +            @pending = true +            self +        end +        def glob_all(*patterns) +            patterns.flatten.each { |pat| +                @actions << [:apply_glob_all, pat] +            } +            @pending = true +            self +        end +        if RUBY_VERSION < "1.8.2" +            FN_DOTFILE_RX_ = ESC_ALT_SEPARATOR ? +                /(^|(#{ESC_SEPARATOR}|#{ESC_ALT_SEPARATOR})+)\..* +                    ((#{ESC_SEPARATOR}|#{ESC_ALT_SEPARATOR})+|$)/x : +                /(^|#{ESC_SEPARATOR}+)\..* (#{ESC_SEPARATOR}+|$)/x +            def apply_glob_unix(pattern) +                inc_files = Dir.glob(pattern) +                unless pattern =~ /(^|\/)\./ +                    inc_files.reject! { |fn| fn =~ FN_DOTFILE_RX_ } +                end +                @items.concat(inc_files) +            end +        else +            def apply_glob_unix(pattern) +                @items.concat(Dir.glob(pattern)) +            end +        end +        private :apply_glob_unix +        def apply_glob_all(pattern) +            @items.concat(Dir.glob(pattern, File::FNM_DOTMATCH)) +        end +        private :apply_glob_all +        def exclude(*patterns) +            patterns.each { |pat| +                if Regexp === pat +                    @actions << [:apply_exclude_rx, pat] +                else +                    @actions << [:apply_exclude, pat] +                end +            } +            @pending = true +            self +        end +        def ignore(*patterns) +            patterns.each { |pat| +                add_ignore_rx(Regexp === pat ? pat : mk_all_rx(pat)) +            } +            @pending = true +            self +        end +        def add_ignore_rx(rx) +            @ignore_rx = +            if @ignore_rx +                Regexp.union(@ignore_rx, rx) +            else +                rx +            end +        end +        private :add_ignore_rx +        def apply_exclude(pattern) +            @items.reject! { |elem| +                File.fnmatch?(pattern, elem, File::FNM_DOTMATCH) && !@keep[elem] +            } +        end +        private :apply_exclude +        def apply_exclude_rx(rx) +            @items.reject! { |elem| +                elem =~ rx && !@keep[elem] +            } +        end +        private :apply_exclude_rx +        def exclude_name(*names) +            names.each { |name| +                @actions << [:apply_exclude_rx, mk_all_rx(name)] +            } +            @pending = true +            self +        end +        alias shun exclude_name +        if File::ALT_SEPARATOR +            def mk_all_rx(file) +                /(^|(#{ESC_SEPARATOR}|#{ESC_ALT_SEPARATOR})+)#{Regexp.escape(file)} +                    ((#{ESC_SEPARATOR}|#{ESC_ALT_SEPARATOR})+|$)/x +            end +        else +            def mk_all_rx(file) +                /(^|#{ESC_SEPARATOR}+)#{Regexp.escape(file)} +                    (#{ESC_SEPARATOR}+|$)/x +            end +        end +        private :mk_all_rx +        def exclude_path(*patterns) +            patterns.each { |pat| +                @actions << [:apply_exclude_path, pat] +            } +            @pending = true +            self +        end +        def apply_exclude_path(pattern) +            flags = File::FNM_DOTMATCH|File::FNM_PATHNAME +            @items.reject! { |elem| +                File.fnmatch?(pattern, elem, flags) && !@keep[elem] +            } +        end +        private :apply_exclude +        def select(&block) +            d = dup +            d.actions << [:apply_select, block] +            d.pending = true +            d +        end +        alias find_all select +        def apply_select blk +            @items = @items.select(&blk) +        end +        private :apply_select +        def map(&block) +            d = dup +            d.actions << [:apply_ary_method, :map!, block] +            d.pending = true +            d +        end +        alias collect map +        def sub_ext(ext, new_ext=nil) +            map { |f| f._rant_sub_ext ext, new_ext } +        end +        def ext(ext_str) +            sub_ext(ext_str) +        end +        def arglist +            Rant::Sys.sp to_ary +        end +        alias to_s arglist +        alias object_inspect inspect +        def uniq! +            @actions << [:apply_ary_method, :uniq!] +            @pending = true +            self +        end +        def sort! +            @actions << [:apply_ary_method, :sort!] +            @pending = true +            self +        end +        def map!(&block) +            @actions << [:apply_ary_method, :map!, block] +            @pending = true +            self +        end +        def reject!(&block) +            @actions << [:apply_ary_method, :reject!, block] +            @pending = true +            self +        end +        private +        def apply_ary_method(meth, block=nil) +            @items.send meth, &block +        end +        def apply_ary_method_1(meth, arg1, block=nil) +            @items.send meth, arg1, &block +        end +    end # class FileList +end # module Rant + +if RUBY_VERSION == "1.8.3" +    module FileUtils +        METHODS = singleton_methods - %w(private_module_function +            commands options have_option? options_of collect_method) +        module Verbose +            class << self +                public(*::FileUtils::METHODS) +            end +            public(*::FileUtils::METHODS) +        end +    end +end + +if RUBY_VERSION < "1.8.1" +    module FileUtils +        undef_method :fu_list +        def fu_list(arg) +            arg.respond_to?(:to_ary) ? arg.to_ary : [arg] +        end +    end +end + +module Rant +    class RacFileList < FileList + +	attr_reader :subdir +	attr_reader :basedir + +	def initialize(rac, store = []) +	    super(store) +	    @rac = rac +	    @subdir = @rac.current_subdir +	    @basedir = Dir.pwd +	    @ignore_hash = nil +            @add_ignore_args = [] +	    update_ignore_rx +	end +        def dup +            c = super +            c.instance_variable_set( +                :@add_ignore_args, @add_ignore_args.dup) +            c +        end +        def copy +            c = super +            c.instance_variable_set( +                :@add_ignore_args, @add_ignore_args.map { |e| e.dup }) +            c +        end +        alias filelist_ignore ignore +        def ignore(*patterns) +            @add_ignore_args.concat patterns +            self +        end +	def ignore_rx +	    update_ignore_rx +	    @ignore_rx +	end +	alias filelist_resolve resolve +	def resolve +	    Sys.cd(@basedir) { filelist_resolve } +	end +	def each_cd(&block) +	    old_pwd = Dir.pwd +	    Sys.cd(@basedir) +	    filelist_resolve if @pending +	    @items.each(&block) +	ensure +	    Sys.cd(old_pwd) +	end +	private +	def update_ignore_rx +	    ri = @rac.var[:ignore] +            ri = ri ? (ri + @add_ignore_args) : @add_ignore_args +	    rh = ri.hash +	    unless rh == @ignore_hash +		@ignore_rx = nil +		filelist_ignore(*ri) +		@ignore_hash = rh +	    end +	end +    end	# class RacFileList + +    class MultiFileList + +	attr_reader :cur_list +	 +	def initialize(rac) +	    @rac = rac +	    @cur_list = RacFileList.new(@rac) +	    @lists = [@cur_list] +	end + +	def each_entry(&block) +	    @lists.each { |list| +		list.each_cd(&block) +	    } +	end + +	def add(filelist) +	    @cur_list = filelist +	    @lists << filelist +	    self +	end + +	def method_missing(sym, *args, &block) +	    if @cur_list && @cur_list.respond_to?(sym) +		if @cur_list.subdir == @rac.current_subdir +		    @cur_list.send(sym, *args, &block) +		else +		    add(RacFileList.new(@rac)) +		    @cur_list.send(sym, *args, &block) +		end +	    else +		super +	    end +	end +    end	# class MultiFileList + +    class CommandError < StandardError +	attr_reader :cmd +	attr_reader :status +	def initialize(cmd, status=nil, msg=nil) +	    @msg = msg +	    @cmd = cmd +	    @status = status +	end +	def message +	    if !@msg && cmd +		if status +		    "Command failed with status #{status.exitstatus}:\n" + +		    "[#{cmd}]" +		else +		    "Command failed:\n[#{cmd}]" +		end +	    else +		@msg +	    end +	end +    end + +    module Sys +	include ::FileUtils::Verbose + +	@symlink_supported = true +	class << self +	    attr_accessor :symlink_supported +	end + +	def fu_output_message(msg)	#:nodoc: +	end +        private :fu_output_message + +        def fu_each_src_dest(src, *rest) +            src = src.to_ary if src.respond_to? :to_ary +            super(src, *rest) +        end +        private :fu_each_src_dest + +	def sh(*cmd_args, &block) +	    cmd_args.flatten! +	    cmd = cmd_args.join(" ") +	    fu_output_message cmd +            success = system(*cmd_args) +	    if block_given? +                block[$?] +            elsif !success +		raise CommandError.new(cmd, $?) +	    end +	end + +	def ruby(*args, &block) +            if args.empty? +                sh(Env::RUBY_EXE, '', &block) +            else +                sh(args.unshift(Env::RUBY_EXE), &block) +            end +	end +        def cd(dir, &block) +            fu_output_message "cd #{dir}" +            orig_pwd = Dir.pwd +            Dir.chdir dir +            if block +                begin +                    block.arity == 0 ? block.call : block.call(Dir.pwd) +                ensure +                    fu_output_message "cd -" +                    Dir.chdir orig_pwd +                end +            else +                self +            end +        end + +	def safe_ln(src, dest) +            dest = dest.to_str +            src = src.respond_to?(:to_ary) ? src.to_ary : src.to_str +	    unless Sys.symlink_supported +		cp(src, dest) +	    else +		begin +		    ln(src, dest) +		rescue Exception # SystemCallError # Errno::EOPNOTSUPP +		    Sys.symlink_supported = false +		    cp(src, dest) +		end +	    end +	end + +        def ln_f(src, dest) +            ln(src, dest, :force => true) +        end + +        def split_path(str) +            str.split(Env.on_windows? ? ";" : ":") +        end + +        if Env.on_windows? +            def root_dir?(path) +                path == "/" || path == "\\" || +                    path =~ %r{\A[a-zA-Z]+:(\\|/)\Z} +            end +            def absolute_path?(path) +                path =~ %r{\A([a-zA-Z]+:)?(/|\\)} +            end +        else +            def root_dir?(path) +                path == "/" +            end +            def absolute_path?(path) +                path =~ %r{\A/} +            end +        end + +        extend self + +        if RUBY_VERSION >= "1.8.4"  # needed by 1.9.0, too +            class << self +                public(*::FileUtils::METHODS) +            end +            public(*::FileUtils::METHODS) +        end + +    end	# module Sys + +    class SysObject +	include Sys +	def initialize(rant) +	    @rant = rant or +		raise ArgumentError, "rant application required" +	end +        def ignore(*patterns) +            @rant.var[:ignore].concat(patterns) +            nil +        end +        def filelist(arg = Rant.__rant_no_value__) +            if Rant.__rant_no_value__.equal?(arg) +                RacFileList.new(@rant) +            elsif arg.respond_to?(:to_rant_filelist) +                arg.to_rant_filelist +            elsif arg.respond_to?(:to_ary) +                RacFileList.new(@rant, arg.to_ary) +            else +                raise TypeError, +                    "cannot convert #{arg.class} into Rant::FileList" +            end +        end +	def [](*patterns) +	    RacFileList.new(@rant).hide_dotfiles.include(*patterns) +	end +	def glob(*patterns, &block) +	    fl = RacFileList.new(@rant).hide_dotfiles.include(*patterns) +            fl.ignore(".", "..") +            if block_given? then yield fl else fl end +	end +        def glob_all(*patterns, &block) +	    fl = RacFileList.new(@rant).include(*patterns) +            fl.ignore(".", "..") # use case: "*.*" as pattern +            if block_given? then yield fl else fl end +        end +        def expand_path(path) +            File.expand_path(@rant.project_to_fs_path(path)) +        end +	private +	def fu_output_message(cmd) +	    @rant.cmd_msg cmd +	end +    end + + +    class TaskFail < StandardError +	def initialize(task, orig, msg) +	    @task = task +	    @orig = orig +            @msg = msg +	end +        def exception +            self +        end +	def task +	    @task +	end +	def tname +	    @task ? @task.name : nil +	end +	def orig +	    @orig +	end +        def msg +            @msg +        end +    end + +    class Rantfile +	attr_reader :tasks, :path +	attr_accessor :project_subdir +	def initialize(path) +	    @path = path or raise ArgumentError, "path required" +	    @tasks = [] +	    @project_subdir = nil +	end +        alias to_s path +        alias to_str path +    end	# class Rantfile + +    module Node + +	INVOKE_OPT = {}.freeze + +	T0 = Time.at(0).freeze + +	attr_reader :name +	attr_reader :rac +	attr_accessor :description +	attr_accessor :rantfile +	attr_accessor :line_number +        attr_accessor :project_subdir +	 +	def initialize +	    @description = nil +	    @rantfile = nil +	    @line_number = nil +	    @run = false +            @project_subdir = "" +	    @success = nil +	end + +        def reference_name +            sd = rac.current_subdir +            case sd +            when "": full_name +            when project_subdir: name +            else "@#{full_name}".sub(/^@#{Regexp.escape sd}\//, '') +            end +        end + +        alias to_s reference_name +        alias to_rant_target name + +	def full_name +	    sd = project_subdir +	    sd.empty? ? name : File.join(sd, name) +	end + +        def ch +            {:file => rantfile.to_str, :ln => line_number} +        end + +	def goto_task_home +	    @rac.goto_project_dir project_subdir +	end + +        def file_target? +            false +        end + +	def done? +	    @success +	end + +	def needed? +            invoke(:needed? => true) +	end + +	def run? +	    @run +	end + +	def invoke(opt = INVOKE_OPT) +	    return circular_dep if run? +	    @run = true +	    begin +		return !done? if opt[:needed?] +		self.run if !done? +                @success = true +	    ensure +		@run = false +	    end +	end + +	def fail msg = nil, orig = nil +            raise TaskFail.new(self, orig, msg) +	end + +	def each_target +	end + +        def has_actions? +            defined? @block and @block +        end + +        def dry_run +            text = "Executing #{name.dump}" +            text << " [NOOP]" unless has_actions? +            @rac.cmd_msg text +            action_descs.each { |ad| +                @rac.cmd_print "  - " +                @rac.cmd_msg ad.sub(/\n$/, '').gsub(/\n/, "\n    ") +            } +        end + +        private +	def run +	    goto_task_home +            return if @rac.running_task(self) +	    return unless has_actions? +            @receiver.pre_run(self) if defined? @receiver and @receiver +	    @block.arity == 0 ? @block.call : @block[self] if @block +	end + +        def action_descs +            descs = [] +            if defined? @receiver and @receiver +                descs.concat(@receiver.pre_action_descs) +            end +            @block ? descs << action_block_desc : descs +        end + +        def action_block_desc +            @block.inspect =~ /^#<Proc:[\da-z]+@(.+):(\d+)>$/i +            fn, ln = $1, $2 +            "Ruby Proc at #{fn.sub(/^#{Regexp.escape @rac.rootdir}\//, '')}:#{ln}" +        end + +	def circular_dep +	    rac.warn_msg "Circular dependency on task `#{full_name}'." +	    false +	end +    end	# module Node + + +    def self.init_import_nodes__default(rac, *rest) +        rac.node_factory = DefaultNodeFactory.new +    end + +    class DefaultNodeFactory +        def new_task(rac, name, pre, blk) +            Task.new(rac, name, pre, &blk) +        end +        def new_file(rac, name, pre, blk) +            FileTask.new(rac, name, pre, &blk) +        end +        def new_dir(rac, name, pre, blk) +            DirTask.new(rac, name, pre, &blk) +        end +        def new_source(rac, name, pre, blk) +            SourceNode.new(rac, name, pre, &blk) +        end +        def new_custom(rac, name, pre, blk) +            UserTask.new(rac, name, pre, &blk) +        end +        def new_auto_subfile(rac, name, pre, blk) +            AutoSubFileTask.new(rac, name, pre, &blk) +        end +    end + +    class Task +	include Node + +        attr_accessor :receiver + +	def initialize(rac, name, prerequisites = [], &block) +	    super() +	    @rac = rac or raise ArgumentError, "rac not given" +            @name = name or raise ArgumentError, "name not given" +	    @pre = prerequisites || [] +	    @pre_resolved = false +	    @block = block +	    @run = false +            @receiver = nil +	end + +	def prerequisites +	    @pre.collect { |pre| pre.to_s } +	end +	alias deps prerequisites + +	def source +	    @pre.first.to_s +	end + +	def has_actions? +            @block or @receiver && @receiver.has_pre_action? +	end + +	def <<(pre) +	    @pre_resolved = false +	    @pre << pre +	end + +	def invoked? +	    !@success.nil? +	end + +	def fail? +	    @success == false +	end + +	def enhance(deps = nil, &blk) +	    if deps +		@pre_resolved = false +		@pre.concat deps +	    end +	    if @block +		if blk +		    first_block = @block +		    @block = lambda { |t| +			first_block[t] +			blk[t] +		    } +		end +	    else +		@block = blk +	    end +	end + +	def invoke(opt = INVOKE_OPT) +	    return circular_dep if @run +	    @run = true +	    begin +		return if done? +		internal_invoke opt +	    ensure +		@run = false +	    end +	end + +	def internal_invoke(opt, ud_init = true) +	    goto_task_home +	    update = ud_init || opt[:force] +	    dep = nil +	    uf = false +	    each_dep { |dep| +		if dep.respond_to? :timestamp +		    handle_timestamped(dep, opt) && update = true +		elsif Node === dep +		    handle_node(dep, opt) && update = true +		else +		    dep, uf = handle_non_node(dep, opt) +		    uf && update = true +		    dep +		end +	    } +            if @receiver +                goto_task_home +                update = true if @receiver.update?(self) +            end +	    return update if opt[:needed?] +            run if update +	    @success = true +	    update +	rescue StandardError => e +	    @success = false +	    self.fail(nil, e) +	end +	private :internal_invoke + +	def handle_node(dep, opt) +	    dep.invoke opt +	end + +	def handle_timestamped(dep, opt) +	    dep.invoke opt +	end + +	def handle_non_node(dep, opt) +	    @rac.err_msg "Unknown task `#{dep}',", +		"referenced in `#{rantfile.path}', line #{@line_number}!" +	    self.fail +	end + +	def each_dep +	    t = nil +	    if @pre_resolved +		return @pre.each { |t| yield(t) } +	    end +	    my_full_name = full_name +	    my_project_subdir = project_subdir +	    @pre.map! { |t| +		if Node === t +		    if t.full_name == my_full_name +			nil +		    else +			yield(t) +			t +		    end +		else +		    t = t.to_s if Symbol === t +		    if t == my_full_name #TODO +			nil +		    else +			selection = @rac.resolve t, +					my_project_subdir +			if selection.empty? +			    yield(t) +			else +			    selection.each { |st| yield(st) } +			    selection +			end +		    end +		end +	    } +            if @pre.kind_of? Rant::FileList +                @pre.resolve +            else +                @pre.flatten! +                @pre.compact! +            end +	    @pre_resolved = true +	end +    end	# class Task + +    class UserTask < Task + +	def initialize(*args) +	    super +	    @block = nil +	    @needed = nil +            @target_files = nil +	    yield self if block_given? +	end + +	def act(&block) +	    @block = block +	end + +	def needed(&block) +	    @needed = block +	end + +        def file_target? +            @target_files and @target_files.include? @name +        end + +        def each_target(&block) +            goto_task_home +            @target_files.each(&block) if @target_files +        end +         +        def file_target(*args) +            args.flatten! +            args << @name if args.empty? +            if @target_files +                @target_files.concat(args) +            else +                @target_files = args +            end +        end +	 +	def invoke(opt = INVOKE_OPT) +	    return circular_dep if @run +	    @run = true +	    begin +		return if done? +		internal_invoke(opt, ud_init_by_needed) +	    ensure +		@run = false +	    end +	end + +	private +	def ud_init_by_needed +	    if @needed +		goto_task_home +		@needed.arity == 0 ? @needed.call : @needed[self] +	    end +	end +    end	# class UserTask + +    class FileTask < Task + +	def initialize(*args) +	    super +	    @ts = T0 +	end + +        def file_target? +            true +        end + +	def invoke(opt = INVOKE_OPT) +	    return circular_dep if @run +	    @run = true +	    begin +		return if done? +		goto_task_home +		if File.exist? @name +		    @ts = File.mtime @name +		    internal_invoke opt, false +		else +		    @ts = T0 +		    internal_invoke opt, true +		end +	    ensure +		@run = false +	    end +	end + +	def timestamp(opt = INVOKE_OPT) +	    File.exist?(@name) ? File.mtime(@name) : T0 +	end + +        def handle_node(dep, opt) +            return true if dep.file_target? && dep.invoke(opt) +	    if File.exist? dep.name +                File.mtime(dep.name) > @ts +            elsif !dep.file_target? +		@rac.err_msg @rac.pos_text(rantfile.path, line_number), +		    "in prerequisites: no such file: `#{dep.full_name}'" +		self.fail +	    end +        end + +	def handle_timestamped(dep, opt) +	    return true if dep.invoke opt +	    dep.timestamp(opt) > @ts +	end + +	def handle_non_node(dep, opt) +            goto_task_home # !!?? +	    unless File.exist? dep +		@rac.err_msg @rac.pos_text(rantfile.path, line_number), +		    "in prerequisites: no such file or task: `#{dep}'" +		self.fail +	    end +	    [dep, File.mtime(dep) > @ts] +	end + +	def each_target +	    goto_task_home +	    yield name +	end +    end	# class FileTask + +    module AutoInvokeDirNode +	private +	def run +            goto_task_home +            return if @rac.running_task(self) +	    dir = File.dirname(name) +            @rac.build dir unless dir == "." || dir == "/" +            return unless @block +            @block.arity == 0 ? @block.call : @block[self] +	end +    end + +    class AutoSubFileTask < FileTask +        include AutoInvokeDirNode +    end + +    class DirTask < Task + +	def initialize(*args) +	    super +	    @ts = T0 +	    @isdir = nil +	end + +	def invoke(opt = INVOKE_OPT) +	    return circular_dep if @run +	    @run = true +	    begin +		return if done? +		goto_task_home +		@isdir = test(?d, @name) +		if @isdir +		    @ts = @block ? test(?M, @name) : Time.now +		    internal_invoke opt, false +		else +		    @ts = T0 +		    internal_invoke opt, true +		end +	    ensure +		@run = false +	    end +	end + +        def file_target? +            true +        end + +        def handle_node(dep, opt) +            return true if dep.file_target? && dep.invoke(opt) +	    if File.exist? dep.name +                File.mtime(dep.name) > @ts +            elsif !dep.file_target? +		@rac.err_msg @rac.pos_text(rantfile.path, line_number), +		    "in prerequisites: no such file: `#{dep.full_name}'" +		self.fail +	    end +        end + +	def handle_timestamped(dep, opt) +	    return @block if dep.invoke opt +	    @block && dep.timestamp(opt) > @ts +	end + +	def handle_non_node(dep, opt) +            goto_task_home +	    unless File.exist? dep +		@rac.err_msg @rac.pos_text(rantfile.path, line_number), +		    "in prerequisites: no such file or task: `#{dep}'" +		self.fail +	    end +	    [dep, @block && File.mtime(dep) > @ts] +	end + +	def run +            return if @rac.running_task(self) +	    @rac.sys.mkdir @name unless @isdir +	    if @block +		@block.arity == 0 ? @block.call : @block[self] +		goto_task_home +		@rac.sys.touch @name +	    end +	end + +	def each_target +	    goto_task_home +	    yield name +	end +    end	# class DirTask + +    class SourceNode +	include Node +	def initialize(rac, name, prerequisites = []) +	    super() +	    @rac = rac +	    @name = name or raise ArgumentError, "name not given" +	    @pre = prerequisites +	    @run = false +	    @ts = nil +	end +	def prerequisites +	    @pre +	end +	def timestamp(opt = INVOKE_OPT) +	    return @ts if @ts +	    goto_task_home +	    if File.exist?(@name) +		@ts = File.mtime @name +	    else +		rac.abort_at(ch, "SourceNode: no such file -- #@name") +	    end +	    sd = project_subdir +	    @pre.each { |f| +		nodes = rac.resolve f, sd +		if nodes.empty? +		    if File.exist? f +			mtime = File.mtime f +			@ts = mtime if mtime > @ts +		    else +			rac.abort_at(ch,  +			    "SourceNode: no such file -- #{f}") +		    end +		else +		    nodes.each { |node| +                        node.invoke(opt) +			if node.respond_to? :timestamp +			    node_ts = node.timestamp(opt) +                            goto_task_home +			    @ts = node_ts if node_ts > @ts +			else +			    rac.abort_at(ch,  +				"SourceNode can't depend on #{node.name}") +			end +		    } +		end +	    } +	    @ts +	end +	def invoke(opt = INVOKE_OPT) +	    false +	end +        def related_sources +            @pre +        end +    end # class SourceNode + +    module Generators +        class Task +	    def self.rant_gen(rac, ch, args, &block) +		unless args.size == 1 +		    rac.abort("Task takes only one argument " + +			"which has to be like one given to the " + +			"`task' function") +		end +		rac.prepare_task(args.first, nil, ch) { |name,pre,blk| +		    rac.node_factory.new_custom(rac, name, pre, block) +		} +	    end +        end +        class Directory +	    def self.rant_gen(rac, ch, args, &block) +		case args.size +		when 1 +		    name, pre = rac.normalize_task_arg(args.first, ch) +		    self.task(rac, ch, name, pre, &block) +		when 2 +		    basedir = args.shift +		    if basedir.respond_to? :to_str +			basedir = basedir.to_str +		    else +			rac.abort_at(ch, +			    "Directory: basedir argument has to be a string.") +		    end +		    name, pre = rac.normalize_task_arg(args.first, ch) +		    self.task(rac, ch, name, pre, basedir, &block) +		else +		    rac.abort_at(ch, "Directory takes one argument, " + +			"which should be like one given to the `task' command.") +		end +	    end + +	    def self.task(rac, ch, name, prerequisites=[], basedir=nil, &block) +		dirs = ::Rant::Sys.split_all(name) +		if dirs.empty? +		    rac.abort_at(ch, +			"Not a valid directory name: `#{name}'") +		end +		path = basedir +		last_task = nil +		task_block = nil +		desc_for_last = rac.pop_desc +		dirs.each { |dir| +                    pre = [path] +                    pre.compact! +		    if dir.equal?(dirs.last) +			rac.cx.desc desc_for_last + +                        dp = prerequisites.dup +                        pre.each { |elem| dp << elem } +                        pre = dp + +			task_block = block +		    end +		    path = path.nil? ? dir : File.join(path, dir) +		    last_task = rac.prepare_task({:__caller__ => ch, +			    path => pre}, task_block) { |name,pre,blk| +			rac.node_factory.new_dir(rac, name, pre, blk) +		    } +		} +		last_task +	    end +        end # class Directory +        class SourceNode +            def self.rant_gen(rac, ch, args) +                unless args.size == 1 +                    rac.abort_at(ch, "SourceNode takes one argument.") +                end +                if block_given? +                    rac.abort_at(ch, "SourceNode doesn't take a block.") +                end +                rac.prepare_task(args.first, nil, ch) { |name, pre, blk| +                    rac.node_factory.new_source(rac, name, pre, blk) +                } +            end +        end +	class Rule +	    def self.rant_gen(rac, ch, args, &block) +		unless args.size == 1 +		    rac.abort_at(ch, "Rule takes only one argument.") +		end +                rac.abort_at(ch, "Rule: block required.") unless block +		arg = args.first +		target = nil +		src_arg = nil +		if Symbol === arg +		    target = ".#{arg}" +		elsif arg.respond_to? :to_str +		    target = arg.to_str +		elsif Regexp === arg +		    target = arg +		elsif Hash === arg && arg.size == 1 +		    arg.each_pair { |target, src_arg| } +		    src_arg = src_arg.to_str if src_arg.respond_to? :to_str +		    target = target.to_str if target.respond_to? :to_str +		    src_arg = ".#{src_arg}" if Symbol === src_arg +		    target = ".#{target}" if Symbol === target +		else +		    rac.abort_at(ch, "Rule argument " + +			"has to be a hash with one key-value pair.") +		end +		esc_target = nil +		target_rx = case target +                    when String +                        esc_target = Regexp.escape(target) +                        /#{esc_target}$/ +                    when Regexp +                        target +                    else +		    rac.abort_at(ch, "rule target has " + +			"to be a string or regular expression") +		end +		src_proc = case src_arg +                    when String, Array +                        unless String === target +                            rac.abort(ch, "rule target has to be " + +                                  "a string if source is a string") +                        end +                        if src_arg.kind_of? String +                            lambda { |name| +                                name.sub(/#{esc_target}$/, src_arg) +                            } +                        else +                            lambda { |name| +                                src_arg.collect { |s_src| +                                    s_src = ".#{s_src}" if Symbol === s_src +                                    name.sub(/#{esc_target}$/, s_src) +                                } +                            } +                        end +                    when Proc: src_arg +                    when nil: lambda { |name| [] } +                    else +                        rac.abort_at(ch, "rule source has to be a " + +                            "String, Array or Proc") +                    end +                rac.resolve_hooks << +                    (block.arity == 2 ? Hook : FileHook).new( +                           rac, ch, target_rx, src_proc, block) +		nil +	    end +            class Hook +                attr_accessor :target_rx +                def initialize(rant, ch, target_rx, src_proc, block) +                    @rant = rant +                    @ch = ch +                    @target_rx = target_rx +                    @src_proc = src_proc +                    @block = block +                end +                def call(target, rel_project_dir) +		    if @target_rx =~ target +                        have_src = true +                        src = @src_proc[target] +                        if src.respond_to? :to_ary +                            have_src = src.to_ary.all? { |s| +                                have_src?(rel_project_dir, s) +                            } +                        else +                            have_src = have_src?(rel_project_dir, src) +                        end +                        if have_src +                            create_nodes(rel_project_dir, target, src) +                        end +                    end +                end +                alias [] call +                private +                def have_src?(rel_project_dir, name) +                    return true unless +                        @rant.rec_save_resolve(name, self, rel_project_dir).empty? +                    test(?e, @rant.abs_path(rel_project_dir, name)) +                end +                def create_nodes(rel_project_dir, target, deps) +                    @rant.goto_project_dir rel_project_dir +                    case nodes = @block[target, deps] +                    when Array: nodes +                    when Node: [nodes] +                    else +                        @rant.abort_at(@ch, "Block has to " + +                            "return Node or array of Nodes.") +                    end +                end +            end +            class FileHook < Hook +                private +                def have_src?(rel_project_dir, name) +                    test(?e, @rant.abs_path(rel_project_dir, name)) or +                        @rant.rec_save_resolve(name, self, rel_project_dir +                            ).any? { |t| t.file_target? } +                end +                def create_nodes(rel_project_dir, target, deps) +                    @rant.goto_project_dir rel_project_dir +                    t = @rant.file(:__caller__ => @ch, +                            target => deps, &@block) +                    [t] +                end +            end +	end # class Rule +	class Action +	    def self.rant_gen(rac, ch, args, &block) +                case args.size +                when 0: +                    unless (rac[:tasks] || rac[:stop_after_load]) +                        yield +                    end +                when 1: +                    rx = args.first +                    unless rx.kind_of? Regexp +                        rac.abort_at(ch, "Action: argument has " + +                            "to be a regular expression.") +                    end +                    rac.resolve_hooks << self.new(rac, block, rx) +                    nil +                else +                    rac.abort_at(ch, "Action: too many arguments.") +                end +	    end +            def initialize(rant, block, rx) +                @rant = rant +                @subdir = @rant.current_subdir +                @block = block +                @rx = rx +            end +            def call(target, rel_project_dir) +                if target =~ @rx +                    @rant.resolve_hooks.delete(self) +                    @rant.goto_project_dir @subdir +                    @block.call +                    @rant.resolve(target, rel_project_dir) +                end +            end +            alias [] call +	end +    end	# module Generators +end # module Rant + +Rant::MAIN_OBJECT = self + +class String +    alias sub_ext _rant_sub_ext +    def to_rant_target +        self +    end +end + +module Rant::Lib +    def parse_caller_elem(elem) +        return { :file => "", :ln => 0 } unless elem +        if elem =~ /^(.+):(\d+)(?::|$)/ +            { :file => $1, :ln => $2.to_i } +        else +            $stderr.puts "parse_caller_elem: #{elem.inspect}" +            { :file => elem, :ln => 0 } +        end +         +    end +    module_function :parse_caller_elem +end # module Lib + +module Rant::Console +    RANT_PREFIX		= "rant: " +    ERROR_PREFIX	= "[ERROR] " +    WARN_PREFIX		= "[WARNING] " +    def msg_prefix +	if defined? @msg_prefix and @msg_prefix +	    @msg_prefix +	else +	    RANT_PREFIX +	end +    end +    def msg(*text) +        pre = msg_prefix +        $stderr.puts "#{pre}#{text.join("\n" + ' ' * pre.length)}" +    end +    def vmsg(importance, *text) +        msg(*text) if verbose >= importance +    end +    def err_msg(*text) +        pre = msg_prefix + ERROR_PREFIX +        $stderr.puts "#{pre}#{text.join("\n" + ' ' * pre.length)}" +    end +    def warn_msg(*text) +        pre = msg_prefix + WARN_PREFIX +        $stderr.puts "#{pre}#{text.join("\n" + ' ' * pre.length)}" +    end +    def ask_yes_no text +        $stderr.print msg_prefix + text + " [y|n] " +        case $stdin.readline +        when /y|yes/i: true +        when /n|no/i: false +        else +            $stderr.puts(' ' * msg_prefix.length + +                "Please answer with `yes' or `no'") +            ask_yes_no text +        end +    end +    def prompt text +        $stderr.print msg_prefix + text +        input = $stdin.readline +	input ? input.chomp : input +    end +    def option_listing opts +	rs = "" +	opts.each { |lopt, *opt_a| +	    if opt_a.size == 2 +		mode, desc = opt_a +	    else +		sopt, mode, desc = opt_a +	    end +	    next unless desc	# "private" option +	    optstr = "" +	    arg = nil +	    if mode != GetoptLong::NO_ARGUMENT +		if desc =~ /(\b[A-Z_]{2,}\b)/ +		    arg = $1 +		end +	    end +	    if lopt +		optstr << lopt +		if arg +		    optstr << " " << arg +		end +		optstr = optstr.ljust(30) +	    end +	    if sopt +		optstr << "   " unless optstr.empty? +		optstr << sopt +		if arg +		    optstr << " " << arg +		end +	    end +	    rs << "  #{optstr}\n" +	    rs << "      #{desc.split("\n").join("\n      ")}\n" +	} +	rs +    end +    extend self +end # module Rant::Console + +module RantContext +    include Rant::Generators + +    Env = Rant::Env +    FileList = Rant::FileList + +    def task(targ, &block) +	rant.task(targ, &block) +    end + +    def file(targ, &block) +	rant.file(targ, &block) +    end + +    def enhance(targ, &block) +	rant.enhance(targ, &block) +    end + +    def desc(*args) +	rant.desc(*args) +    end + +    def gen(*args, &block) +	rant.gen(*args, &block) +    end + +    def import(*args, &block) +	rant.import(*args, &block) +    end + +    def plugin(*args, &block) +	rant.plugin(*args, &block) +    end + +    def subdirs(*args) +	rant.subdirs(*args) +    end + +    def source(opt, rantfile = nil) +	rant.source(opt, rantfile) +    end + +    def sys(*args, &block) +	rant.sys(*args, &block) +    end + +    def var(*args, &block) +	rant.var(*args, &block) +    end + +    def make(*args, &block) +        rant.make(*args, &block) +    end + +end	# module RantContext + +class RantAppContext +    include RantContext + +    def initialize(app) +	@__rant__ = app +    end + +    def rant +        @__rant__ +    end + +    def method_missing(sym, *args) +	Rant::MAIN_OBJECT.send(sym, *args) +    rescue NoMethodError +	raise NameError, "NameError: undefined local " + +	    "variable or method `#{sym}' for main:Object", caller +    end +end + +module Rant + +    @__rant__ = nil +    class << self + +	def run(first_arg=nil, *other_args) +	    other_args = other_args.flatten +	    args = first_arg.nil? ? ARGV.dup : ([first_arg] + other_args) +	    if rant && !rant.run? +		rant.run(args.flatten) +	    else +                @__rant__ = Rant::RantApp.new +		rant.run(args) +	    end +	end + +	def rant +	    @__rant__ +	end +    end + +end # module Rant + +class Rant::RantApp +    include Rant::Console + +    class AutoLoadNodeFactory +        def initialize(rant) +            @rant = rant +        end +        def method_missing(sym, *args, &block) +            @rant.import "nodes/default" +            @rant.node_factory.send(sym, *args, &block) +        end +    end + + + +    OPTIONS	= [ +	[ "--help",	"-h",	GetoptLong::NO_ARGUMENT, +	    "Print this help and exit."				], +	[ "--version",	"-V",	GetoptLong::NO_ARGUMENT, +	    "Print version of Rant and exit."			], +	[ "--verbose",	"-v",	GetoptLong::NO_ARGUMENT, +	    "Print more messages to stderr."			], +	[ "--quiet",	"-q",	GetoptLong::NO_ARGUMENT, +	    "Don't print commands."			        ], +	[ "--err-commands",	GetoptLong::NO_ARGUMENT, +	    "Print failed commands and their exit status."	], +	[ "--directory","-C",	GetoptLong::REQUIRED_ARGUMENT, +	    "Run rant in DIRECTORY."				], +        [ "--cd-parent","-c",   GetoptLong::NO_ARGUMENT, +            "Run rant in parent directory with Rantfile."       ], +        [ "--look-up",  "-u",   GetoptLong::NO_ARGUMENT, +            "Look in parent directories for root Rantfile."     ], +	[ "--rantfile",	"-f",	GetoptLong::REQUIRED_ARGUMENT, +	    "Process RANTFILE instead of standard rantfiles.\n" + +	    "Multiple files may be specified with this option." ], +	[ "--force-run","-a",	GetoptLong::REQUIRED_ARGUMENT, +	    "Force rebuild of TARGET and all dependencies."     ], +        [ "--dry-run",  "-n",   GetoptLong::NO_ARGUMENT, +            "Print info instead of actually executing actions."  ], +	[ "--tasks",	"-T",	GetoptLong::NO_ARGUMENT, +	    "Show a list of all described tasks and exit."	], +	 + +        [ "--import",   "-i",   GetoptLong::REQUIRED_ARGUMENT, nil ], +	[ "--stop-after-load",	GetoptLong::NO_ARGUMENT, nil	], +	[ "--trace-abort",	GetoptLong::NO_ARGUMENT, nil	], +    ] + +    ROOT_DIR_ID = "@" +    ESCAPE_ID = "\\" + +    attr_reader :args +    attr_reader :rantfiles +    attr_reader :force_targets +    attr_reader :plugins +    attr_reader :context +    alias cx context +    attr_reader :tasks +    attr_reader :imports +    attr_reader :current_subdir +    attr_reader :resolve_hooks +    attr_reader :rootdir + +    attr_accessor :node_factory + +    def initialize +	@args = [] +	@context = RantAppContext.new(self) +	@sys = ::Rant::SysObject.new(self) +	@rantfiles = [] +	@tasks = {} +	@opts = { +	    :verbose        => 0, +	    :quiet          => false, +	} +        @rootdir = Dir.pwd      # root directory of project +	@arg_rantfiles = []	# rantfiles given in args +	@arg_targets = []	# targets given in args +	@force_targets = []     # targets given with -a option +	@run = false            # run method was called at least once +	@done = false           # run method was successful +	@plugins = [] +	@var = Rant::RantVar::Space.new +	@var.query :ignore, :AutoList, [] +	@imports = [] + +	@task_desc = nil +        @last_build_subdir = "" + +	@current_subdir = "" +	@resolve_hooks = [] + +        @node_factory = AutoLoadNodeFactory.new(self) +    end + +    def [](opt) +	@opts[opt] +    end + +    def []=(opt, val) +        @opts[opt] = val +    end + +    def expand_path(subdir, path) +	case path +	when nil:	subdir.dup +	when "":	subdir.dup +	when /^@/:	path.sub(/^@/, '') +	else +            path = path.sub(/^\\(?=@)/, '') +	    if subdir.empty? +		path +	    else +		File.join(subdir, path) +	    end +	end +    end +    def resolve_root_ref(path) +        return File.join(@rootdir, path[1..-1]) if path =~ /^@/ +        path.sub(/^\\(?=@)/, '') +    end +    def project_to_fs_path(path) +	sub = expand_path(@current_subdir, path) +	sub.empty? ? @rootdir : File.join(@rootdir, sub) +    end +    def abs_path(subdir, fn) +        return fn if Rant::Sys.absolute_path?(fn) +        path = File.join(@rootdir, subdir, fn) +        path.gsub!(%r{/+}, "/") +        path.sub!(%r{/$}, "") if path.length > 1 +        path +    end +    def goto(dir) +        goto_project_dir(expand_path(@current_subdir, dir)) +    end +    def goto_project_dir(dir='') +        @current_subdir = dir +        abs_path = @current_subdir.empty? ? +            @rootdir : File.join(@rootdir, @current_subdir) +	unless Dir.pwd == abs_path +	    Dir.chdir abs_path +	    vmsg 1, "in #{abs_path}" +	end +    end + +    def run? +	@run +    end + +    def done? +	@done +    end + +    def run(*args) +	@run = true +	@args.concat(args.flatten) +	orig_pwd = @rootdir = Dir.pwd +	process_args +        Dir.chdir(@rootdir) rescue abort $!.message +	load_rantfiles + +	raise Rant::RantDoneException if @opts[:stop_after_load] + +	@plugins.each { |plugin| plugin.rant_start } +	if @opts[:tasks] +	    show_descriptions +	    raise Rant::RantDoneException +	end +	run_tasks +	raise Rant::RantDoneException +    rescue Rant::RantDoneException +	@done = true +	@plugins.each { |plugin| plugin.rant_done } +	return 0 +    rescue Rant::RantAbortException +	$stderr.puts "rant aborted!" +	return 1 +    rescue Exception => e +	ch = get_ch_from_backtrace(e.backtrace) +	if ch && !@opts[:trace_abort] +	    err_msg(pos_text(ch[:file], ch[:ln]), e.message) +	else +	    err_msg e.message, e.backtrace[0..4] +	end +	$stderr.puts "rant aborted!" +	return 1 +    ensure +        Dir.chdir @rootdir if test ?d, @rootdir +        hooks = var._get("__at_return__") +        hooks.each { |hook| hook.call } if hooks +	@plugins.each { |plugin| plugin.rant_plugin_stop } +	@plugins.each { |plugin| plugin.rant_quit } +        Dir.chdir orig_pwd +    end + + +    def desc(*args) +	if args.empty? || (args.size == 1 && args.first.nil?) +	    @task_desc = nil +	else +	    @task_desc = args.join("\n") +	end +    end + +    def task(targ, &block) +	prepare_task(targ, block) { |name,pre,blk| +            @node_factory.new_task(self, name, pre, blk) +	} +    end + +    def file(targ, &block) +	prepare_task(targ, block) { |name,pre,blk| +            @node_factory.new_file(self, name, pre, blk) +	} +    end + +    def gen(*args, &block) +	ch = Rant::Lib::parse_caller_elem(caller[1]) +	generator = args.shift +	unless generator.respond_to? :rant_gen +	    abort_at(ch, +		"gen: First argument has to be a task-generator.") +	end +	generator.rant_gen(self, ch, args, &block) +    end + +    def import(*args, &block) +	ch = Rant::Lib::parse_caller_elem(caller[1]) +	if block +	    warn_msg pos_text(ch[:file], ch[:ln]), +		"import: ignoring block" +	end +	args.flatten.each { |arg| +	    unless String === arg +                abort_at(ch, "import: only strings allowed as arguments") +	    end +	    unless @imports.include? arg +		unless Rant::CODE_IMPORTS.include? arg +		    begin +			vmsg 2, "import #{arg}" +			require "rant/import/#{arg}" +		    rescue LoadError => e +			abort_at(ch, "No such import - #{arg}") +		    end +		    Rant::CODE_IMPORTS << arg.dup +		end +                init_msg = "init_import_#{arg.gsub(/[^\w]/, '__')}" +                Rant.send init_msg, self if Rant.respond_to? init_msg +		@imports << arg.dup +	    end +	} +    end + +    def plugin(*args, &block) +	clr = caller[1] +	ch = Rant::Lib::parse_caller_elem(clr) +	name = nil +	pre = [] +	ln = ch[:ln] || 0 +	file = ch[:file] + +	pl_name = args.shift +	pl_name = pl_name.to_str if pl_name.respond_to? :to_str +	pl_name = pl_name.to_s if pl_name.is_a? Symbol +	unless pl_name.is_a? String +	    abort(pos_text(file, ln), +		"Plugin name has to be a string or symbol.") +	end +	lc_pl_name = pl_name.downcase +	import_name = "plugin/#{lc_pl_name}" +	unless Rant::CODE_IMPORTS.include? import_name +	    begin +		require "rant/plugin/#{lc_pl_name}" +		Rant::CODE_IMPORTS << import_name +	    rescue LoadError +		abort(pos_text(file, ln), +		    "no such plugin library -- #{lc_pl_name}") +	    end +	end +	pl_class = nil +	begin +	    pl_class = ::Rant::Plugin.const_get(pl_name) +	rescue NameError, ArgumentError +	    abort(pos_text(file, ln), +		"no such plugin -- #{pl_name}") +	end + +	plugin = pl_class.rant_plugin_new(self, ch, *args, &block) +	@plugins << plugin +	vmsg 2, "Plugin `#{plugin.rant_plugin_name}' registered." +	plugin.rant_plugin_init +	plugin +    end + +    def enhance(targ, &block) +	prepare_task(targ, block) { |name,pre,blk| +	    t = resolve(name).last +	    if t +		unless t.respond_to? :enhance +		    abort("Can't enhance task `#{name}'") +		end +		t.enhance(pre, &blk) +		return t +	    end +	    warn_msg "enhance \"#{name}\": no such task", +		"Generating a new file task with the given name." +            @node_factory.new_file(self, name, pre, blk) +	} +    end + +    def source(opt, rantfile = nil) +	unless rantfile +	    rantfile = opt +	    opt = nil +	end +	make_rf = opt != :n && opt != :now +	rf, is_new = rantfile_for_path(rantfile) +	return false unless is_new +	make rantfile if make_rf +	unless File.exist? rf.path +	    abort("source: No such file -- #{rantfile}") +	end + +	load_file rf +    end + +    def subdirs(*args) +	args.flatten! +	ch = Rant::Lib::parse_caller_elem(caller[1]) +	args.each { |arg| +	    if arg.respond_to? :to_str +		arg = arg.to_str +	    else +		abort_at(ch, "subdirs: arguments must be strings") +	    end +	    loaded = false +	    prev_subdir = @current_subdir +	    begin +		goto arg +                if test(?f, Rant::SUB_RANTFILE) +                    path = Rant::SUB_RANTFILE +                else +                    path = rantfile_in_dir +                end +                if path +                    if defined? @initial_subdir and +                            @initial_subdir == @current_subdir +                        rf, is_new = rantfile_for_path(path, false) +                        @rantfiles.unshift rf if is_new +                    else +                        rf, is_new = rantfile_for_path(path) +                    end +		    load_file rf if is_new +                elsif !@opts[:no_warn_subdir] +                    warn_msg(pos_text(ch[:file], ch[:ln]), +                        "subdirs: No Rantfile in subdir `#{arg}'.") +                end +	    ensure +		goto_project_dir prev_subdir +	    end +	} +    rescue SystemCallError => e +	abort_at(ch, "subdirs: " + e.message) +    end + +    def sys(*args, &block) +	args.empty? ? @sys : @sys.sh(*args, &block) +    end + +    def var(*args, &block) +	args.empty? ? @var : @var.query(*args, &block) +    end + +    def pop_desc +	td = @task_desc +	@task_desc = nil +	td +    end + +    def abort(*msg) +	err_msg(msg) unless msg.empty? +	$stderr.puts caller if @opts[:trace_abort] +	raise Rant::RantAbortException +    end + +    def abort_at(ch, *msg) +	err_msg(pos_text(ch[:file], ch[:ln]), msg) +	$stderr.puts caller if @opts[:trace_abort] +	raise Rant::RantAbortException +    end + +    def show_help +	puts "rant [-f Rantfile] [Options] [targets]" +	puts +	puts "Options are:" +	print option_listing(OPTIONS) +    end + +    def show_descriptions +	tlist = select_tasks { |t| t.description } +	def_target = target_list.first +	if tlist.empty? +	    puts "rant         # => " + list_task_names( +		resolve(def_target)).join(', ') +	    msg "No described tasks." +	    return +	end +	prefix = "rant " +	infix = "  # " +        name_length = (tlist.map{ |t| t.to_s.length } << 7).max +	cmd_length = prefix.length + name_length +	unless tlist.first.to_s == def_target +	    defaults = list_task_names( +		resolve(def_target)).join(', ') +	    puts "#{prefix}#{' ' * name_length}#{infix}=> #{defaults}" +	end +	tlist.each { |t| +	    print(prefix + t.to_s.ljust(name_length) + infix) +	    dt = t.description.sub(/\s+$/, "") +	    puts dt.gsub(/\n/, "\n" + ' ' * cmd_length + infix + "  ") +	} +	true +    end + +    def list_task_names(*tasks) +	rsl = [] +	tasks.flatten.each { |t| +	    if t.respond_to?(:has_actions?) && t.has_actions? +		rsl << t +	    elsif t.respond_to? :prerequisites +		if t.prerequisites.empty? +		    rsl << t +		else +                    t.prerequisites.each { |pre| +                        rsl.concat(list_task_names( +                            resolve(pre, t.project_subdir))) +                    } +		end +	    else +		rsl << t +	    end +	} +	rsl +    end +    private :list_task_names + +    def verbose +	@opts[:verbose] +    end + +    def quiet? +	@opts[:quiet] +    end + +    def pos_text(file, ln) +	t = "in file `#{file}'" +        t << ", line #{ln}" if ln && ln > 0 +	t << ": " +    end + +    def cmd_msg(cmd) +	puts cmd unless quiet? +    end + +    def cmd_print(text) +        print text unless quiet? +        $stdout.flush +    end + +    def cmd_targets +	@force_targets + @arg_targets +    end + +    def running_task(task) +        if @current_subdir != @last_build_subdir +            cmd_msg "(in #{@current_subdir.empty? ? +                @rootdir : @current_subdir})" +            @last_build_subdir = @current_subdir +        end +        if @opts[:dry_run] +            task.dry_run +            true +        end +    end + +    private +    def have_any_task? +        !@tasks.empty? +    end + +    def target_list +	if !have_any_task? && @resolve_hooks.empty? +	    abort("No tasks defined for this rant application!") +	end + +	target_list = @force_targets + @arg_targets +	if target_list.empty? +	    def_tasks = resolve "default" +	    unless def_tasks.empty? +		target_list << "default" +	    else +		@rantfiles.each { |f| +                    first = f.tasks.first +		    if first +                        target_list << first.reference_name +			break +		    end +		} +	    end +	end +	target_list +    end + +    def run_tasks +	target_list.each { |target| +	    if build(target) == 0 +		abort("Don't know how to make `#{target}'.") +	    end +        } +    end + +    def make(target, *args, &block) +        ch = nil +        if target.respond_to? :to_hash +            targ = target.to_hash +            ch = Rant::Lib.parse_caller_elem(caller[1]) +            abort_at(ch, "make: too many arguments") unless args.empty? +            tn = nil +            prepare_task(targ, block, ch) { |name,pre,blk| +                tn = name +                @node_factory.new_file(self, name, pre, blk) +            } +            build(tn) +        elsif target.respond_to? :to_rant_target +            rt = target.to_rant_target +            opt = args.shift +            unless args.empty? +                ch ||= Rant::Lib.parse_caller_elem(caller[1]) +                abort_at(ch, "make: too many arguments") +            end +            if block +                ch ||= Rant::Lib.parse_caller_elem(caller[1]) +                prepare_task(rt, block, ch) { |name,pre,blk| +                    @node_factory.new_file(self, name, pre, blk) +                } +                build(rt) +            else +                build(rt, opt||{}) +            end +        elsif target.respond_to? :rant_gen +            ch = Rant::Lib.parse_caller_elem(caller[1]) +            rv = target.rant_gen(self, ch, args, &block) +            unless rv.respond_to? :to_rant_target +                abort_at(ch, "make: invalid generator return value") +            end +            build(rv.to_rant_target) +            rv +        else +            ch = Rant::Lib.parse_caller_elem(caller[1]) +            abort_at(ch, +                "make: generator or target as first argument required.") +        end +    end +    public :make + +    def build(target, opt = {}) +	opt[:force] = true if @force_targets.delete(target) +        opt[:dry_run] = @opts[:dry_run] +	matching_tasks = 0 +	old_subdir = @current_subdir +	old_pwd = Dir.pwd +	resolve(target).each { |t| +            unless opt[:type] == :file && !t.file_target? +                matching_tasks += 1 +                begin +                    t.invoke(opt)  +                rescue Rant::TaskFail => e +                    err_task_fail(e) +                    abort +                end +            end +	} +	@current_subdir = old_subdir +	Dir.chdir old_pwd +	matching_tasks +    end +    public :build + +    def resolve(task_name, rel_project_dir = @current_subdir) +	s = @tasks[expand_path(rel_project_dir, task_name)] +	case s +	when nil +	    @resolve_hooks.each { |s| +		s = s[task_name, rel_project_dir] +		return s if s +	    } +	    [] +	when Rant::Node: [s] +	else # assuming list of tasks +	    s +	end +    end +    public :resolve + +    def rec_save_resolve(task_name, excl_hook, rel_project_dir = @current_subdir) +	s = @tasks[expand_path(rel_project_dir, task_name)] +	case s +	when nil +	    @resolve_hooks.each { |s| +                next if s == excl_hook +		s = s[task_name, rel_project_dir] +		return s if s +	    } +	    [] +	when Rant::Node: [s] +	else +	    s +	end +    end +    public :rec_save_resolve + +    def at_resolve(&block) +	@resolve_hooks << block if block +    end +    public :at_resolve + +    def at_return(&block) +        hooks = var._get("__at_return__") +        if hooks +            hooks << block +        else +            var._set("__at_return__", [block]) +        end +    end +    public :at_return + +    def select_tasks +	selection = [] +	@rantfiles.each { |rf| +	    rf.tasks.each { |t| +		selection << t if yield t +	    } +	} +	selection +    end +    public :select_tasks + +    def load_rantfiles +        unless @arg_rantfiles.empty? +            @arg_rantfiles.each { |fn| +                if test(?f, fn) +                    rf, is_new = rantfile_for_path(fn) +                    load_file rf if is_new +                else +                    abort "No such file -- #{fn}" +                end +            } +            return +        end +        return if have_any_task? +        fn = rantfile_in_dir +        if @opts[:cd_parent] +            old_root = @rootdir +            until fn or @rootdir == "/" +                @rootdir = File.dirname(@rootdir) +                fn = rantfile_in_dir(@rootdir) +            end +            if @rootdir != old_root and fn +                Dir.chdir @rootdir +                cmd_msg "(in #@rootdir)"  +            end +        end +        if fn +            rf, is_new = rantfile_for_path(fn) +            load_file rf if is_new +            return +        end +        have_sub_rantfile = test(?f, Rant::SUB_RANTFILE) +        if have_sub_rantfile || @opts[:look_up] +            cur_dir = Dir.pwd +            until cur_dir == "/" +                cur_dir = File.dirname(cur_dir) +                Dir.chdir cur_dir +                fn = rantfile_in_dir +                if fn +                    @initial_subdir = @rootdir.sub( +                        /^#{Regexp.escape cur_dir}\//, '') +                    @rootdir = cur_dir +                    cmd_msg "(root is #@rootdir, in #@initial_subdir)" +                    @last_build_subdir = @initial_subdir +                    rf, is_new = rantfile_for_path(fn) +                    load_file rf if is_new +                    goto_project_dir @initial_subdir +                    if have_sub_rantfile +                        rf, is_new = rantfile_for_path( +                                Rant::SUB_RANTFILE, false) +                        if is_new +                            @rantfiles.unshift rf +                            load_file rf +                        end +                    end +                    break +                end +            end +        end +        if @rantfiles.empty? +            abort("No Rantfile found, looking for:", +                Rant::RANTFILES.join(", ")) +        end +    end + +    def load_file(rantfile) +	vmsg 1, "source #{rantfile}" +	@context.instance_eval(File.read(rantfile), rantfile) +    end +    private :load_file + +    def rantfile_in_dir(dir=nil) +	::Rant::RANTFILES.each { |rfn| +	    path = dir ? File.join(dir, rfn) : rfn +            return path if test ?f, path +	} +        nil +    end + +    def process_args +	old_argv = ARGV.dup +	ARGV.replace(@args.dup) +	cmd_opts = GetoptLong.new(*OPTIONS.collect { |lst| lst[0..-2] }) +	cmd_opts.quiet = true +	cmd_opts.each { |opt, value| +	    case opt +	    when "--verbose": @opts[:verbose] += 1 +	    when "--version" +		puts "rant #{Rant::VERSION}" +		raise Rant::RantDoneException +	    when "--help" +		show_help +		raise Rant::RantDoneException +	    when "--directory" +                @rootdir = File.expand_path(value) +	    when "--rantfile" +		@arg_rantfiles << value +	    when "--force-run" +		@force_targets << value +            when "--import" +                import value +	    else +		@opts[opt.sub(/^--/, '').tr('-', "_").to_sym] = true +	    end +	} +    rescue GetoptLong::Error => e +	abort(e.message) +    ensure +	rem_args = ARGV.dup +	ARGV.replace(old_argv) +	rem_args.each { |ra| +	    if ra =~ /(^[^=]+)=([^=]+)$/ +		vmsg 2, "var: #$1=#$2" +		@var[$1] = $2 +	    else +		@arg_targets << ra +	    end +	} +    end + +    def prepare_task(targ, block, clr = caller[2]) + +	if targ.is_a? Hash +	    targ.reject! { |k, v| clr = v if k == :__caller__ } +	end +	ch = Hash === clr ? clr : Rant::Lib::parse_caller_elem(clr) + +	name, pre = normalize_task_arg(targ, ch) + +	file, is_new = rantfile_for_path(ch[:file]) +	nt = yield(name, pre, block) +	nt.rantfile = file +        nt.project_subdir = @current_subdir +	nt.line_number = ch[:ln] +	nt.description = @task_desc +	@task_desc = nil +	file.tasks << nt +	hash_task nt +	nt +    end +    public :prepare_task + +    def hash_task(task) +	n = task.full_name +	et = @tasks[n] +	case et +	when nil +	    @tasks[n] = task +	when Rant::Node +	    mt = [et, task] +	    @tasks[n] = mt +	else # assuming list of tasks +	    et << task +	end +    end + +    def normalize_task_arg(targ, ch) +	name = nil +	pre = [] +	 +	if targ.is_a? Hash +	    if targ.empty? +		abort_at(ch, "Empty hash as task argument, " + +		    "task name required.") +	    end +	    if targ.size > 1 +		abort_at(ch, "Too many hash elements, " + +		    "should only be one.") +	    end +	    targ.each_pair { |k,v| +		name = normalize_task_name(k, ch) +		pre = v +	    } +	    unless ::Rant::FileList === pre +		if pre.respond_to? :to_ary +		    pre = pre.to_ary.dup +		    pre.map! { |elem| +			normalize_task_name(elem, ch) +		    } +		else +		    pre = [normalize_task_name(pre, ch)] +		end +	    end +	else +	    name = normalize_task_name(targ, ch) +	end + +	[name, pre] +    end +    public :normalize_task_arg + +    def normalize_task_name(arg, ch) +	return arg if arg.is_a? String +	if Symbol === arg +	    arg.to_s +	elsif arg.respond_to? :to_str +	    arg.to_str +	else +	    abort_at(ch, "Task name has to be a string or symbol.") +	end +    end + +    def rantfile_for_path(path, register=true) +	abs_path = File.expand_path(path) +        file = @rantfiles.find { |rf| rf.path == abs_path } +	if file +	    [file, false] +	else +	    file = Rant::Rantfile.new abs_path +	    file.project_subdir = @current_subdir +	    @rantfiles << file if register +	    [file, true] +	end +    end + +    def get_ch_from_backtrace(backtrace) +	backtrace.each { |clr| +	    ch = ::Rant::Lib.parse_caller_elem(clr) +	    if ::Rant::Env.on_windows? +		return ch if @rantfiles.any? { |rf| +		    rf.path.tr("\\", "/").sub(/^\w\:/, '') == +			ch[:file].tr("\\", "/").sub(/^\w\:/, '') +		} +	    else +		return ch if @rantfiles.any? { |rf| +		    rf.path == ch[:file] +		} +	    end +	} +	nil +    end + +    def err_task_fail(e) +	msg = [] +	t_msg = ["Task `#{e.tname}' fail."] +	orig = e +	loop { orig = orig.orig; break unless Rant::TaskFail === orig } +	if orig && orig != e && !(Rant::RantAbortException === orig) +            ch = get_ch_from_backtrace(orig.backtrace) +            msg << pos_text(ch[:file], ch[:ln]) if ch +            unless Rant::CommandError === orig && !@opts[:err_commands] +                msg << orig.message +                msg << orig.backtrace[0..4] unless ch +            end +	end +        if e.msg && !e.msg.empty? +            ch = get_ch_from_backtrace(e.backtrace) +            t_msg.unshift(e.msg) +            t_msg.unshift(pos_text(ch[:file], ch[:ln])) if ch +        end +	err_msg msg unless msg.empty? +	err_msg t_msg +    end +end	# class Rant::RantApp + +$".concat(['rant/rantlib.rb', 'rant/init.rb', 'rant/rantvar.rb', 'rant/rantsys.rb', 'rant/import/filelist/core.rb', 'rant/node.rb', 'rant/import/nodes/default.rb', 'rant/coregen.rb']) +Rant::CODE_IMPORTS.concat %w(nodes/default +    ) + +# Catch a `require "rant"', sad... +alias require_backup_by_rant require +def require libf +    if libf == "rant" +        # TODO: needs rework! look at lib/rant.rb +	self.class.instance_eval { include Rant } +    else +	begin +	    require_backup_by_rant libf +	rescue +	    raise $!, caller +	end +    end +end + +exit Rant.run | 
