From cf8ff1a3ec40f321e9ecf7afdbc4f1b8254597a4 Mon Sep 17 00:00:00 2001 From: Ralph Amissah Date: Fri, 8 Nov 2013 20:52:43 -0500 Subject: v4 v5: rake (& rant) sisu installer * do less by default * remove rant generated sisu-install --- data/doc/sisu/CHANGELOG_v4 | 4 + data/doc/sisu/CHANGELOG_v5 | 4 + rbuild | 34 +- sisu-install | 3088 -------------------------------------------- 4 files changed, 22 insertions(+), 3108 deletions(-) delete mode 100755 sisu-install diff --git a/data/doc/sisu/CHANGELOG_v4 b/data/doc/sisu/CHANGELOG_v4 index f20d4b83..e1ca2f64 100644 --- a/data/doc/sisu/CHANGELOG_v4 +++ b/data/doc/sisu/CHANGELOG_v4 @@ -30,6 +30,10 @@ http://www.jus.uio.no/sisu/pkg/src/sisu_4.2.12.orig.tar.xz sisu_4.2.12.orig.tar.xz sisu_4.2.12-1.dsc +* rake (& rant) sisu installer + * do less by default + * removed rant generated sisu-install + * sisu_manual, minor info addition %% 4.2.11.orig.tar.xz (2013-11-05:44/2) diff --git a/data/doc/sisu/CHANGELOG_v5 b/data/doc/sisu/CHANGELOG_v5 index 5cb6573d..35f82417 100644 --- a/data/doc/sisu/CHANGELOG_v5 +++ b/data/doc/sisu/CHANGELOG_v5 @@ -30,6 +30,10 @@ http://www.jus.uio.no/sisu/pkg/src/sisu_5.0.25.orig.tar.xz sisu_5.0.25.orig.tar.xz sisu_5.0.25-1.dsc +* rake (& rant) sisu installer + * do less by default + * removed rant generated sisu-install + * sisu_manual, minor info addition %% 5.0.24.orig.tar.xz (2013-11-05:44/2) diff --git a/rbuild b/rbuild index f6220de5..084d3ad7 100644 --- a/rbuild +++ b/rbuild @@ -29,8 +29,6 @@ raise 'Please, use ruby1.9.3 or later.' if RUBY_VERSION < '1.9.3' rake -T [if rant is preferred and installed] rant -T - [else [if sisu-install is present]] - ruby sisu-install -T SiSU can also be Setup/Installation using: * Minero Aoki's setup.rb, provided along with SiSU, or @@ -106,7 +104,7 @@ class Project_details def version stamp={} v="#{dir.pwd}/conf/sisu/version.yml" - version=if File.exist?(v) + if File.exist?(v) stamp=YAML::load(File::open(v)) stamp[:version] else '' @@ -119,7 +117,7 @@ def answer?(ask) print ask + " ['yes', 'no' or 'quit']: " resp=File.new('/dev/tty').gets.strip #resp=gets.strip - ans=if resp == 'yes'; true + if resp == 'yes'; true elsif resp == 'no'; false elsif resp =~/^quit|exit$/; exit else puts "[please type: 'yes', 'no' or 'quit']" @@ -129,13 +127,9 @@ end def default_notice ans= %{#{@p.rake_rant} Information on alternative actions is available using: - [if rake is installed:] - "rake help" or "rake -T" - [if rant is installed:] - "rant help" or "rant -T" - [else [if sisu-install is present]:] - "sisu-install help" or "sisu-install -T" - Default action selected - "install and to setup #{@p.name}" proceed? } + [if rake (or rant) is installed:] + "rake help" or "rake -T" (or "rant help" or "rant -T") + Default action selected - "install #{@p.name}" proceed? } resp=answer?(ans) exit unless resp end @@ -217,23 +211,23 @@ def project_help Commands quick start list - #{@p.name} Rake/Rant Help: (This Rakefile or Rantfile uses the same directory structure as setup.rb) + #{@p.name} Rake/Rant Help: (Rakefile or Rantfile) rake -T or rant -T # a task list, (generated by Rake or Rant) for more complete and up to date help rake system or rant system # system info used Quick start install and remove project #{@p.name} as root: rake or rant # install #{@p.name} + rake base - rake setup or rant setup # install #{@p.name} (without additonal configuration and generating of test file) + rake setup # install #{@p.name} (larger install) - rake install or rant reinstall # reinstall #{@p.name} + rake install # reinstall #{@p.name} - rake remove or rant remove # clobber/remove #{@p.name}, current version: #{@p.version} - rake remove_package or rant remove_package # clobber/remove #{@p.name}, all versions - -For a more detailed and up to date list of command options use + rake remove # clobber/remove #{@p.name}, current version: #{@p.version} + rake remove_package # clobber/remove #{@p.name}, all versions +For a more detailed and up to date list of command options use: rake -T rant -T @@ -242,9 +236,9 @@ end def tasks sys('rant -T') end - #%% tasks + #% tasks desc "rake/rant (as root type 'rake' or 'rant' for default action)" -task :default => [:default_notice,:project] +task :default => [:default_notice,:setup_base] #task :default => [:help,:notice,:project] desc "Setup/Install #{@p.name} and try generate a file" task :project=> [:setup_bin,:setup_lib,:setup_conf,:setup_share,:setup_data,:setup_man,:setup_vim,:post_install_note] diff --git a/sisu-install b/sisu-install deleted file mode 100755 index 206867fd..00000000 --- a/sisu-install +++ /dev/null @@ -1,3088 +0,0 @@ -#!/usr/bin/env ruby - -# sisu-install - Monolithic rant script, autogenerated by rant-import 0.5.8. -# -# Copyright (C) 2005 Stefan Lang -# -# 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 =~ /^#$/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 -- cgit v1.2.3