aboutsummaryrefslogtreecommitdiffhomepage
path: root/sisu-install
diff options
context:
space:
mode:
authorRalph Amissah <ralph@amissah.com>2007-07-07 09:21:47 +0100
committerRalph Amissah <ralph@amissah.com>2007-07-07 09:21:47 +0100
commit52f8b9c0b1e1606a4260ef2e0df4d525497691b1 (patch)
treecfb189e2c1dce1af31fc4eb9c414b3bbc6128202 /sisu-install
parent0.55.1 sync (diff)
cgi-sample search form; texinfo fix; xml scaffold; help, man pages etc. visited; screen output, color set to true; docbook entries removed
* cgi generated sample search form * order results on files of the same title, in multiple files (with different filenames) * postgresql, character case sensitivity, control, on/off * tail decoration, gplv3 & sisu info * texinfo/info (pinfo) module starts to do something vaguely useful again [not a much used module, testing required] * print XML rendition of document structure to screen -T * sisurc.yml default, color set to true [apologies if this causes anyone any inconvenience, it is configurable in sisurc.yml] * help, man pages, README (man(8) related and env, 'sisu -V') * docbook entries removed for the present time * sisu-install (install ruby rant script renamed) and permissions set to executable
Diffstat (limited to 'sisu-install')
-rwxr-xr-xsisu-install3088
1 files changed, 3088 insertions, 0 deletions
diff --git a/sisu-install b/sisu-install
new file mode 100755
index 00000000..3d78c6c1
--- /dev/null
+++ b/sisu-install
@@ -0,0 +1,3088 @@
+#!/usr/bin/env ruby
+
+# sisu-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