diff --git a/ChangeLog b/ChangeLog index d8b76ce617..a6da7eb1a8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Fri Jun 24 07:11:37 2011 Eric Hodel + + * lib/rake: Import Rake 0.9.2 + Fri Jun 24 00:44:15 2011 Tadayoshi Funaba * ext/date/date_core.c (c_valid_{julian,gregorian}_p): fixed the range of month. diff --git a/NEWS b/NEWS index 8817111dea..072e5fdb50 100644 --- a/NEWS +++ b/NEWS @@ -184,8 +184,12 @@ with all sufficient information, see the ChangeLog file. * optparse * support for bash/zsh completion. +* Rake + * Rake has been upgraded from 0.8.7 to 0.9.2. For full release notes see + https://github.com/jimweirich/rake/blob/master/CHANGES + * RDoc - * RDoc has been upgraded to RDoc 3.6.1. For full release notes see + * RDoc has been upgraded from 2.5.8 to 3.6.1. For full release notes see http://docs.seattlerb.org/rdoc/History_txt.html * rexml diff --git a/lib/rake.rb b/lib/rake.rb index e597db5eb2..d88cf6cfe6 100644 --- a/lib/rake.rb +++ b/lib/rake.rb @@ -1,6 +1,6 @@ #-- -# Copyright 2003, 2004, 2005, 2006, 2007, 2008 by Jim Weirich (jim@weirichhouse.org) +# Copyright 2003-2010 by Jim Weirich (jim.weirich@gmail.com) # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to @@ -20,14 +20,10 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. #++ -# -# = Rake -- Ruby Make -# -# This is the main file for the Rake application. Normally it is referenced -# as a library via a require statement, but it can be distributed -# independently as an application. -RAKEVERSION = '0.8.7' +require 'rake/version' + +RAKEVERSION = Rake::VERSION require 'rbconfig' require 'fileutils' @@ -36,2450 +32,34 @@ require 'monitor' require 'optparse' require 'ostruct' +require 'rake/ext/module' +require 'rake/ext/string' +require 'rake/ext/time' + require 'rake/win32' +require 'rake/task_argument_error' +require 'rake/rule_recursion_overflow_error' +require 'rake/rake_module' +require 'rake/pseudo_status' +require 'rake/task_arguments' +require 'rake/invocation_chain' +require 'rake/task' +require 'rake/file_task' +require 'rake/file_creation_task' +require 'rake/multi_task' +require 'rake/dsl_definition' +require 'rake/file_utils_ext' +require 'rake/file_list' +require 'rake/default_loader' +require 'rake/early_time' +require 'rake/name_space' +require 'rake/task_manager' +require 'rake/application' + $trace = false -###################################################################### -# Rake extensions to Module. -# -class Module - # Check for an existing method in the current class before extending. IF - # the method already exists, then a warning is printed and the extension is - # not added. Otherwise the block is yielded and any definitions in the - # block will take effect. - # - # Usage: - # - # class String - # rake_extension("xyz") do - # def xyz - # ... - # end - # end - # end - # - def rake_extension(method) - if method_defined?(method) - $stderr.puts "WARNING: Possible conflict with Rake extension: #{self}##{method} already exists" - else - yield - end - end -end # module Module +# Some top level Constants. - -###################################################################### -# User defined methods to be added to String. -# -class String - rake_extension("ext") do - # Replace the file extension with +newext+. If there is no extension on - # the string, append the new extension to the end. If the new extension - # is not given, or is the empty string, remove any existing extension. - # - # +ext+ is a user added method for the String class. - def ext(newext='') - return self.dup if ['.', '..'].include? self - if newext != '' - newext = (newext =~ /^\./) ? newext : ("." + newext) - end - self.chomp(File.extname(self)) << newext - end - end - - rake_extension("pathmap") do - # Explode a path into individual components. Used by +pathmap+. - def pathmap_explode - head, tail = File.split(self) - return [self] if head == self - return [tail] if head == '.' || tail == '/' - return [head, tail] if head == '/' - return head.pathmap_explode + [tail] - end - protected :pathmap_explode - - # Extract a partial path from the path. Include +n+ directories from the - # front end (left hand side) if +n+ is positive. Include |+n+| - # directories from the back end (right hand side) if +n+ is negative. - def pathmap_partial(n) - dirs = File.dirname(self).pathmap_explode - partial_dirs = - if n > 0 - dirs[0...n] - elsif n < 0 - dirs.reverse[0...-n].reverse - else - "." - end - File.join(partial_dirs) - end - protected :pathmap_partial - - # Preform the pathmap replacement operations on the given path. The - # patterns take the form 'pat1,rep1;pat2,rep2...'. - def pathmap_replace(patterns, &block) - result = self - patterns.split(';').each do |pair| - pattern, replacement = pair.split(',') - pattern = Regexp.new(pattern) - if replacement == '*' && block_given? - result = result.sub(pattern, &block) - elsif replacement - result = result.sub(pattern, replacement) - else - result = result.sub(pattern, '') - end - end - result - end - protected :pathmap_replace - - # Map the path according to the given specification. The specification - # controls the details of the mapping. The following special patterns are - # recognized: - # - # * %p -- The complete path. - # * %f -- The base file name of the path, with its file extension, - # but without any directories. - # * %n -- The file name of the path without its file extension. - # * %d -- The directory list of the path. - # * %x -- The file extension of the path. An empty string if there - # is no extension. - # * %X -- Everything *but* the file extension. - # * %s -- The alternate file separater if defined, otherwise use - # the standard file separator. - # * %% -- A percent sign. - # - # The %d specifier can also have a numeric prefix (e.g. '%2d'). If the - # number is positive, only return (up to) +n+ directories in the path, - # starting from the left hand side. If +n+ is negative, return (up to) - # |+n+| directories from the right hand side of the path. - # - # Examples: - # - # 'a/b/c/d/file.txt'.pathmap("%2d") => 'a/b' - # 'a/b/c/d/file.txt'.pathmap("%-2d") => 'c/d' - # - # Also the %d, %p, %f, %n, %x, and %X operators can take a - # pattern/replacement argument to perform simple string substititions on a - # particular part of the path. The pattern and replacement are speparated - # by a comma and are enclosed by curly braces. The replacement spec comes - # after the % character but before the operator letter. (e.g. - # "%{old,new}d"). Muliple replacement specs should be separated by - # semi-colons (e.g. "%{old,new;src,bin}d"). - # - # Regular expressions may be used for the pattern, and back refs may be - # used in the replacement text. Curly braces, commas and semi-colons are - # excluded from both the pattern and replacement text (let's keep parsing - # reasonable). - # - # For example: - # - # "src/org/onestepback/proj/A.java".pathmap("%{^src,bin}X.class") - # - # returns: - # - # "bin/org/onestepback/proj/A.class" - # - # If the replacement text is '*', then a block may be provided to perform - # some arbitrary calculation for the replacement. - # - # For example: - # - # "/path/to/file.TXT".pathmap("%X%{.*,*}x") { |ext| - # ext.downcase - # } - # - # Returns: - # - # "/path/to/file.txt" - # - def pathmap(spec=nil, &block) - return self if spec.nil? - result = '' - spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag| - case frag - when '%f' - result << File.basename(self) - when '%n' - result << File.basename(self).ext - when '%d' - result << File.dirname(self) - when '%x' - result << File.extname(self) - when '%X' - result << self.ext - when '%p' - result << self - when '%s' - result << (File::ALT_SEPARATOR || File::SEPARATOR) - when '%-' - # do nothing - when '%%' - result << "%" - when /%(-?\d+)d/ - result << pathmap_partial($1.to_i) - when /^%\{([^}]*)\}(\d*[dpfnxX])/ - patterns, operator = $1, $2 - result << pathmap('%' + operator).pathmap_replace(patterns, &block) - when /^%/ - fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'" - else - result << frag - end - end - result - end - end -end # class String - -############################################################################## -module Rake - - # Errors ----------------------------------------------------------- - - # Error indicating an ill-formed task declaration. - class TaskArgumentError < ArgumentError - end - - # Error indicating a recursion overflow error in task selection. - class RuleRecursionOverflowError < StandardError - def initialize(*args) - super - @targets = [] - end - - def add_target(target) - @targets << target - end - - def message - super + ": [" + @targets.reverse.join(' => ') + "]" - end - end - - # -------------------------------------------------------------------------- - # Rake module singleton methods. - # - class << self - # Current Rake Application - def application - @application ||= Rake::Application.new - end - - # Set the current Rake application object. - def application=(app) - @application = app - end - - # Return the original directory where the Rake application was started. - def original_dir - application.original_dir - end - - end - - #################################################################### - # Mixin for creating easily cloned objects. - # - module Cloneable - # Clone an object by making a new object and setting all the instance - # variables to the same values. - def dup - sibling = self.class.new - instance_variables.each do |ivar| - value = self.instance_variable_get(ivar) - new_value = value.clone rescue value - sibling.instance_variable_set(ivar, new_value) - end - sibling.taint if tainted? - sibling - end - - def clone - sibling = dup - sibling.freeze if frozen? - sibling - end - end - - #################################################################### - # Exit status class for times the system just gives us a nil. - class PseudoStatus - attr_reader :exitstatus - def initialize(code=0) - @exitstatus = code - end - def to_i - @exitstatus << 8 - end - def >>(n) - to_i >> n - end - def stopped? - false - end - def exited? - true - end - end - - #################################################################### - # TaskAguments manage the arguments passed to a task. - # - class TaskArguments - include Enumerable - - attr_reader :names - - # Create a TaskArgument object with a list of named arguments - # (given by :names) and a set of associated values (given by - # :values). :parent is the parent argument object. - def initialize(names, values, parent=nil) - @names = names - @parent = parent - @hash = {} - names.each_with_index { |name, i| - @hash[name.to_sym] = values[i] unless values[i].nil? - } - end - - # Create a new argument scope using the prerequisite argument - # names. - def new_scope(names) - values = names.collect { |n| self[n] } - self.class.new(names, values, self) - end - - # Find an argument value by name or index. - def [](index) - lookup(index.to_sym) - end - - # Specify a hash of default values for task arguments. Use the - # defaults only if there is no specific value for the given - # argument. - def with_defaults(defaults) - @hash = defaults.merge(@hash) - end - - def each(&block) - @hash.each(&block) - end - - def method_missing(sym, *args, &block) - lookup(sym.to_sym) - end - - def to_hash - @hash - end - - def to_s - @hash.inspect - end - - def inspect - to_s - end - - protected - - def lookup(name) - if @hash.has_key?(name) - @hash[name] - elsif ENV.has_key?(name.to_s) - ENV[name.to_s] - elsif ENV.has_key?(name.to_s.upcase) - ENV[name.to_s.upcase] - elsif @parent - @parent.lookup(name) - end - end - end - - EMPTY_TASK_ARGS = TaskArguments.new([], []) - - #################################################################### - # InvocationChain tracks the chain of task invocations to detect - # circular dependencies. - class InvocationChain - def initialize(value, tail) - @value = value - @tail = tail - end - - def member?(obj) - @value == obj || @tail.member?(obj) - end - - def append(value) - if member?(value) - fail RuntimeError, "Circular dependency detected: #{to_s} => #{value}" - end - self.class.new(value, self) - end - - def to_s - "#{prefix}#{@value}" - end - - def self.append(value, chain) - chain.append(value) - end - - private - - def prefix - "#{@tail.to_s} => " - end - - class EmptyInvocationChain - def member?(obj) - false - end - def append(value) - InvocationChain.new(value, self) - end - def to_s - "TOP" - end - end - - EMPTY = EmptyInvocationChain.new - - end # class InvocationChain - -end # module Rake - -module Rake - - ########################################################################### - # A Task is the basic unit of work in a Rakefile. Tasks have associated - # actions (possibly more than one) and a list of prerequisites. When - # invoked, a task will first ensure that all of its prerequisites have an - # opportunity to run and then it will execute its own actions. - # - # Tasks are not usually created directly using the new method, but rather - # use the +file+ and +task+ convenience methods. - # - class Task - # List of prerequisites for a task. - attr_reader :prerequisites - - # List of actions attached to a task. - attr_reader :actions - - # Application owning this task. - attr_accessor :application - - # Comment for this task. Restricted to a single line of no more than 50 - # characters. - attr_reader :comment - - # Full text of the (possibly multi-line) comment. - attr_reader :full_comment - - # Array of nested namespaces names used for task lookup by this task. - attr_reader :scope - - # Return task name - def to_s - name - end - - def inspect - "<#{self.class} #{name} => [#{prerequisites.join(', ')}]>" - end - - # List of sources for task. - attr_writer :sources - def sources - @sources ||= [] - end - - # First source from a rule (nil if no sources) - def source - @sources.first if defined?(@sources) - end - - # Create a task named +task_name+ with no actions or prerequisites. Use - # +enhance+ to add actions and prerequisites. - def initialize(task_name, app) - @name = task_name.to_s - @prerequisites = [] - @actions = [] - @already_invoked = false - @full_comment = nil - @comment = nil - @lock = Monitor.new - @application = app - @scope = app.current_scope - @arg_names = nil - end - - # Enhance a task with prerequisites or actions. Returns self. - def enhance(deps=nil, &block) - @prerequisites |= deps if deps - @actions << block if block_given? - self - end - - # Name of the task, including any namespace qualifiers. - def name - @name.to_s - end - - # Name of task with argument list description. - def name_with_args # :nodoc: - if arg_description - "#{name}#{arg_description}" - else - name - end - end - - # Argument description (nil if none). - def arg_description # :nodoc: - @arg_names ? "[#{(arg_names || []).join(',')}]" : nil - end - - # Name of arguments for this task. - def arg_names - @arg_names || [] - end - - # Reenable the task, allowing its tasks to be executed if the task - # is invoked again. - def reenable - @already_invoked = false - end - - # Clear the existing prerequisites and actions of a rake task. - def clear - clear_prerequisites - clear_actions - self - end - - # Clear the existing prerequisites of a rake task. - def clear_prerequisites - prerequisites.clear - self - end - - # Clear the existing actions on a rake task. - def clear_actions - actions.clear - self - end - - # Invoke the task if it is needed. Prerequites are invoked first. - def invoke(*args) - task_args = TaskArguments.new(arg_names, args) - invoke_with_call_chain(task_args, InvocationChain::EMPTY) - end - - # Same as invoke, but explicitly pass a call chain to detect - # circular dependencies. - def invoke_with_call_chain(task_args, invocation_chain) # :nodoc: - new_chain = InvocationChain.append(self, invocation_chain) - @lock.synchronize do - if application.options.trace - puts "** Invoke #{name} #{format_trace_flags}" - end - return if @already_invoked - @already_invoked = true - invoke_prerequisites(task_args, new_chain) - execute(task_args) if needed? - end - end - protected :invoke_with_call_chain - - # Invoke all the prerequisites of a task. - def invoke_prerequisites(task_args, invocation_chain) # :nodoc: - @prerequisites.each { |n| - prereq = application[n, @scope] - prereq_args = task_args.new_scope(prereq.arg_names) - prereq.invoke_with_call_chain(prereq_args, invocation_chain) - } - end - - # Format the trace flags for display. - def format_trace_flags - flags = [] - flags << "first_time" unless @already_invoked - flags << "not_needed" unless needed? - flags.empty? ? "" : "(" + flags.join(", ") + ")" - end - private :format_trace_flags - - # Execute the actions associated with this task. - def execute(args=nil) - args ||= EMPTY_TASK_ARGS - if application.options.dryrun - puts "** Execute (dry run) #{name}" - return - end - if application.options.trace - puts "** Execute #{name}" - end - application.enhance_with_matching_rule(name) if @actions.empty? - @actions.each do |act| - case act.arity - when 1 - act.call(self) - else - act.call(self, args) - end - end - end - - # Is this task needed? - def needed? - true - end - - # Timestamp for this task. Basic tasks return the current time for their - # time stamp. Other tasks can be more sophisticated. - def timestamp - @prerequisites.collect { |p| application[p].timestamp }.max || Time.now - end - - # Add a description to the task. The description can consist of an option - # argument list (enclosed brackets) and an optional comment. - def add_description(description) - return if ! description - comment = description.strip - add_comment(comment) if comment && ! comment.empty? - end - - # Writing to the comment attribute is the same as adding a description. - def comment=(description) - add_description(description) - end - - # Add a comment to the task. If a comment alread exists, separate - # the new comment with " / ". - def add_comment(comment) - if @full_comment - @full_comment << " / " - else - @full_comment = '' - end - @full_comment << comment - if @full_comment =~ /\A([^.]+?\.)( |$)/ - @comment = $1 - else - @comment = @full_comment - end - end - private :add_comment - - # Set the names of the arguments for this task. +args+ should be - # an array of symbols, one for each argument name. - def set_arg_names(args) - @arg_names = args.map { |a| a.to_sym } - end - - # Return a string describing the internal state of a task. Useful for - # debugging. - def investigation - result = "------------------------------\n" - result << "Investigating #{name}\n" - result << "class: #{self.class}\n" - result << "task needed: #{needed?}\n" - result << "timestamp: #{timestamp}\n" - result << "pre-requisites: \n" - prereqs = @prerequisites.collect {|name| application[name]} - prereqs.sort! {|a,b| a.timestamp <=> b.timestamp} - prereqs.each do |p| - result << "--#{p.name} (#{p.timestamp})\n" - end - latest_prereq = @prerequisites.collect{|n| application[n].timestamp}.max - result << "latest-prerequisite time: #{latest_prereq}\n" - result << "................................\n\n" - return result - end - - # ---------------------------------------------------------------- - # Rake Module Methods - # - class << self - - # Clear the task list. This cause rake to immediately forget all the - # tasks that have been assigned. (Normally used in the unit tests.) - def clear - Rake.application.clear - end - - # List of all defined tasks. - def tasks - Rake.application.tasks - end - - # Return a task with the given name. If the task is not currently - # known, try to synthesize one from the defined rules. If no rules are - # found, but an existing file matches the task name, assume it is a file - # task with no dependencies or actions. - def [](task_name) - Rake.application[task_name] - end - - # TRUE if the task name is already defined. - def task_defined?(task_name) - Rake.application.lookup(task_name) != nil - end - - # Define a task given +args+ and an option block. If a rule with the - # given name already exists, the prerequisites and actions are added to - # the existing task. Returns the defined task. - def define_task(*args, &block) - Rake.application.define_task(self, *args, &block) - end - - # Define a rule for synthesizing tasks. - def create_rule(*args, &block) - Rake.application.create_rule(*args, &block) - end - - # Apply the scope to the task name according to the rules for - # this kind of task. Generic tasks will accept the scope as - # part of the name. - def scope_name(scope, task_name) - (scope + [task_name]).join(':') - end - - end # class << Rake::Task - end # class Rake::Task - - - ########################################################################### - # A FileTask is a task that includes time based dependencies. If any of a - # FileTask's prerequisites have a timestamp that is later than the file - # represented by this task, then the file must be rebuilt (using the - # supplied actions). - # - class FileTask < Task - - # Is this file task needed? Yes if it doesn't exist, or if its time stamp - # is out of date. - def needed? - ! File.exist?(name) || out_of_date?(timestamp) - end - - # Time stamp for file task. - def timestamp - if File.exist?(name) - File.mtime(name.to_s) - else - Rake::EARLY - end - end - - private - - # Are there any prerequisites with a later time than the given time stamp? - def out_of_date?(stamp) - @prerequisites.any? { |n| application[n].timestamp > stamp} - end - - # ---------------------------------------------------------------- - # Task class methods. - # - class << self - # Apply the scope to the task name according to the rules for this kind - # of task. File based tasks ignore the scope when creating the name. - def scope_name(scope, task_name) - task_name - end - end - end # class Rake::FileTask - - ########################################################################### - # A FileCreationTask is a file task that when used as a dependency will be - # needed if and only if the file has not been created. Once created, it is - # not re-triggered if any of its dependencies are newer, nor does trigger - # any rebuilds of tasks that depend on it whenever it is updated. - # - class FileCreationTask < FileTask - # Is this file task needed? Yes if it doesn't exist. - def needed? - ! File.exist?(name) - end - - # Time stamp for file creation task. This time stamp is earlier - # than any other time stamp. - def timestamp - Rake::EARLY - end - end - - ########################################################################### - # Same as a regular task, but the immediate prerequisites are done in - # parallel using Ruby threads. - # - class MultiTask < Task - private - def invoke_prerequisites(args, invocation_chain) - threads = @prerequisites.collect { |p| - Thread.new(p) { |r| application[r].invoke_with_call_chain(args, invocation_chain) } - } - threads.each { |t| t.join } - end - end -end # module Rake - -## ########################################################################### -# Task Definition Functions ... - -# Declare a basic task. -# -# Example: -# task :clobber => [:clean] do -# rm_rf "html" -# end -# -def task(*args, &block) - Rake::Task.define_task(*args, &block) -end - - -# Declare a file task. -# -# Example: -# file "config.cfg" => ["config.template"] do -# open("config.cfg", "w") do |outfile| -# open("config.template") do |infile| -# while line = infile.gets -# outfile.puts line -# end -# end -# end -# end -# -def file(*args, &block) - Rake::FileTask.define_task(*args, &block) -end - -# Declare a file creation task. -# (Mainly used for the directory command). -def file_create(args, &block) - Rake::FileCreationTask.define_task(args, &block) -end - -# Declare a set of files tasks to create the given directories on demand. -# -# Example: -# directory "testdata/doc" -# -def directory(dir) - Rake.each_dir_parent(dir) do |d| - file_create d do |t| - mkdir_p t.name if ! File.exist?(t.name) - end - end -end - -# Declare a task that performs its prerequisites in parallel. Multitasks does -# *not* guarantee that its prerequisites will execute in any given order -# (which is obvious when you think about it) -# -# Example: -# multitask :deploy => [:deploy_gem, :deploy_rdoc] -# -def multitask(args, &block) - Rake::MultiTask.define_task(args, &block) -end - -# Create a new rake namespace and use it for evaluating the given block. -# Returns a NameSpace object that can be used to lookup tasks defined in the -# namespace. -# -# E.g. -# -# ns = namespace "nested" do -# task :run -# end -# task_run = ns[:run] # find :run in the given namespace. -# -def namespace(name=nil, &block) - Rake.application.in_namespace(name, &block) -end - -# Declare a rule for auto-tasks. -# -# Example: -# rule '.o' => '.c' do |t| -# sh %{cc -o #{t.name} #{t.source}} -# end -# -def rule(*args, &block) - Rake::Task.create_rule(*args, &block) -end - -# Describe the next rake task. -# -# Example: -# desc "Run the Unit Tests" -# task :test => [:build] -# runtests -# end -# -def desc(description) - Rake.application.last_description = description -end - -# Import the partial Rakefiles +fn+. Imported files are loaded _after_ the -# current file is completely loaded. This allows the import statement to -# appear anywhere in the importing file, and yet allowing the imported files -# to depend on objects defined in the importing file. -# -# A common use of the import statement is to include files containing -# dependency declarations. -# -# See also the --rakelibdir command line option. -# -# Example: -# import ".depend", "my_rules" -# -def import(*fns) - fns.each do |fn| - Rake.application.add_import(fn) - end -end - -############################################################################# -# This a FileUtils extension that defines several additional commands to be -# added to the FileUtils utility functions. -# -module FileUtils - RUBY_EXT = ((RbConfig::CONFIG['ruby_install_name'] =~ /\.(com|cmd|exe|bat|rb|sh)$/) ? - "" : - RbConfig::CONFIG['EXEEXT']) - - RUBY = File.join( - RbConfig::CONFIG['bindir'], - RbConfig::CONFIG['ruby_install_name'] + RUBY_EXT). - sub(/.*\s.*/m, '"\&"') - - OPT_TABLE['sh'] = %w(noop verbose) - OPT_TABLE['ruby'] = %w(noop verbose) - - # Run the system command +cmd+. If multiple arguments are given the command - # is not run with the shell (same semantics as Kernel::exec and - # Kernel::system). - # - # Example: - # sh %{ls -ltr} - # - # sh 'ls', 'file with spaces' - # - # # check exit status after command runs - # sh %{grep pattern file} do |ok, res| - # if ! ok - # puts "pattern not found (status = #{res.exitstatus})" - # end - # end - # - def sh(*cmd, &block) - options = (Hash === cmd.last) ? cmd.pop : {} - unless block_given? - show_command = cmd.join(" ") - show_command = show_command[0,42] + "..." unless $trace - # TODO code application logic heref show_command.length > 45 - block = lambda { |ok, status| - ok or fail "Command failed with status (#{status.exitstatus}): [#{show_command}]" - } - end - if RakeFileUtils.verbose_flag == :default - options[:verbose] = true - else - options[:verbose] ||= RakeFileUtils.verbose_flag - end - options[:noop] ||= RakeFileUtils.nowrite_flag - rake_check_options options, :noop, :verbose - rake_output_message cmd.join(" ") if options[:verbose] - unless options[:noop] - res = rake_system(*cmd) - status = $? - status = PseudoStatus.new(1) if !res && status.nil? - block.call(res, status) - end - end - - def rake_system(*cmd) - system(*cmd) - end - private :rake_system - - # Run a Ruby interpreter with the given arguments. - # - # Example: - # ruby %{-pe '$_.upcase!' 1 then - sh(*([RUBY] + args + [options]), &block) - else - sh("#{RUBY} #{args.first}", options, &block) - end - end - - LN_SUPPORTED = [true] - - # Attempt to do a normal file link, but fall back to a copy if the link - # fails. - def safe_ln(*args) - unless LN_SUPPORTED[0] - cp(*args) - else - begin - ln(*args) - rescue StandardError, NotImplementedError - LN_SUPPORTED[0] = false - cp(*args) - end - end - end - - # Split a file path into individual directory names. - # - # Example: - # split_all("a/b/c") => ['a', 'b', 'c'] - # - def split_all(path) - head, tail = File.split(path) - return [tail] if head == '.' || tail == '/' - return [head, tail] if head == '/' - return split_all(head) + [tail] - end -end - -############################################################################# -# RakeFileUtils provides a custom version of the FileUtils methods that -# respond to the verbose and nowrite commands. -# -module RakeFileUtils - include FileUtils - - class << self - attr_accessor :verbose_flag, :nowrite_flag - end - RakeFileUtils.verbose_flag = :default - RakeFileUtils.nowrite_flag = false - - $fileutils_verbose = true - $fileutils_nowrite = false - - FileUtils::OPT_TABLE.each do |name, opts| - default_options = [] - if opts.include?(:verbose) || opts.include?("verbose") - default_options << ':verbose => RakeFileUtils.verbose_flag' - end - if opts.include?(:noop) || opts.include?("noop") - default_options << ':noop => RakeFileUtils.nowrite_flag' - end - - next if default_options.empty? - module_eval(<<-EOS, __FILE__, __LINE__ + 1) - def #{name}( *args, &block ) - super( - *rake_merge_option(args, - #{default_options.join(', ')} - ), &block) - end - EOS - end - - # Get/set the verbose flag controlling output from the FileUtils utilities. - # If verbose is true, then the utility method is echoed to standard output. - # - # Examples: - # verbose # return the current value of the verbose flag - # verbose(v) # set the verbose flag to _v_. - # verbose(v) { code } # Execute code with the verbose flag set temporarily to _v_. - # # Return to the original value when code is done. - def verbose(value=nil) - oldvalue = RakeFileUtils.verbose_flag - RakeFileUtils.verbose_flag = value unless value.nil? - if block_given? - begin - yield - ensure - RakeFileUtils.verbose_flag = oldvalue - end - end - RakeFileUtils.verbose_flag - end - - # Get/set the nowrite flag controlling output from the FileUtils utilities. - # If verbose is true, then the utility method is echoed to standard output. - # - # Examples: - # nowrite # return the current value of the nowrite flag - # nowrite(v) # set the nowrite flag to _v_. - # nowrite(v) { code } # Execute code with the nowrite flag set temporarily to _v_. - # # Return to the original value when code is done. - def nowrite(value=nil) - oldvalue = RakeFileUtils.nowrite_flag - RakeFileUtils.nowrite_flag = value unless value.nil? - if block_given? - begin - yield - ensure - RakeFileUtils.nowrite_flag = oldvalue - end - end - oldvalue - end - - # Use this function to prevent protentially destructive ruby code from - # running when the :nowrite flag is set. - # - # Example: - # - # when_writing("Building Project") do - # project.build - # end - # - # The following code will build the project under normal conditions. If the - # nowrite(true) flag is set, then the example will print: - # DRYRUN: Building Project - # instead of actually building the project. - # - def when_writing(msg=nil) - if RakeFileUtils.nowrite_flag - puts "DRYRUN: #{msg}" if msg - else - yield - end - end - - # Merge the given options with the default values. - def rake_merge_option(args, defaults) - if Hash === args.last - defaults.update(args.last) - args.pop - end - args.push defaults - args - end - private :rake_merge_option - - # Send the message to the default rake output (which is $stderr). - def rake_output_message(message) - $stderr.puts(message) - end - private :rake_output_message - - # Check that the options do not contain options not listed in +optdecl+. An - # ArgumentError exception is thrown if non-declared options are found. - def rake_check_options(options, *optdecl) - h = options.dup - optdecl.each do |name| - h.delete name - end - raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty? - end - private :rake_check_options - - extend self -end - -############################################################################# -# Include the FileUtils file manipulation functions in the top level module, -# but mark them private so that they don't unintentionally define methods on -# other objects. - -include RakeFileUtils -private(*FileUtils.instance_methods(false)) -private(*RakeFileUtils.instance_methods(false)) - -###################################################################### -module Rake - - ########################################################################### - # A FileList is essentially an array with a few helper methods defined to - # make file manipulation a bit easier. - # - # FileLists are lazy. When given a list of glob patterns for possible files - # to be included in the file list, instead of searching the file structures - # to find the files, a FileList holds the pattern for latter use. - # - # This allows us to define a number of FileList to match any number of - # files, but only search out the actual files when then FileList itself is - # actually used. The key is that the first time an element of the - # FileList/Array is requested, the pending patterns are resolved into a real - # list of file names. - # - class FileList - - include Cloneable - - # == Method Delegation - # - # The lazy evaluation magic of FileLists happens by implementing all the - # array specific methods to call +resolve+ before delegating the heavy - # lifting to an embedded array object (@items). - # - # In addition, there are two kinds of delegation calls. The regular kind - # delegates to the @items array and returns the result directly. Well, - # almost directly. It checks if the returned value is the @items object - # itself, and if so will return the FileList object instead. - # - # The second kind of delegation call is used in methods that normally - # return a new Array object. We want to capture the return value of these - # methods and wrap them in a new FileList object. We enumerate these - # methods in the +SPECIAL_RETURN+ list below. - - # List of array methods (that are not in +Object+) that need to be - # delegated. - ARRAY_METHODS = (Array.instance_methods - (Object.instance_methods - [:<=>])).map { |n| n.to_s } - - # List of additional methods that must be delegated. - MUST_DEFINE = %w[to_a inspect] - - # List of methods that should not be delegated here (we define special - # versions of them explicitly below). - MUST_NOT_DEFINE = %w[to_a to_ary partition *] - - # List of delegated methods that return new array values which need - # wrapping. - SPECIAL_RETURN = %w[ - map collect sort sort_by select find_all reject grep - compact flatten uniq values_at - + - & | - ] - - DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE).collect{ |s| s.to_s }.sort.uniq - - # Now do the delegation. - DELEGATING_METHODS.each_with_index do |sym, i| - if SPECIAL_RETURN.include?(sym) - define_method(sym) do |*args, &block| - resolve - result = @items.send(sym, *args, &block) - FileList.new.import(result) - end - else - define_method(sym) do |*args, &block| - resolve - result = @items.send(sym, *args, &block) - result.object_id == @items.object_id ? self : result - end - end - end - - # Create a file list from the globbable patterns given. If you wish to - # perform multiple includes or excludes at object build time, use the - # "yield self" pattern. - # - # Example: - # file_list = FileList.new('lib/**/*.rb', 'test/test*.rb') - # - # pkg_files = FileList.new('lib/**/*') do |fl| - # fl.exclude(/\bCVS\b/) - # end - # - def initialize(*patterns) - @pending_add = [] - @pending = false - @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup - @exclude_procs = DEFAULT_IGNORE_PROCS.dup - @exclude_re = nil - @items = [] - patterns.each { |pattern| include(pattern) } - yield self if block_given? - end - - # Add file names defined by glob patterns to the file list. If an array - # is given, add each element of the array. - # - # Example: - # file_list.include("*.java", "*.cfg") - # file_list.include %w( math.c lib.h *.o ) - # - def include(*filenames) - # TODO: check for pending - filenames.each do |fn| - if fn.respond_to? :to_ary - include(*fn.to_ary) - else - @pending_add << fn - end - end - @pending = true - self - end - alias :add :include - - # Register a list of file name patterns that should be excluded from the - # list. Patterns may be regular expressions, glob patterns or regular - # strings. In addition, a block given to exclude will remove entries that - # return true when given to the block. - # - # Note that glob patterns are expanded against the file system. If a file - # is explicitly added to a file list, but does not exist in the file - # system, then an glob pattern in the exclude list will not exclude the - # file. - # - # Examples: - # FileList['a.c', 'b.c'].exclude("a.c") => ['b.c'] - # FileList['a.c', 'b.c'].exclude(/^a/) => ['b.c'] - # - # If "a.c" is a file, then ... - # FileList['a.c', 'b.c'].exclude("a.*") => ['b.c'] - # - # If "a.c" is not a file, then ... - # FileList['a.c', 'b.c'].exclude("a.*") => ['a.c', 'b.c'] - # - def exclude(*patterns, &block) - patterns.each do |pat| - @exclude_patterns << pat - end - if block_given? - @exclude_procs << block - end - resolve_exclude if ! @pending - self - end - - - # Clear all the exclude patterns so that we exclude nothing. - def clear_exclude - @exclude_patterns = [] - @exclude_procs = [] - calculate_exclude_regexp if ! @pending - self - end - - # Define equality. - def ==(array) - to_ary == array - end - - # Return the internal array object. - def to_a - resolve - @items - end - - # Return the internal array object. - def to_ary - to_a - end - - # Lie about our class. - def is_a?(klass) - klass == Array || super(klass) - end - alias kind_of? is_a? - - # Redefine * to return either a string or a new file list. - def *(other) - result = @items * other - case result - when Array - FileList.new.import(result) - else - result - end - end - - # Resolve all the pending adds now. - def resolve - if @pending - @pending = false - @pending_add.each do |fn| resolve_add(fn) end - @pending_add = [] - resolve_exclude - end - self - end - - def calculate_exclude_regexp - ignores = [] - @exclude_patterns.each do |pat| - case pat - when Regexp - ignores << pat - when /[*?]/ - Dir[pat].each do |p| ignores << p end - else - ignores << Regexp.quote(pat) - end - end - if ignores.empty? - @exclude_re = /^$/ - else - re_str = ignores.collect { |p| "(" + p.to_s + ")" }.join("|") - @exclude_re = Regexp.new(re_str) - end - end - - def resolve_add(fn) - case fn - when %r{[*?\[\{]} - add_matching(fn) - else - self << fn - end - end - private :resolve_add - - def resolve_exclude - calculate_exclude_regexp - reject! { |fn| exclude?(fn) } - self - end - private :resolve_exclude - - # Return a new FileList with the results of running +sub+ against each - # element of the oringal list. - # - # Example: - # FileList['a.c', 'b.c'].sub(/\.c$/, '.o') => ['a.o', 'b.o'] - # - def sub(pat, rep) - inject(FileList.new) { |res, fn| res << fn.sub(pat,rep) } - end - - # Return a new FileList with the results of running +gsub+ against each - # element of the original list. - # - # Example: - # FileList['lib/test/file', 'x/y'].gsub(/\//, "\\") - # => ['lib\\test\\file', 'x\\y'] - # - def gsub(pat, rep) - inject(FileList.new) { |res, fn| res << fn.gsub(pat,rep) } - end - - # Same as +sub+ except that the oringal file list is modified. - def sub!(pat, rep) - each_with_index { |fn, i| self[i] = fn.sub(pat,rep) } - self - end - - # Same as +gsub+ except that the original file list is modified. - def gsub!(pat, rep) - each_with_index { |fn, i| self[i] = fn.gsub(pat,rep) } - self - end - - # Apply the pathmap spec to each of the included file names, returning a - # new file list with the modified paths. (See String#pathmap for - # details.) - def pathmap(spec=nil) - collect { |fn| fn.pathmap(spec) } - end - - # Return a new FileList with String#ext method applied - # to each member of the array. - # - # This method is a shortcut for: - # - # array.collect { |item| item.ext(newext) } - # - # +ext+ is a user added method for the Array class. - def ext(newext='') - collect { |fn| fn.ext(newext) } - end - - - # Grep each of the files in the filelist using the given pattern. If a - # block is given, call the block on each matching line, passing the file - # name, line number, and the matching line of text. If no block is given, - # a standard emac style file:linenumber:line message will be printed to - # standard out. - def egrep(pattern, *options) - each do |fn| - open(fn, "rb", *options) do |inf| - count = 0 - inf.each do |line| - count += 1 - if pattern.match(line) - if block_given? - yield fn, count, line - else - puts "#{fn}:#{count}:#{line}" - end - end - end - end - end - end - - # Return a new file list that only contains file names from the current - # file list that exist on the file system. - def existing - select { |fn| File.exist?(fn) } - end - - # Modify the current file list so that it contains only file name that - # exist on the file system. - def existing! - resolve - @items = @items.select { |fn| File.exist?(fn) } - self - end - - # FileList version of partition. Needed because the nested arrays should - # be FileLists in this version. - def partition(&block) # :nodoc: - resolve - result = @items.partition(&block) - [ - FileList.new.import(result[0]), - FileList.new.import(result[1]), - ] - end - - # Convert a FileList to a string by joining all elements with a space. - def to_s - resolve - self.join(' ') - end - - # Add matching glob patterns. - def add_matching(pattern) - Dir[pattern].each do |fn| - self << fn unless exclude?(fn) - end - end - private :add_matching - - # Should the given file name be excluded? - def exclude?(fn) - calculate_exclude_regexp unless @exclude_re - fn =~ @exclude_re || @exclude_procs.any? { |p| p.call(fn) } - end - - DEFAULT_IGNORE_PATTERNS = [ - /(^|[\/\\])CVS([\/\\]|$)/, - /(^|[\/\\])\.svn([\/\\]|$)/, - /\.bak$/, - /~$/ - ] - DEFAULT_IGNORE_PROCS = [ - proc { |fn| fn =~ /(^|[\/\\])core$/ && ! File.directory?(fn) } - ] -# @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup - - def import(array) - @items = array - self - end - - class << self - # Create a new file list including the files listed. Similar to: - # - # FileList.new(*args) - def [](*args) - new(*args) - end - end - end # FileList -end - -module Rake - class << self - - # Yield each file or directory component. - def each_dir_parent(dir) # :nodoc: - old_length = nil - while dir != '.' && dir.length != old_length - yield(dir) - old_length = dir.length - dir = File.dirname(dir) - end - end - end -end # module Rake - -# Alias FileList to be available at the top level. FileList = Rake::FileList - -############################################################################# -module Rake - - # Default Rakefile loader used by +import+. - class DefaultLoader - def load(fn) - Kernel.load(File.expand_path(fn)) - end - end - - # EarlyTime is a fake timestamp that occurs _before_ any other time value. - class EarlyTime - include Comparable - include Singleton - - def <=>(other) - -1 - end - - def to_s - "" - end - end - - EARLY = EarlyTime.instance -end # module Rake - -############################################################################# -# Extensions to time to allow comparisons with an early time class. -# -class Time - alias rake_original_time_compare :<=> - def <=>(other) - if Rake::EarlyTime === other - - other.<=>(self) - else - rake_original_time_compare(other) - end - end -end # class Time - -module Rake - - #################################################################### - # The NameSpace class will lookup task names in the the scope - # defined by a +namespace+ command. - # - class NameSpace - - # Create a namespace lookup object using the given task manager - # and the list of scopes. - def initialize(task_manager, scope_list) - @task_manager = task_manager - @scope = scope_list.dup - end - - # Lookup a task named +name+ in the namespace. - def [](name) - @task_manager.lookup(name, @scope) - end - - # Return the list of tasks defined in this and nested namespaces. - def tasks - @task_manager.tasks_in_scope(@scope) - end - end # NameSpace - - - #################################################################### - # The TaskManager module is a mixin for managing tasks. - module TaskManager - # Track the last comment made in the Rakefile. - attr_accessor :last_description - alias :last_comment :last_description # Backwards compatibility - - def initialize - super - @tasks = Hash.new - @rules = Array.new - @scope = Array.new - @last_description = nil - end - - def create_rule(*args, &block) - pattern, _, deps = resolve_args(args) - pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern - @rules << [pattern, deps, block] - end - - def define_task(task_class, *args, &block) - task_name, arg_names, deps = resolve_args(args) - task_name = task_class.scope_name(@scope, task_name) - deps = [deps] unless deps.respond_to?(:to_ary) - deps = deps.collect {|d| d.to_s } - task = intern(task_class, task_name) - task.set_arg_names(arg_names) unless arg_names.empty? - task.add_description(@last_description) - @last_description = nil - task.enhance(deps, &block) - task - end - - # Lookup a task. Return an existing task if found, otherwise - # create a task of the current type. - def intern(task_class, task_name) - @tasks[task_name.to_s] ||= task_class.new(task_name, self) - end - - # Find a matching task for +task_name+. - def [](task_name, scopes=nil) - task_name = task_name.to_s - self.lookup(task_name, scopes) or - enhance_with_matching_rule(task_name) or - synthesize_file_task(task_name) or - fail "Don't know how to build task '#{task_name}'" - end - - def synthesize_file_task(task_name) - return nil unless File.exist?(task_name) - define_task(Rake::FileTask, task_name) - end - - # Resolve the arguments for a task/rule. Returns a triplet of - # [task_name, arg_name_list, prerequisites]. - def resolve_args(args) - if args.last.is_a?(Hash) - deps = args.pop - resolve_args_with_dependencies(args, deps) - else - resolve_args_without_dependencies(args) - end - end - - # Resolve task arguments for a task or rule when there are no - # dependencies declared. - # - # The patterns recognized by this argument resolving function are: - # - # task :t - # task :t, [:a] - # task :t, :a (deprecated) - # - def resolve_args_without_dependencies(args) - task_name = args.shift - if args.size == 1 && args.first.respond_to?(:to_ary) - arg_names = args.first.to_ary - else - arg_names = args - end - [task_name, arg_names, []] - end - private :resolve_args_without_dependencies - - # Resolve task arguments for a task or rule when there are - # dependencies declared. - # - # The patterns recognized by this argument resolving function are: - # - # task :t => [:d] - # task :t, [a] => [:d] - # task :t, :needs => [:d] (deprecated) - # task :t, :a, :needs => [:d] (deprecated) - # - def resolve_args_with_dependencies(args, hash) # :nodoc: - fail "Task Argument Error" if hash.size != 1 - key, value = hash.map { |k, v| [k,v] }.first - if args.empty? - task_name = key - arg_names = [] - deps = value - elsif key == :needs - task_name = args.shift - arg_names = args - deps = value - else - task_name = args.shift - arg_names = key - deps = value - end - deps = [deps] unless deps.respond_to?(:to_ary) - [task_name, arg_names, deps] - end - private :resolve_args_with_dependencies - - # If a rule can be found that matches the task name, enhance the - # task with the prerequisites and actions from the rule. Set the - # source attribute of the task appropriately for the rule. Return - # the enhanced task or nil of no rule was found. - def enhance_with_matching_rule(task_name, level=0) - fail Rake::RuleRecursionOverflowError, - "Rule Recursion Too Deep" if level >= 16 - @rules.each do |pattern, extensions, block| - if pattern.match(task_name) - task = attempt_rule(task_name, extensions, block, level) - return task if task - end - end - nil - rescue Rake::RuleRecursionOverflowError => ex - ex.add_target(task_name) - fail ex - end - - # List of all defined tasks in this application. - def tasks - @tasks.values.sort_by { |t| t.name } - end - - # List of all the tasks defined in the given scope (and its - # sub-scopes). - def tasks_in_scope(scope) - prefix = scope.join(":") - tasks.select { |t| - /^#{prefix}:/ =~ t.name - } - end - - # Clear all tasks in this application. - def clear - @tasks.clear - @rules.clear - end - - # Lookup a task, using scope and the scope hints in the task name. - # This method performs straight lookups without trying to - # synthesize file tasks or rules. Special scope names (e.g. '^') - # are recognized. If no scope argument is supplied, use the - # current scope. Return nil if the task cannot be found. - def lookup(task_name, initial_scope=nil) - initial_scope ||= @scope - task_name = task_name.to_s - if task_name =~ /^rake:/ - scopes = [] - task_name = task_name.sub(/^rake:/, '') - elsif task_name =~ /^(\^+)/ - scopes = initial_scope[0, initial_scope.size - $1.size] - task_name = task_name.sub(/^(\^+)/, '') - else - scopes = initial_scope - end - lookup_in_scope(task_name, scopes) - end - - # Lookup the task name - def lookup_in_scope(name, scope) - n = scope.size - while n >= 0 - tn = (scope[0,n] + [name]).join(':') - task = @tasks[tn] - return task if task - n -= 1 - end - nil - end - private :lookup_in_scope - - # Return the list of scope names currently active in the task - # manager. - def current_scope - @scope.dup - end - - # Evaluate the block in a nested namespace named +name+. Create - # an anonymous namespace if +name+ is nil. - def in_namespace(name) - name ||= generate_name - @scope.push(name) - ns = NameSpace.new(self, @scope) - yield(ns) - ns - ensure - @scope.pop - end - - private - - # Generate an anonymous namespace name. - def generate_name - @seed ||= 0 - @seed += 1 - "_anon_#{@seed}" - end - - def trace_rule(level, message) - puts "#{" "*level}#{message}" if Rake.application.options.trace_rules - end - - # Attempt to create a rule given the list of prerequisites. - def attempt_rule(task_name, extensions, block, level) - sources = make_sources(task_name, extensions) - prereqs = sources.collect { |source| - trace_rule level, "Attempting Rule #{task_name} => #{source}" - if File.exist?(source) || Rake::Task.task_defined?(source) - trace_rule level, "(#{task_name} => #{source} ... EXIST)" - source - elsif parent = enhance_with_matching_rule(source, level+1) - trace_rule level, "(#{task_name} => #{source} ... ENHANCE)" - parent.name - else - trace_rule level, "(#{task_name} => #{source} ... FAIL)" - return nil - end - } - task = FileTask.define_task({task_name => prereqs}, &block) - task.sources = prereqs - task - end - - # Make a list of sources from the list of file name extensions / - # translation procs. - def make_sources(task_name, extensions) - extensions.collect { |ext| - case ext - when /%/ - task_name.pathmap(ext) - when %r{/} - ext - when /^\./ - task_name.ext(ext) - when String - ext - when Proc - if ext.arity == 1 - ext.call(task_name) - else - ext.call - end - else - fail "Don't know how to handle rule dependent: #{ext.inspect}" - end - }.flatten - end - - end # TaskManager - - ###################################################################### - # Rake main application object. When invoking +rake+ from the - # command line, a Rake::Application object is created and run. - # - class Application - include TaskManager - - # The name of the application (typically 'rake') - attr_reader :name - - # The original directory where rake was invoked. - attr_reader :original_dir - - # Name of the actual rakefile used. - attr_reader :rakefile - - # List of the top level task names (task names from the command line). - attr_reader :top_level_tasks - - DEFAULT_RAKEFILES = ['rakefile', 'Rakefile', 'rakefile.rb', 'Rakefile.rb'].freeze - - # Initialize a Rake::Application object. - def initialize - super - @name = 'rake' - @rakefiles = DEFAULT_RAKEFILES.dup - @rakefile = nil - @pending_imports = [] - @imported = [] - @loaders = {} - @default_loader = Rake::DefaultLoader.new - @original_dir = Dir.pwd - @top_level_tasks = [] - add_loader('rb', DefaultLoader.new) - add_loader('rf', DefaultLoader.new) - add_loader('rake', DefaultLoader.new) - @tty_output = STDOUT.tty? - end - - # Run the Rake application. The run method performs the following three steps: - # - # * Initialize the command line options (+init+). - # * Define the tasks (+load_rakefile+). - # * Run the top level tasks (+run_tasks+). - # - # If you wish to build a custom rake command, you should call +init+ on your - # application. The define any tasks. Finally, call +top_level+ to run your top - # level tasks. - def run - init - load_rakefile - top_level - end - - # Initialize the command line parameters and app name. - def init(app_name='rake') - standard_exception_handling do - @name = app_name - handle_options - collect_tasks - end - end - - # Find the rakefile and then load it and any pending imports. - def load_rakefile - standard_exception_handling do - raw_load_rakefile - end - end - - # Run the top level tasks of a Rake application. - def top_level - standard_exception_handling do - if options.show_tasks - display_tasks_and_comments - elsif options.show_prereqs - display_prerequisites - else - top_level_tasks.each { |task_name| invoke_task(task_name) } - end - end - end - - # Add a loader to handle imported files ending in the extension - # +ext+. - def add_loader(ext, loader) - ext = ".#{ext}" unless ext =~ /^\./ - @loaders[ext] = loader - end - - # Application options from the command line - def options - @options ||= OpenStruct.new - end - - # private ---------------------------------------------------------------- - - def invoke_task(task_string) - name, args = parse_task_string(task_string) - t = self[name] - t.invoke(*args) - end - - def parse_task_string(string) - if string =~ /^([^\[]+)(\[(.*)\])$/ - name = $1 - args = $3.split(/\s*,\s*/) - else - name = string - args = [] - end - [name, args] - end - - # Provide standard execption handling for the given block. - def standard_exception_handling - begin - yield - rescue SystemExit => ex - # Exit silently with current status - raise - rescue OptionParser::InvalidOption => ex - # Exit silently - exit(false) - rescue Exception => ex - # Exit with error message - $stderr.puts "#{name} aborted!" - $stderr.puts ex.message - if options.trace or true - $stderr.puts ex.backtrace.join("\n") - else - $stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || "" - $stderr.puts "(See full trace by running task with --trace)" - end - exit(false) - end - end - - # True if one of the files in RAKEFILES is in the current directory. - # If a match is found, it is copied into @rakefile. - def have_rakefile - @rakefiles.each do |fn| - if File.exist?(fn) - others = Dir.glob(fn, File::FNM_CASEFOLD) - return others.size == 1 ? others.first : fn - elsif fn == '' - return fn - end - end - return nil - end - - # True if we are outputting to TTY, false otherwise - def tty_output? - @tty_output - end - - # Override the detected TTY output state (mostly for testing) - def tty_output=( tty_output_state ) - @tty_output = tty_output_state - end - - # We will truncate output if we are outputting to a TTY or if we've been - # given an explicit column width to honor - def truncate_output? - tty_output? || ENV['RAKE_COLUMNS'] - end - - # Display the tasks and comments. - def display_tasks_and_comments - displayable_tasks = tasks.select { |t| - t.comment && t.name =~ options.show_task_pattern - } - if options.full_description - displayable_tasks.each do |t| - puts "#{name} #{t.name_with_args}" - t.full_comment.split("\n").each do |line| - puts " #{line}" - end - puts - end - else - width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10 - max_column = truncate_output? ? terminal_width - name.size - width - 7 : nil - displayable_tasks.each do |t| - printf "#{name} %-#{width}s # %s\n", - t.name_with_args, max_column ? truncate(t.comment, max_column) : t.comment - end - end - end - - def terminal_width - if ENV['RAKE_COLUMNS'] - result = ENV['RAKE_COLUMNS'].to_i - else - result = unix? ? dynamic_width : 80 - end - (result < 10) ? 80 : result - rescue - 80 - end - - # Calculate the dynamic width of the - def dynamic_width - @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput) - end - - def dynamic_width_stty - %x{stty size 2>/dev/null}.split[1].to_i - end - - def dynamic_width_tput - %x{tput cols 2>/dev/null}.to_i - end - - def unix? - RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i - end - - def windows? - Win32.windows? - end - - def truncate(string, width) - if string.length <= width - string - else - ( string[0, width-3] || "" ) + "..." - end - end - - # Display the tasks and prerequisites - def display_prerequisites - tasks.each do |t| - puts "#{name} #{t.name}" - t.prerequisites.each { |pre| puts " #{pre}" } - end - end - - # A list of all the standard options used in rake, suitable for - # passing to OptionParser. - def standard_rake_options - [ - ['--classic-namespace', '-C', "Put Task and FileTask in the top level namespace", - lambda { |value| - require 'rake/classic_namespace' - options.classic_namespace = true - } - ], - ['--describe', '-D [PATTERN]', "Describe the tasks (matching optional PATTERN), then exit.", - lambda { |value| - options.show_tasks = true - options.full_description = true - options.show_task_pattern = Regexp.new(value || '') - } - ], - ['--dry-run', '-n', "Do a dry run without executing actions.", - lambda { |value| - verbose(true) - nowrite(true) - options.dryrun = true - options.trace = true - } - ], - ['--execute', '-e CODE', "Execute some Ruby code and exit.", - lambda { |value| - eval(value) - exit - } - ], - ['--execute-print', '-p CODE', "Execute some Ruby code, print the result, then exit.", - lambda { |value| - puts eval(value) - exit - } - ], - ['--execute-continue', '-E CODE', - "Execute some Ruby code, then continue with normal task processing.", - lambda { |value| eval(value) } - ], - ['--libdir', '-I LIBDIR', "Include LIBDIR in the search path for required modules.", - lambda { |value| $:.push(value) } - ], - ['--prereqs', '-P', "Display the tasks and dependencies, then exit.", - lambda { |value| options.show_prereqs = true } - ], - ['--quiet', '-q', "Do not log messages to standard output.", - lambda { |value| verbose(false) } - ], - ['--rakefile', '-f [FILE]', "Use FILE as the rakefile.", - lambda { |value| - value ||= '' - @rakefiles.clear - @rakefiles << value - } - ], - ['--rakelibdir', '--rakelib', '-R RAKELIBDIR', - "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')", - lambda { |value| options.rakelib = value.split(':') } - ], - ['--require', '-r MODULE', "Require MODULE before executing rakefile.", - lambda { |value| - begin - require value - rescue LoadError => ex - begin - rake_require value - rescue LoadError - raise ex - end - end - } - ], - ['--rules', "Trace the rules resolution.", - lambda { |value| options.trace_rules = true } - ], - ['--no-search', '--nosearch', '-N', "Do not search parent directories for the Rakefile.", - lambda { |value| options.nosearch = true } - ], - ['--silent', '-s', "Like --quiet, but also suppresses the 'in directory' announcement.", - lambda { |value| - verbose(false) - options.silent = true - } - ], - ['--system', '-g', - "Using system wide (global) rakefiles (usually '~/.rake/*.rake').", - lambda { |value| options.load_system = true } - ], - ['--no-system', '--nosystem', '-G', - "Use standard project Rakefile search paths, ignore system wide rakefiles.", - lambda { |value| options.ignore_system = true } - ], - ['--tasks', '-T [PATTERN]', "Display the tasks (matching optional PATTERN) with descriptions, then exit.", - lambda { |value| - options.show_tasks = true - options.show_task_pattern = Regexp.new(value || '') - options.full_description = false - } - ], - ['--trace', '-t', "Turn on invoke/execute tracing, enable full backtrace.", - lambda { |value| - options.trace = true - verbose(true) - } - ], - ['--verbose', '-v', "Log message to standard output.", - lambda { |value| verbose(true) } - ], - ['--version', '-V', "Display the program version.", - lambda { |value| - puts "rake, version #{RAKEVERSION}" - exit - } - ] - ] - end - - # Read and handle the command line options. - def handle_options - options.rakelib = ['rakelib'] - - OptionParser.new do |opts| - opts.banner = "rake [-f rakefile] {options} targets..." - opts.separator "" - opts.separator "Options are ..." - - opts.on_tail("-h", "--help", "-H", "Display this help message.") do - puts opts - exit - end - - standard_rake_options.each { |args| opts.on(*args) } - end.parse! - - # If class namespaces are requested, set the global options - # according to the values in the options structure. - if options.classic_namespace - $show_tasks = options.show_tasks - $show_prereqs = options.show_prereqs - $trace = options.trace - $dryrun = options.dryrun - $silent = options.silent - end - end - - # Similar to the regular Ruby +require+ command, but will check - # for *.rake files in addition to *.rb files. - def rake_require(file_name, paths=$LOAD_PATH, loaded=$") - return false if loaded.include?(file_name) - paths.each do |path| - fn = file_name + ".rake" - full_path = File.join(path, fn) - if File.exist?(full_path) - load full_path - loaded << fn - return true - end - end - fail LoadError, "Can't find #{file_name}" - end - - def find_rakefile_location - here = Dir.pwd - while ! (fn = have_rakefile) - Dir.chdir("..") - if Dir.pwd == here || options.nosearch - return nil - end - here = Dir.pwd - end - [fn, here] - ensure - Dir.chdir(Rake.original_dir) - end - - def raw_load_rakefile # :nodoc: - rakefile, location = find_rakefile_location - if (! options.ignore_system) && - (options.load_system || rakefile.nil?) && - system_dir && File.directory?(system_dir) - puts "(in #{Dir.pwd})" unless options.silent - glob("#{system_dir}/*.rake") do |name| - add_import name - end - else - fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})" if - rakefile.nil? - @rakefile = rakefile - Dir.chdir(location) - puts "(in #{Dir.pwd})" unless options.silent - $rakefile = @rakefile if options.classic_namespace - load File.expand_path(@rakefile) if @rakefile && @rakefile != '' - options.rakelib.each do |rlib| - glob("#{rlib}/*.rake") do |name| - add_import name - end - end - end - load_imports - end - - def glob(path, &block) - Dir[path.gsub("\\", '/')].each(&block) - end - private :glob - - # The directory path containing the system wide rakefiles. - def system_dir - @system_dir ||= ENV['RAKE_SYSTEM'] || standard_system_dir - end - - # The standard directory containing system wide rake files. - unless method_defined?(:standard_system_dir) - def standard_system_dir #:nodoc: - File.expand_path('~/.rake') - end - end - private :standard_system_dir - - # Collect the list of tasks on the command line. If no tasks are - # given, return a list containing only the default task. - # Environmental assignments are processed at this time as well. - def collect_tasks - @top_level_tasks = [] - ARGV.each do |arg| - if arg =~ /^(\w+)=(.*)$/ - ENV[$1] = $2 - else - @top_level_tasks << arg unless arg =~ /^-/ - end - end - @top_level_tasks.push("default") if @top_level_tasks.size == 0 - end - - # Add a file to the list of files to be imported. - def add_import(fn) - @pending_imports << fn - end - - # Load the pending list of imported files. - def load_imports - while fn = @pending_imports.shift - next if @imported.member?(fn) - if fn_task = lookup(fn) - fn_task.invoke - end - ext = File.extname(fn) - loader = @loaders[ext] || @default_loader - loader.load(fn) - @imported << fn - end - end - - # Warn about deprecated use of top level constant names. - def const_warning(const_name) - @const_warning ||= false - if ! @const_warning - $stderr.puts %{WARNING: Deprecated reference to top-level constant '#{const_name}' } + - %{found at: #{rakefile_location}} # ' - $stderr.puts %{ Use --classic-namespace on rake command} - $stderr.puts %{ or 'require "rake/classic_namespace"' in Rakefile} - end - @const_warning = true - end - - def rakefile_location - begin - fail - rescue RuntimeError => ex - ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || "" - end - end - end -end - - -class Module - # Rename the original handler to make it available. - alias :rake_original_const_missing :const_missing - - # Check for deprecated uses of top level (i.e. in Object) uses of - # Rake class names. If someone tries to reference the constant - # name, display a warning and return the proper object. Using the - # --classic-namespace command line option will define these - # constants in Object and avoid this handler. - def const_missing(const_name) - case const_name - when :Task - Rake.application.const_warning(const_name) - Rake::Task - when :FileTask - Rake.application.const_warning(const_name) - Rake::FileTask - when :FileCreationTask - Rake.application.const_warning(const_name) - Rake::FileCreationTask - when :RakeApp - Rake.application.const_warning(const_name) - Rake::Application - else - rake_original_const_missing(const_name) - end - end -end +RakeFileUtils = Rake::FileUtilsExt diff --git a/lib/rake/alt_system.rb b/lib/rake/alt_system.rb new file mode 100644 index 0000000000..05af19863a --- /dev/null +++ b/lib/rake/alt_system.rb @@ -0,0 +1,109 @@ +# +# Copyright (c) 2008 James M. Lawrence +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + +require 'rbconfig' + +# +# Alternate implementations of system() and backticks `` on Windows +# for ruby-1.8 and earlier. +# +module Rake::AltSystem + WINDOWS = RbConfig::CONFIG["host_os"] =~ + %r!(msdos|mswin|djgpp|mingw|[Ww]indows)! + + class << self + def define_module_function(name, &block) + define_method(name, &block) + module_function(name) + end + end + + if WINDOWS and RUBY_VERSION < "1.9.0" + RUNNABLE_EXTS = %w[com exe bat cmd] + RUNNABLE_PATTERN = %r!\.(#{RUNNABLE_EXTS.join('|')})\Z!i + + define_module_function :kernel_system, &Kernel.method(:system) + define_module_function :kernel_backticks, &Kernel.method(:'`') + + module_function + + def repair_command(cmd) + "call " + ( + if cmd =~ %r!\A\s*\".*?\"! + # already quoted + cmd + elsif match = cmd.match(%r!\A\s*(\S+)!) + if match[1] =~ %r!/! + # avoid x/y.bat interpretation as x with option /y + %Q!"#{match[1]}"! + match.post_match + else + # a shell command will fail if quoted + cmd + end + else + # empty or whitespace + cmd + end + ) + end + + def find_runnable(file) + if file =~ RUNNABLE_PATTERN + file + else + RUNNABLE_EXTS.each { |ext| + if File.exist?(test = "#{file}.#{ext}") + return test + end + } + nil + end + end + + def system(cmd, *args) + repaired = ( + if args.empty? + [repair_command(cmd)] + elsif runnable = find_runnable(cmd) + [File.expand_path(runnable), *args] + else + # non-existent file + [cmd, *args] + end + ) + kernel_system(*repaired) + end + + def backticks(cmd) + kernel_backticks(repair_command(cmd)) + end + + define_module_function :'`', &method(:backticks) + else + # Non-Windows or ruby-1.9+: same as Kernel versions + define_module_function :system, &Kernel.method(:system) + define_module_function :backticks, &Kernel.method(:'`') + define_module_function :'`', &Kernel.method(:'`') + end +end diff --git a/lib/rake/application.rb b/lib/rake/application.rb new file mode 100644 index 0000000000..a4954f27e7 --- /dev/null +++ b/lib/rake/application.rb @@ -0,0 +1,594 @@ +require 'shellwords' +require 'optparse' + +require 'rake/task_manager' +require 'rake/win32' + +module Rake + + ###################################################################### + # Rake main application object. When invoking +rake+ from the + # command line, a Rake::Application object is created and run. + # + class Application + include TaskManager + + # The name of the application (typically 'rake') + attr_reader :name + + # The original directory where rake was invoked. + attr_reader :original_dir + + # Name of the actual rakefile used. + attr_reader :rakefile + + # Number of columns on the terminal + attr_accessor :terminal_columns + + # List of the top level task names (task names from the command line). + attr_reader :top_level_tasks + + DEFAULT_RAKEFILES = ['rakefile', 'Rakefile', 'rakefile.rb', 'Rakefile.rb'].freeze + + # Initialize a Rake::Application object. + def initialize + super + @name = 'rake' + @rakefiles = DEFAULT_RAKEFILES.dup + @rakefile = nil + @pending_imports = [] + @imported = [] + @loaders = {} + @default_loader = Rake::DefaultLoader.new + @original_dir = Dir.pwd + @top_level_tasks = [] + add_loader('rb', DefaultLoader.new) + add_loader('rf', DefaultLoader.new) + add_loader('rake', DefaultLoader.new) + @tty_output = STDOUT.tty? + @terminal_columns = ENV['RAKE_COLUMNS'].to_i + end + + # Run the Rake application. The run method performs the following + # three steps: + # + # * Initialize the command line options (+init+). + # * Define the tasks (+load_rakefile+). + # * Run the top level tasks (+run_tasks+). + # + # If you wish to build a custom rake command, you should call + # +init+ on your application. Then define any tasks. Finally, + # call +top_level+ to run your top level tasks. + def run + standard_exception_handling do + init + load_rakefile + top_level + end + end + + # Initialize the command line parameters and app name. + def init(app_name='rake') + standard_exception_handling do + @name = app_name + handle_options + collect_tasks + end + end + + # Find the rakefile and then load it and any pending imports. + def load_rakefile + standard_exception_handling do + raw_load_rakefile + end + end + + # Run the top level tasks of a Rake application. + def top_level + standard_exception_handling do + if options.show_tasks + display_tasks_and_comments + elsif options.show_prereqs + display_prerequisites + else + top_level_tasks.each { |task_name| invoke_task(task_name) } + end + end + end + + # Add a loader to handle imported files ending in the extension + # +ext+. + def add_loader(ext, loader) + ext = ".#{ext}" unless ext =~ /^\./ + @loaders[ext] = loader + end + + # Application options from the command line + def options + @options ||= OpenStruct.new + end + + # private ---------------------------------------------------------------- + + def invoke_task(task_string) + name, args = parse_task_string(task_string) + t = self[name] + t.invoke(*args) + end + + def parse_task_string(string) + if string =~ /^([^\[]+)(\[(.*)\])$/ + name = $1 + args = $3.split(/\s*,\s*/) + else + name = string + args = [] + end + [name, args] + end + + # Provide standard exception handling for the given block. + def standard_exception_handling + begin + yield + rescue SystemExit => ex + # Exit silently with current status + raise + rescue OptionParser::InvalidOption => ex + $stderr.puts ex.message + exit(false) + rescue Exception => ex + # Exit with error message + display_error_message(ex) + exit(false) + end + end + + # Display the error message that caused the exception. + def display_error_message(ex) + $stderr.puts "#{name} aborted!" + $stderr.puts ex.message + if options.trace + $stderr.puts ex.backtrace.join("\n") + else + $stderr.puts rakefile_location(ex.backtrace) + end + $stderr.puts "Tasks: #{ex.chain}" if has_chain?(ex) + $stderr.puts "(See full trace by running task with --trace)" unless options.trace + end + + # Warn about deprecated usage. + # + # Example: + # Rake.application.deprecate("import", "Rake.import", caller.first) + # + def deprecate(old_usage, new_usage, call_site) + return if options.ignore_deprecate + $stderr.puts "WARNING: '#{old_usage}' is deprecated. " + + "Please use '#{new_usage}' instead.\n" + + " at #{call_site}" + end + + # Does the exception have a task invocation chain? + def has_chain?(exception) + exception.respond_to?(:chain) && exception.chain + end + private :has_chain? + + # True if one of the files in RAKEFILES is in the current directory. + # If a match is found, it is copied into @rakefile. + def have_rakefile + @rakefiles.each do |fn| + if File.exist?(fn) + others = Dir.glob(fn, File::FNM_CASEFOLD) + return others.size == 1 ? others.first : fn + elsif fn == '' + return fn + end + end + return nil + end + + # True if we are outputting to TTY, false otherwise + def tty_output? + @tty_output + end + + # Override the detected TTY output state (mostly for testing) + def tty_output=( tty_output_state ) + @tty_output = tty_output_state + end + + # We will truncate output if we are outputting to a TTY or if we've been + # given an explicit column width to honor + def truncate_output? + tty_output? || @terminal_columns.nonzero? + end + + # Display the tasks and comments. + def display_tasks_and_comments + displayable_tasks = tasks.select { |t| + t.comment && t.name =~ options.show_task_pattern + } + case options.show_tasks + when :tasks + width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10 + max_column = truncate_output? ? terminal_width - name.size - width - 7 : nil + + displayable_tasks.each do |t| + printf "#{name} %-#{width}s # %s\n", + t.name_with_args, max_column ? truncate(t.comment, max_column) : t.comment + end + when :describe + displayable_tasks.each do |t| + puts "#{name} #{t.name_with_args}" + t.full_comment.split("\n").each do |line| + puts " #{line}" + end + puts + end + when :lines + displayable_tasks.each do |t| + t.locations.each do |loc| + printf "#{name} %-30s %s\n",t.name_with_args, loc + end + end + else + fail "Unknown show task mode: '#{options.show_tasks}'" + end + end + + def terminal_width + if @terminal_columns.nonzero? + result = @terminal_columns + else + result = unix? ? dynamic_width : 80 + end + (result < 10) ? 80 : result + rescue + 80 + end + + # Calculate the dynamic width of the + def dynamic_width + @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput) + end + + def dynamic_width_stty + %x{stty size 2>/dev/null}.split[1].to_i + end + + def dynamic_width_tput + %x{tput cols 2>/dev/null}.to_i + end + + def unix? + RbConfig::CONFIG['host_os'] =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i + end + + def windows? + Win32.windows? + end + + def truncate(string, width) + if string.length <= width + string + else + ( string[0, width-3] || "" ) + "..." + end + end + + # Display the tasks and prerequisites + def display_prerequisites + tasks.each do |t| + puts "#{name} #{t.name}" + t.prerequisites.each { |pre| puts " #{pre}" } + end + end + + # A list of all the standard options used in rake, suitable for + # passing to OptionParser. + def standard_rake_options + [ + ['--classic-namespace', '-C', "Put Task and FileTask in the top level namespace", + lambda { |value| + require 'rake/classic_namespace' + options.classic_namespace = true + } + ], + ['--describe', '-D [PATTERN]', "Describe the tasks (matching optional PATTERN), then exit.", + lambda { |value| + options.show_tasks = :describe + options.show_task_pattern = Regexp.new(value || '') + TaskManager.record_task_metadata = true + } + ], + ['--dry-run', '-n', "Do a dry run without executing actions.", + lambda { |value| + Rake.verbose(true) + Rake.nowrite(true) + options.dryrun = true + options.trace = true + } + ], + ['--execute', '-e CODE', "Execute some Ruby code and exit.", + lambda { |value| + eval(value) + exit + } + ], + ['--execute-print', '-p CODE', "Execute some Ruby code, print the result, then exit.", + lambda { |value| + puts eval(value) + exit + } + ], + ['--execute-continue', '-E CODE', + "Execute some Ruby code, then continue with normal task processing.", + lambda { |value| eval(value) } + ], + ['--libdir', '-I LIBDIR', "Include LIBDIR in the search path for required modules.", + lambda { |value| $:.push(value) } + ], + ['--no-search', '--nosearch', '-N', "Do not search parent directories for the Rakefile.", + lambda { |value| options.nosearch = true } + ], + ['--prereqs', '-P', "Display the tasks and dependencies, then exit.", + lambda { |value| options.show_prereqs = true } + ], + ['--quiet', '-q', "Do not log messages to standard output.", + lambda { |value| Rake.verbose(false) } + ], + ['--rakefile', '-f [FILE]', "Use FILE as the rakefile.", + lambda { |value| + value ||= '' + @rakefiles.clear + @rakefiles << value + } + ], + ['--rakelibdir', '--rakelib', '-R RAKELIBDIR', + "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')", + lambda { |value| options.rakelib = value.split(':') } + ], + ['--require', '-r MODULE', "Require MODULE before executing rakefile.", + lambda { |value| + begin + require value + rescue LoadError => ex + begin + rake_require value + rescue LoadError + raise ex + end + end + } + ], + ['--rules', "Trace the rules resolution.", + lambda { |value| options.trace_rules = true } + ], + ['--silent', '-s', "Like --quiet, but also suppresses the 'in directory' announcement.", + lambda { |value| + Rake.verbose(false) + options.silent = true + } + ], + ['--system', '-g', + "Using system wide (global) rakefiles (usually '~/.rake/*.rake').", + lambda { |value| options.load_system = true } + ], + ['--no-system', '--nosystem', '-G', + "Use standard project Rakefile search paths, ignore system wide rakefiles.", + lambda { |value| options.ignore_system = true } + ], + ['--tasks', '-T [PATTERN]', "Display the tasks (matching optional PATTERN) with descriptions, then exit.", + lambda { |value| + options.show_tasks = :tasks + options.show_task_pattern = Regexp.new(value || '') + Rake::TaskManager.record_task_metadata = true + } + ], + ['--trace', '-t', "Turn on invoke/execute tracing, enable full backtrace.", + lambda { |value| + options.trace = true + Rake.verbose(true) + } + ], + ['--verbose', '-v', "Log message to standard output.", + lambda { |value| Rake.verbose(true) } + ], + ['--version', '-V', "Display the program version.", + lambda { |value| + puts "rake, version #{RAKEVERSION}" + exit + } + ], + ['--where', '-W [PATTERN]', "Describe the tasks (matching optional PATTERN), then exit.", + lambda { |value| + options.show_tasks = :lines + options.show_task_pattern = Regexp.new(value || '') + Rake::TaskManager.record_task_metadata = true + } + ], + ['--no-deprecation-warnings', '-X', "Disable the deprecation warnings.", + lambda { |value| + options.ignore_deprecate = true + } + ], + ] + end + + # Read and handle the command line options. + def handle_options + options.rakelib = ['rakelib'] + + OptionParser.new do |opts| + opts.banner = "rake [-f rakefile] {options} targets..." + opts.separator "" + opts.separator "Options are ..." + + opts.on_tail("-h", "--help", "-H", "Display this help message.") do + puts opts + exit + end + + standard_rake_options.each { |args| opts.on(*args) } + opts.environment('RAKEOPT') + end.parse! + + # If class namespaces are requested, set the global options + # according to the values in the options structure. + if options.classic_namespace + $show_tasks = options.show_tasks + $show_prereqs = options.show_prereqs + $trace = options.trace + $dryrun = options.dryrun + $silent = options.silent + end + end + + # Similar to the regular Ruby +require+ command, but will check + # for *.rake files in addition to *.rb files. + def rake_require(file_name, paths=$LOAD_PATH, loaded=$") + fn = file_name + ".rake" + return false if loaded.include?(fn) + paths.each do |path| + full_path = File.join(path, fn) + if File.exist?(full_path) + Rake.load_rakefile(full_path) + loaded << fn + return true + end + end + fail LoadError, "Can't find #{file_name}" + end + + def find_rakefile_location + here = Dir.pwd + while ! (fn = have_rakefile) + Dir.chdir("..") + if Dir.pwd == here || options.nosearch + return nil + end + here = Dir.pwd + end + [fn, here] + ensure + Dir.chdir(Rake.original_dir) + end + + def print_rakefile_directory(location) + $stderr.puts "(in #{Dir.pwd})" unless + options.silent or original_dir == location + end + + def raw_load_rakefile # :nodoc: + rakefile, location = find_rakefile_location + if (! options.ignore_system) && + (options.load_system || rakefile.nil?) && + system_dir && File.directory?(system_dir) + print_rakefile_directory(location) + glob("#{system_dir}/*.rake") do |name| + add_import name + end + else + fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})" if + rakefile.nil? + @rakefile = rakefile + Dir.chdir(location) + print_rakefile_directory(location) + $rakefile = @rakefile if options.classic_namespace + Rake.load_rakefile(File.expand_path(@rakefile)) if @rakefile && @rakefile != '' + options.rakelib.each do |rlib| + glob("#{rlib}/*.rake") do |name| + add_import name + end + end + end + load_imports + end + + def glob(path, &block) + Dir[path.gsub("\\", '/')].each(&block) + end + private :glob + + # The directory path containing the system wide rakefiles. + def system_dir + @system_dir ||= + begin + if ENV['RAKE_SYSTEM'] + ENV['RAKE_SYSTEM'] + else + standard_system_dir + end + end + end + + # The standard directory containing system wide rake files. + if Win32.windows? + def standard_system_dir #:nodoc: + Win32.win32_system_dir + end + else + def standard_system_dir #:nodoc: + File.join(File.expand_path('~'), '.rake') + end + end + private :standard_system_dir + + # Collect the list of tasks on the command line. If no tasks are + # given, return a list containing only the default task. + # Environmental assignments are processed at this time as well. + def collect_tasks + @top_level_tasks = [] + ARGV.each do |arg| + if arg =~ /^(\w+)=(.*)$/ + ENV[$1] = $2 + else + @top_level_tasks << arg unless arg =~ /^-/ + end + end + @top_level_tasks.push("default") if @top_level_tasks.size == 0 + end + + # Add a file to the list of files to be imported. + def add_import(fn) + @pending_imports << fn + end + + # Load the pending list of imported files. + def load_imports + while fn = @pending_imports.shift + next if @imported.member?(fn) + if fn_task = lookup(fn) + fn_task.invoke + end + ext = File.extname(fn) + loader = @loaders[ext] || @default_loader + loader.load(fn) + @imported << fn + end + end + + # Warn about deprecated use of top level constant names. + def const_warning(const_name) + @const_warning ||= false + if ! @const_warning + $stderr.puts %{WARNING: Deprecated reference to top-level constant '#{const_name}' } + + %{found at: #{rakefile_location}} # ' + $stderr.puts %{ Use --classic-namespace on rake command} + $stderr.puts %{ or 'require "rake/classic_namespace"' in Rakefile} + end + @const_warning = true + end + + def rakefile_location backtrace = caller + backtrace.map { |t| t[/([^:]+):/,1] } + + re = /^#{@rakefile}$/ + re = /#{re.source}/i if windows? + + backtrace.find { |str| str =~ re } || '' + end + end +end diff --git a/lib/rake/classic_namespace.rb b/lib/rake/classic_namespace.rb index feb7569966..d87aba0f33 100644 --- a/lib/rake/classic_namespace.rb +++ b/lib/rake/classic_namespace.rb @@ -2,6 +2,7 @@ # Loading this file enables compatibility with older Rakefile that # referenced Task from the top level. +warn "WARNING: Classic namespaces are deprecated and will be removed from future versions of Rake." Task = Rake::Task FileTask = Rake::FileTask FileCreationTask = Rake::FileCreationTask diff --git a/lib/rake/cloneable.rb b/lib/rake/cloneable.rb new file mode 100644 index 0000000000..19c780bff6 --- /dev/null +++ b/lib/rake/cloneable.rb @@ -0,0 +1,25 @@ +module Rake + # ########################################################################## + # Mixin for creating easily cloned objects. + # + module Cloneable + # Clone an object by making a new object and setting all the instance + # variables to the same values. + def dup + sibling = self.class.new + instance_variables.each do |ivar| + value = self.instance_variable_get(ivar) + new_value = value.clone rescue value + sibling.instance_variable_set(ivar, new_value) + end + sibling.taint if tainted? + sibling + end + + def clone + sibling = dup + sibling.freeze if frozen? + sibling + end + end +end diff --git a/lib/rake/contrib/compositepublisher.rb b/lib/rake/contrib/compositepublisher.rb index 31ef080dd7..69952a0808 100644 --- a/lib/rake/contrib/compositepublisher.rb +++ b/lib/rake/contrib/compositepublisher.rb @@ -18,3 +18,4 @@ module Rake end end + diff --git a/lib/rake/contrib/ftptools.rb b/lib/rake/contrib/ftptools.rb index 12e4ff25de..78420c7412 100644 --- a/lib/rake/contrib/ftptools.rb +++ b/lib/rake/contrib/ftptools.rb @@ -99,7 +99,7 @@ module Rake # :nodoc: end end - # Create an FTP uploader targetting the directory +path+ on +host+ + # Create an FTP uploader targeting the directory +path+ on +host+ # using the given account and password. +path+ will be the root # path of the uploader. def initialize(path, host, account, password) @@ -118,7 +118,7 @@ module Rake # :nodoc: current_dir = File.join(route) if @created[current_dir].nil? @created[current_dir] = true - puts "Creating Directory #{current_dir}" if @verbose + $stderr.puts "Creating Directory #{current_dir}" if @verbose @ftp.mkdir(current_dir) rescue nil end end @@ -141,7 +141,7 @@ module Rake # :nodoc: # Upload a single file to the uploader's root path. def upload(file) - puts "Uploading #{file}" if @verbose + $stderr.puts "Uploading #{file}" if @verbose dir = File.dirname(file) makedirs(dir) @ftp.putbinaryfile(file, file) unless File.directory?(file) diff --git a/lib/rake/contrib/publisher.rb b/lib/rake/contrib/publisher.rb index 7f69d3a654..baa9a3607d 100644 --- a/lib/rake/contrib/publisher.rb +++ b/lib/rake/contrib/publisher.rb @@ -1,10 +1,6 @@ -# Copyright 2003, 2004, 2005, 2006, 2007, 2008 by Jim Weirich (jim@weirichhouse.org) +# Copyright 2003-2010 by Jim Weirich (jim.weirich@gmail.com) # All rights reserved. -# Permission is granted for use, copying, modification, distribution, -# and distribution of modified versions of this work as long as the -# above copyright notice is included. - # Configuration information about an upload host system. # * name :: Name of host system. # * webdir :: Base directory for the web information for the diff --git a/lib/rake/contrib/sshpublisher.rb b/lib/rake/contrib/sshpublisher.rb index e679716c7b..bd6adc127e 100644 --- a/lib/rake/contrib/sshpublisher.rb +++ b/lib/rake/contrib/sshpublisher.rb @@ -1,3 +1,4 @@ +require 'rake/dsl_definition' require 'rake/contrib/compositepublisher' module Rake @@ -5,6 +6,8 @@ module Rake # Publish an entire directory to an existing remote directory using # SSH. class SshDirPublisher + include Rake::DSL + def initialize(host, remote_dir, local_dir) @host = host @remote_dir = remote_dir @@ -27,6 +30,8 @@ module Rake # Publish a list of files to an existing remote directory. class SshFilePublisher + include Rake::DSL + # Create a publisher using the give host information. def initialize(host, remote_dir, local_dir, *files) @host = host diff --git a/lib/rake/contrib/sys.rb b/lib/rake/contrib/sys.rb new file mode 100644 index 0000000000..41963f1fef --- /dev/null +++ b/lib/rake/contrib/sys.rb @@ -0,0 +1,191 @@ +warn 'Sys has been deprecated in favor of FileUtils' + +#-- +# Copyright 2003-2010 by Jim Weirich (jim.weirich@gmail.com) +# All rights reserved. +#++ +# +begin + require 'ftools' +rescue LoadError +end +require 'rbconfig' + +###################################################################### +# Sys provides a number of file manipulation tools for the convenience +# of writing Rakefiles. All commands in this module will announce +# their activity on standard output if the $verbose flag is set +# ($verbose = true is the default). You can control this by globally +# setting $verbose or by using the +verbose+ and +quiet+ methods. +# +# Sys has been deprecated in favor of the FileUtils module available +# in Ruby 1.8. +# +module Sys + RUBY = RbConfig::CONFIG['ruby_install_name'] + + # Install all the files matching +wildcard+ into the +dest_dir+ + # directory. The permission mode is set to +mode+. + def install(wildcard, dest_dir, mode) + Dir[wildcard].each do |fn| + File.install(fn, dest_dir, mode, $verbose) + end + end + + # Run the system command +cmd+. + def run(cmd) + log cmd + system(cmd) or fail "Command Failed: [#{cmd}]" + end + + # Run a Ruby interpreter with the given arguments. + def ruby(*args) + run "#{RUBY} #{args.join(' ')}" + end + + # Copy a single file from +file_name+ to +dest_file+. + def copy(file_name, dest_file) + log "Copying file #{file_name} to #{dest_file}" + File.copy(file_name, dest_file) + end + + # Copy all files matching +wildcard+ into the directory +dest_dir+. + def copy_files(wildcard, dest_dir) + for_matching_files(wildcard, dest_dir) { |from, to| copy(from, to) } + end + + # Link +file_name+ to +dest_file+. + def link(file_name, dest_file) + log "Linking file #{file_name} to #{dest_file}" + File.link(file_name, dest_file) + end + + # Link all files matching +wildcard+ into the directory +dest_dir+. + def link_files(wildcard, dest_dir) + for_matching_files(wildcard, dest_dir) { |from, to| link(from, to) } + end + + # Symlink +file_name+ to +dest_file+. + def symlink(file_name, dest_file) + log "Symlinking file #{file_name} to #{dest_file}" + File.symlink(file_name, dest_file) + end + + # Symlink all files matching +wildcard+ into the directory +dest_dir+. + def symlink_files(wildcard, dest_dir) + for_matching_files(wildcard, dest_dir) { |from, to| link(from, to) } + end + + # Remove all files matching +wildcard+. If a matching file is a + # directory, it must be empty to be removed. used +delete_all+ to + # recursively delete directories. + def delete(*wildcards) + wildcards.each do |wildcard| + Dir[wildcard].each do |fn| + if File.directory?(fn) + log "Deleting directory #{fn}" + Dir.delete(fn) + else + log "Deleting file #{fn}" + File.delete(fn) + end + end + end + end + + # Recursively delete all files and directories matching +wildcard+. + def delete_all(*wildcards) + wildcards.each do |wildcard| + Dir[wildcard].each do |fn| + next if ! File.exist?(fn) + if File.directory?(fn) + Dir["#{fn}/*"].each do |subfn| + next if subfn=='.' || subfn=='..' + delete_all(subfn) + end + log "Deleting directory #{fn}" + Dir.delete(fn) + else + log "Deleting file #{fn}" + File.delete(fn) + end + end + end + end + + # Make the directories given in +dirs+. + def makedirs(*dirs) + dirs.each do |fn| + log "Making directory #{fn}" + File.makedirs(fn) + end + end + + # Make +dir+ the current working directory for the duration of + # executing the given block. + def indir(dir) + olddir = Dir.pwd + Dir.chdir(dir) + yield + ensure + Dir.chdir(olddir) + end + + # Split a file path into individual directory names. + # + # For example: + # split_all("a/b/c") => ['a', 'b', 'c'] + def split_all(path) + head, tail = File.split(path) + return [tail] if head == '.' || tail == '/' + return [head, tail] if head == '/' + return split_all(head) + [tail] + end + + # Write a message to standard error if $verbose is enabled. + def log(msg) + print " " if $trace && $verbose + $stderr.puts msg if $verbose + end + + # Perform a block with $verbose disabled. + def quiet(&block) + with_verbose(false, &block) + end + + # Perform a block with $verbose enabled. + def verbose(&block) + with_verbose(true, &block) + end + + # Perform a block with each file matching a set of wildcards. + def for_files(*wildcards) + wildcards.each do |wildcard| + Dir[wildcard].each do |fn| + yield(fn) + end + end + end + + extend(self) + + private # ---------------------------------------------------------- + + def for_matching_files(wildcard, dest_dir) + Dir[wildcard].each do |fn| + dest_file = File.join(dest_dir, fn) + parent = File.dirname(dest_file) + makedirs(parent) if ! File.directory?(parent) + yield(fn, dest_file) + end + end + + def with_verbose(v) + oldverbose = $verbose + $verbose = v + yield + ensure + $verbose = oldverbose + end + +end diff --git a/lib/rake/default_loader.rb b/lib/rake/default_loader.rb new file mode 100644 index 0000000000..5dd3c05617 --- /dev/null +++ b/lib/rake/default_loader.rb @@ -0,0 +1,10 @@ +module Rake + + # Default Rakefile loader used by +import+. + class DefaultLoader + def load(fn) + Rake.load_rakefile(File.expand_path(fn)) + end + end + +end diff --git a/lib/rake/dsl_definition.rb b/lib/rake/dsl_definition.rb new file mode 100644 index 0000000000..e0b9c6fd0e --- /dev/null +++ b/lib/rake/dsl_definition.rb @@ -0,0 +1,167 @@ +# Rake DSL functions. +require 'rake/file_utils_ext' + +module Rake + module DSL + + # Include the FileUtils file manipulation functions in the top + # level module, but mark them private so that they don't + # unintentionally define methods on other objects. + + include FileUtilsExt + private(*FileUtils.instance_methods(false)) + private(*FileUtilsExt.instance_methods(false)) + + private + + # Declare a basic task. + # + # Example: + # task :clobber => [:clean] do + # rm_rf "html" + # end + # + def task(*args, &block) + Rake::Task.define_task(*args, &block) + end + + + # Declare a file task. + # + # Example: + # file "config.cfg" => ["config.template"] do + # open("config.cfg", "w") do |outfile| + # open("config.template") do |infile| + # while line = infile.gets + # outfile.puts line + # end + # end + # end + # end + # + def file(*args, &block) + Rake::FileTask.define_task(*args, &block) + end + + # Declare a file creation task. + # (Mainly used for the directory command). + def file_create(args, &block) + Rake::FileCreationTask.define_task(args, &block) + end + + # Declare a set of files tasks to create the given directories on + # demand. + # + # Example: + # directory "testdata/doc" + # + def directory(dir) + Rake.each_dir_parent(dir) do |d| + file_create d do |t| + mkdir_p t.name if ! File.exist?(t.name) + end + end + end + + # Declare a task that performs its prerequisites in + # parallel. Multitasks does *not* guarantee that its prerequisites + # will execute in any given order (which is obvious when you think + # about it) + # + # Example: + # multitask :deploy => [:deploy_gem, :deploy_rdoc] + # + def multitask(args, &block) + Rake::MultiTask.define_task(args, &block) + end + + # Create a new rake namespace and use it for evaluating the given + # block. Returns a NameSpace object that can be used to lookup + # tasks defined in the namespace. + # + # E.g. + # + # ns = namespace "nested" do + # task :run + # end + # task_run = ns[:run] # find :run in the given namespace. + # + def namespace(name=nil, &block) + name = name.to_s if name.kind_of?(Symbol) + name = name.to_str if name.respond_to?(:to_str) + unless name.kind_of?(String) || name.nil? + raise ArgumentError, "Expected a String or Symbol for a namespace name" + end + Rake.application.in_namespace(name, &block) + end + + # Declare a rule for auto-tasks. + # + # Example: + # rule '.o' => '.c' do |t| + # sh %{cc -o #{t.name} #{t.source}} + # end + # + def rule(*args, &block) + Rake::Task.create_rule(*args, &block) + end + + # Describe the next rake task. + # + # Example: + # desc "Run the Unit Tests" + # task :test => [:build] + # runtests + # end + # + def desc(description) + Rake.application.last_description = description + end + + # Import the partial Rakefiles +fn+. Imported files are loaded + # _after_ the current file is completely loaded. This allows the + # import statement to appear anywhere in the importing file, and yet + # allowing the imported files to depend on objects defined in the + # importing file. + # + # A common use of the import statement is to include files + # containing dependency declarations. + # + # See also the --rakelibdir command line option. + # + # Example: + # import ".depend", "my_rules" + # + def import(*fns) + fns.each do |fn| + Rake.application.add_import(fn) + end + end + end + + module DeprecatedObjectDSL + Commands = Object.new.extend DSL + DSL.private_instance_methods(false).each do |name| + line = __LINE__+1 + class_eval %{ + def #{name}(*args, &block) + unless Rake.application.options.ignore_deprecate + unless @rake_dsl_warning + $stderr.puts "WARNING: Global access to Rake DSL methods is deprecated. Please include" + $stderr.puts " ... Rake::DSL into classes and modules which use the Rake DSL methods." + @rake_dsl_warning = true + end + $stderr.puts "WARNING: DSL method \#{self.class}##{name} called at \#{caller.first}" + end + Rake::DeprecatedObjectDSL::Commands.send(:#{name}, *args, &block) + end + private :#{name} + }, __FILE__, line + end + end + + extend FileUtilsExt +end + +self.extend Rake::DSL +include Rake::DeprecatedObjectDSL diff --git a/lib/rake/early_time.rb b/lib/rake/early_time.rb new file mode 100644 index 0000000000..8c0e7d3339 --- /dev/null +++ b/lib/rake/early_time.rb @@ -0,0 +1,18 @@ +module Rake + + # EarlyTime is a fake timestamp that occurs _before_ any other time value. + class EarlyTime + include Comparable + include Singleton + + def <=>(other) + -1 + end + + def to_s + "" + end + end + + EARLY = EarlyTime.instance +end diff --git a/lib/rake/ext/core.rb b/lib/rake/ext/core.rb new file mode 100644 index 0000000000..1f3a738906 --- /dev/null +++ b/lib/rake/ext/core.rb @@ -0,0 +1,27 @@ +###################################################################### +# Core extension library +# +class Module + # Check for an existing method in the current class before extending. IF + # the method already exists, then a warning is printed and the extension is + # not added. Otherwise the block is yielded and any definitions in the + # block will take effect. + # + # Usage: + # + # class String + # rake_extension("xyz") do + # def xyz + # ... + # end + # end + # end + # + def rake_extension(method) + if method_defined?(method) + $stderr.puts "WARNING: Possible conflict with Rake extension: #{self}##{method} already exists" + else + yield + end + end +end diff --git a/lib/rake/ext/module.rb b/lib/rake/ext/module.rb new file mode 100644 index 0000000000..3f64aef6c8 --- /dev/null +++ b/lib/rake/ext/module.rb @@ -0,0 +1,39 @@ +require 'rake/ext/core' +require 'rake/task' +require 'rake/file_task' +require 'rake/file_creation_task' +require 'rake/application' +require 'rake/task_manager' + +###################################################################### +# Rake extensions to Module. +# +class Module + + # Rename the original handler to make it available. + alias :rake_original_const_missing :const_missing + + # Check for deprecated uses of top level (i.e. in Object) uses of + # Rake class names. If someone tries to reference the constant + # name, display a warning and return the proper object. Using the + # --classic-namespace command line option will define these + # constants in Object and avoid this handler. + def const_missing(const_name) + case const_name + when :Task + Rake.application.const_warning(const_name) + Rake::Task + when :FileTask + Rake.application.const_warning(const_name) + Rake::FileTask + when :FileCreationTask + Rake.application.const_warning(const_name) + Rake::FileCreationTask + when :RakeApp + Rake.application.const_warning(const_name) + Rake::Application + else + rake_original_const_missing(const_name) + end + end +end diff --git a/lib/rake/ext/string.rb b/lib/rake/ext/string.rb new file mode 100644 index 0000000000..fb22a9deb1 --- /dev/null +++ b/lib/rake/ext/string.rb @@ -0,0 +1,167 @@ +require 'rake/ext/core' + +###################################################################### +# Rake extension methods for String. +# +class String + rake_extension("ext") do + # Replace the file extension with +newext+. If there is no extension on + # the string, append the new extension to the end. If the new extension + # is not given, or is the empty string, remove any existing extension. + # + # +ext+ is a user added method for the String class. + def ext(newext='') + return self.dup if ['.', '..'].include? self + if newext != '' + newext = (newext =~ /^\./) ? newext : ("." + newext) + end + self.chomp(File.extname(self)) << newext + end + end + + rake_extension("pathmap") do + # Explode a path into individual components. Used by +pathmap+. + def pathmap_explode + head, tail = File.split(self) + return [self] if head == self + return [tail] if head == '.' || tail == '/' + return [head, tail] if head == '/' + return head.pathmap_explode + [tail] + end + protected :pathmap_explode + + # Extract a partial path from the path. Include +n+ directories from the + # front end (left hand side) if +n+ is positive. Include |+n+| + # directories from the back end (right hand side) if +n+ is negative. + def pathmap_partial(n) + dirs = File.dirname(self).pathmap_explode + partial_dirs = + if n > 0 + dirs[0...n] + elsif n < 0 + dirs.reverse[0...-n].reverse + else + "." + end + File.join(partial_dirs) + end + protected :pathmap_partial + + # Preform the pathmap replacement operations on the given path. The + # patterns take the form 'pat1,rep1;pat2,rep2...'. + def pathmap_replace(patterns, &block) + result = self + patterns.split(';').each do |pair| + pattern, replacement = pair.split(',') + pattern = Regexp.new(pattern) + if replacement == '*' && block_given? + result = result.sub(pattern, &block) + elsif replacement + result = result.sub(pattern, replacement) + else + result = result.sub(pattern, '') + end + end + result + end + protected :pathmap_replace + + # Map the path according to the given specification. The specification + # controls the details of the mapping. The following special patterns are + # recognized: + # + # * %p -- The complete path. + # * %f -- The base file name of the path, with its file extension, + # but without any directories. + # * %n -- The file name of the path without its file extension. + # * %d -- The directory list of the path. + # * %x -- The file extension of the path. An empty string if there + # is no extension. + # * %X -- Everything *but* the file extension. + # * %s -- The alternate file separator if defined, otherwise use + # the standard file separator. + # * %% -- A percent sign. + # + # The %d specifier can also have a numeric prefix (e.g. '%2d'). If the + # number is positive, only return (up to) +n+ directories in the path, + # starting from the left hand side. If +n+ is negative, return (up to) + # |+n+| directories from the right hand side of the path. + # + # Examples: + # + # 'a/b/c/d/file.txt'.pathmap("%2d") => 'a/b' + # 'a/b/c/d/file.txt'.pathmap("%-2d") => 'c/d' + # + # Also the %d, %p, %f, %n, %x, and %X operators can take a + # pattern/replacement argument to perform simple string substitutions on a + # particular part of the path. The pattern and replacement are separated + # by a comma and are enclosed by curly braces. The replacement spec comes + # after the % character but before the operator letter. (e.g. + # "%{old,new}d"). Multiple replacement specs should be separated by + # semi-colons (e.g. "%{old,new;src,bin}d"). + # + # Regular expressions may be used for the pattern, and back refs may be + # used in the replacement text. Curly braces, commas and semi-colons are + # excluded from both the pattern and replacement text (let's keep parsing + # reasonable). + # + # For example: + # + # "src/org/onestepback/proj/A.java".pathmap("%{^src,bin}X.class") + # + # returns: + # + # "bin/org/onestepback/proj/A.class" + # + # If the replacement text is '*', then a block may be provided to perform + # some arbitrary calculation for the replacement. + # + # For example: + # + # "/path/to/file.TXT".pathmap("%X%{.*,*}x") { |ext| + # ext.downcase + # } + # + # Returns: + # + # "/path/to/file.txt" + # + def pathmap(spec=nil, &block) + return self if spec.nil? + result = '' + spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag| + case frag + when '%f' + result << File.basename(self) + when '%n' + result << File.basename(self).ext + when '%d' + result << File.dirname(self) + when '%x' + result << File.extname(self) + when '%X' + result << self.ext + when '%p' + result << self + when '%s' + result << (File::ALT_SEPARATOR || File::SEPARATOR) + when '%-' + # do nothing + when '%%' + result << "%" + when /%(-?\d+)d/ + result << pathmap_partial($1.to_i) + when /^%\{([^}]*)\}(\d*[dpfnxX])/ + patterns, operator = $1, $2 + result << pathmap('%' + operator).pathmap_replace(patterns, &block) + when /^%/ + fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'" + else + result << frag + end + end + result + end + end +end # class String + diff --git a/lib/rake/ext/time.rb b/lib/rake/ext/time.rb new file mode 100644 index 0000000000..ca3ea2a795 --- /dev/null +++ b/lib/rake/ext/time.rb @@ -0,0 +1,14 @@ +# ########################################################################### +# Extensions to time to allow comparisons with an early time class. +# +class Time + alias rake_original_time_compare :<=> + def <=>(other) + if Rake::EarlyTime === other + - other.<=>(self) + else + rake_original_time_compare(other) + end + end +end # class Time + diff --git a/lib/rake/file_creation_task.rb b/lib/rake/file_creation_task.rb new file mode 100644 index 0000000000..c87e2192bb --- /dev/null +++ b/lib/rake/file_creation_task.rb @@ -0,0 +1,24 @@ +require 'rake/file_task' +require 'rake/early_time' + +module Rake + + # A FileCreationTask is a file task that when used as a dependency will be + # needed if and only if the file has not been created. Once created, it is + # not re-triggered if any of its dependencies are newer, nor does trigger + # any rebuilds of tasks that depend on it whenever it is updated. + # + class FileCreationTask < FileTask + # Is this file task needed? Yes if it doesn't exist. + def needed? + ! File.exist?(name) + end + + # Time stamp for file creation task. This time stamp is earlier + # than any other time stamp. + def timestamp + Rake::EARLY + end + end + +end diff --git a/lib/rake/file_list.rb b/lib/rake/file_list.rb new file mode 100644 index 0000000000..9f780346f3 --- /dev/null +++ b/lib/rake/file_list.rb @@ -0,0 +1,403 @@ +require 'rake/cloneable' +require 'rake/file_utils_ext' +require 'rake/pathmap' + +###################################################################### +module Rake + + # ######################################################################### + # A FileList is essentially an array with a few helper methods defined to + # make file manipulation a bit easier. + # + # FileLists are lazy. When given a list of glob patterns for possible files + # to be included in the file list, instead of searching the file structures + # to find the files, a FileList holds the pattern for latter use. + # + # This allows us to define a number of FileList to match any number of + # files, but only search out the actual files when then FileList itself is + # actually used. The key is that the first time an element of the + # FileList/Array is requested, the pending patterns are resolved into a real + # list of file names. + # + class FileList + + include Cloneable + + # == Method Delegation + # + # The lazy evaluation magic of FileLists happens by implementing all the + # array specific methods to call +resolve+ before delegating the heavy + # lifting to an embedded array object (@items). + # + # In addition, there are two kinds of delegation calls. The regular kind + # delegates to the @items array and returns the result directly. Well, + # almost directly. It checks if the returned value is the @items object + # itself, and if so will return the FileList object instead. + # + # The second kind of delegation call is used in methods that normally + # return a new Array object. We want to capture the return value of these + # methods and wrap them in a new FileList object. We enumerate these + # methods in the +SPECIAL_RETURN+ list below. + + # List of array methods (that are not in +Object+) that need to be + # delegated. + ARRAY_METHODS = (Array.instance_methods - Object.instance_methods).map { |n| n.to_s } + + # List of additional methods that must be delegated. + MUST_DEFINE = %w[to_a inspect <=>] + + # List of methods that should not be delegated here (we define special + # versions of them explicitly below). + MUST_NOT_DEFINE = %w[to_a to_ary partition *] + + # List of delegated methods that return new array values which need + # wrapping. + SPECIAL_RETURN = %w[ + map collect sort sort_by select find_all reject grep + compact flatten uniq values_at + + - & | + ] + + DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE).collect{ |s| s.to_s }.sort.uniq + + # Now do the delegation. + DELEGATING_METHODS.each_with_index do |sym, i| + if SPECIAL_RETURN.include?(sym) + ln = __LINE__+1 + class_eval %{ + def #{sym}(*args, &block) + resolve + result = @items.send(:#{sym}, *args, &block) + FileList.new.import(result) + end + }, __FILE__, ln + else + ln = __LINE__+1 + class_eval %{ + def #{sym}(*args, &block) + resolve + result = @items.send(:#{sym}, *args, &block) + result.object_id == @items.object_id ? self : result + end + }, __FILE__, ln + end + end + + # Create a file list from the globbable patterns given. If you wish to + # perform multiple includes or excludes at object build time, use the + # "yield self" pattern. + # + # Example: + # file_list = FileList.new('lib/**/*.rb', 'test/test*.rb') + # + # pkg_files = FileList.new('lib/**/*') do |fl| + # fl.exclude(/\bCVS\b/) + # end + # + def initialize(*patterns) + @pending_add = [] + @pending = false + @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup + @exclude_procs = DEFAULT_IGNORE_PROCS.dup + @items = [] + patterns.each { |pattern| include(pattern) } + yield self if block_given? + end + + # Add file names defined by glob patterns to the file list. If an array + # is given, add each element of the array. + # + # Example: + # file_list.include("*.java", "*.cfg") + # file_list.include %w( math.c lib.h *.o ) + # + def include(*filenames) + # TODO: check for pending + filenames.each do |fn| + if fn.respond_to? :to_ary + include(*fn.to_ary) + else + @pending_add << fn + end + end + @pending = true + self + end + alias :add :include + + # Register a list of file name patterns that should be excluded from the + # list. Patterns may be regular expressions, glob patterns or regular + # strings. In addition, a block given to exclude will remove entries that + # return true when given to the block. + # + # Note that glob patterns are expanded against the file system. If a file + # is explicitly added to a file list, but does not exist in the file + # system, then an glob pattern in the exclude list will not exclude the + # file. + # + # Examples: + # FileList['a.c', 'b.c'].exclude("a.c") => ['b.c'] + # FileList['a.c', 'b.c'].exclude(/^a/) => ['b.c'] + # + # If "a.c" is a file, then ... + # FileList['a.c', 'b.c'].exclude("a.*") => ['b.c'] + # + # If "a.c" is not a file, then ... + # FileList['a.c', 'b.c'].exclude("a.*") => ['a.c', 'b.c'] + # + def exclude(*patterns, &block) + patterns.each do |pat| + @exclude_patterns << pat + end + if block_given? + @exclude_procs << block + end + resolve_exclude if ! @pending + self + end + + + # Clear all the exclude patterns so that we exclude nothing. + def clear_exclude + @exclude_patterns = [] + @exclude_procs = [] + self + end + + # Define equality. + def ==(array) + to_ary == array + end + + # Return the internal array object. + def to_a + resolve + @items + end + + # Return the internal array object. + def to_ary + to_a + end + + # Lie about our class. + def is_a?(klass) + klass == Array || super(klass) + end + alias kind_of? is_a? + + # Redefine * to return either a string or a new file list. + def *(other) + result = @items * other + case result + when Array + FileList.new.import(result) + else + result + end + end + + # Resolve all the pending adds now. + def resolve + if @pending + @pending = false + @pending_add.each do |fn| resolve_add(fn) end + @pending_add = [] + resolve_exclude + end + self + end + + def resolve_add(fn) + case fn + when %r{[*?\[\{]} + add_matching(fn) + else + self << fn + end + end + private :resolve_add + + def resolve_exclude + reject! { |fn| exclude?(fn) } + self + end + private :resolve_exclude + + # Return a new FileList with the results of running +sub+ against each + # element of the original list. + # + # Example: + # FileList['a.c', 'b.c'].sub(/\.c$/, '.o') => ['a.o', 'b.o'] + # + def sub(pat, rep) + inject(FileList.new) { |res, fn| res << fn.sub(pat,rep) } + end + + # Return a new FileList with the results of running +gsub+ against each + # element of the original list. + # + # Example: + # FileList['lib/test/file', 'x/y'].gsub(/\//, "\\") + # => ['lib\\test\\file', 'x\\y'] + # + def gsub(pat, rep) + inject(FileList.new) { |res, fn| res << fn.gsub(pat,rep) } + end + + # Same as +sub+ except that the original file list is modified. + def sub!(pat, rep) + each_with_index { |fn, i| self[i] = fn.sub(pat,rep) } + self + end + + # Same as +gsub+ except that the original file list is modified. + def gsub!(pat, rep) + each_with_index { |fn, i| self[i] = fn.gsub(pat,rep) } + self + end + + # Apply the pathmap spec to each of the included file names, returning a + # new file list with the modified paths. (See String#pathmap for + # details.) + def pathmap(spec=nil) + collect { |fn| fn.pathmap(spec) } + end + + # Return a new FileList with String#ext method applied to + # each member of the array. + # + # This method is a shortcut for: + # + # array.collect { |item| item.ext(newext) } + # + # +ext+ is a user added method for the Array class. + def ext(newext='') + collect { |fn| fn.ext(newext) } + end + + + # Grep each of the files in the filelist using the given pattern. If a + # block is given, call the block on each matching line, passing the file + # name, line number, and the matching line of text. If no block is given, + # a standard emacs style file:linenumber:line message will be printed to + # standard out. Returns the number of matched items. + def egrep(pattern, *options) + matched = 0 + each do |fn| + begin + open(fn, "rb", *options) do |inf| + count = 0 + inf.each do |line| + count += 1 + if pattern.match(line) + matched += 1 + if block_given? + yield fn, count, line + else + puts "#{fn}:#{count}:#{line}" + end + end + end + end + rescue StandardError => ex + $stderr.puts "Error while processing '#{fn}': #{ex}" + end + end + matched + end + + # Return a new file list that only contains file names from the current + # file list that exist on the file system. + def existing + select { |fn| File.exist?(fn) } + end + + # Modify the current file list so that it contains only file name that + # exist on the file system. + def existing! + resolve + @items = @items.select { |fn| File.exist?(fn) } + self + end + + # FileList version of partition. Needed because the nested arrays should + # be FileLists in this version. + def partition(&block) # :nodoc: + resolve + result = @items.partition(&block) + [ + FileList.new.import(result[0]), + FileList.new.import(result[1]), + ] + end + + # Convert a FileList to a string by joining all elements with a space. + def to_s + resolve + self.join(' ') + end + + # Add matching glob patterns. + def add_matching(pattern) + Dir[pattern].each do |fn| + self << fn unless exclude?(fn) + end + end + private :add_matching + + # Should the given file name be excluded? + def exclude?(fn) + return true if @exclude_patterns.any? do |pat| + case pat + when Regexp + fn =~ pat + when /[*?]/ + File.fnmatch?(pat, fn, File::FNM_PATHNAME) + else + fn == pat + end + end + @exclude_procs.any? { |p| p.call(fn) } + end + + DEFAULT_IGNORE_PATTERNS = [ + /(^|[\/\\])CVS([\/\\]|$)/, + /(^|[\/\\])\.svn([\/\\]|$)/, + /\.bak$/, + /~$/ + ] + DEFAULT_IGNORE_PROCS = [ + proc { |fn| fn =~ /(^|[\/\\])core$/ && ! File.directory?(fn) } + ] + + def import(array) + @items = array + self + end + + class << self + # Create a new file list including the files listed. Similar to: + # + # FileList.new(*args) + def [](*args) + new(*args) + end + end + end +end + +module Rake + class << self + + # Yield each file or directory component. + def each_dir_parent(dir) # :nodoc: + old_length = nil + while dir != '.' && dir.length != old_length + yield(dir) + old_length = dir.length + dir = File.dirname(dir) + end + end + end +end # module Rake diff --git a/lib/rake/file_task.rb b/lib/rake/file_task.rb new file mode 100644 index 0000000000..78902a86fd --- /dev/null +++ b/lib/rake/file_task.rb @@ -0,0 +1,47 @@ +require 'rake/task.rb' +require 'rake/early_time' + +module Rake + # ######################################################################### + # A FileTask is a task that includes time based dependencies. If any of a + # FileTask's prerequisites have a timestamp that is later than the file + # represented by this task, then the file must be rebuilt (using the + # supplied actions). + # + class FileTask < Task + + # Is this file task needed? Yes if it doesn't exist, or if its time stamp + # is out of date. + def needed? + ! File.exist?(name) || out_of_date?(timestamp) + end + + # Time stamp for file task. + def timestamp + if File.exist?(name) + File.mtime(name.to_s) + else + Rake::EARLY + end + end + + private + + # Are there any prerequisites with a later time than the given time stamp? + def out_of_date?(stamp) + @prerequisites.any? { |n| application[n, @scope].timestamp > stamp} + end + + # ---------------------------------------------------------------- + # Task class methods. + # + class << self + # Apply the scope to the task name according to the rules for this kind + # of task. File based tasks ignore the scope when creating the name. + def scope_name(scope, task_name) + task_name + end + end + end +end + diff --git a/lib/rake/file_utils.rb b/lib/rake/file_utils.rb new file mode 100644 index 0000000000..9e0198e644 --- /dev/null +++ b/lib/rake/file_utils.rb @@ -0,0 +1,112 @@ +require 'rbconfig' +require 'fileutils' + +# ########################################################################### +# This a FileUtils extension that defines several additional commands to be +# added to the FileUtils utility functions. +# +module FileUtils + # Path to the currently running Ruby program + RUBY = File.join( + RbConfig::CONFIG['bindir'], + RbConfig::CONFIG['ruby_install_name'] + RbConfig::CONFIG['EXEEXT']). + sub(/.*\s.*/m, '"\&"') + + OPT_TABLE['sh'] = %w(noop verbose) + OPT_TABLE['ruby'] = %w(noop verbose) + + # Run the system command +cmd+. If multiple arguments are given the command + # is not run with the shell (same semantics as Kernel::exec and + # Kernel::system). + # + # Example: + # sh %{ls -ltr} + # + # sh 'ls', 'file with spaces' + # + # # check exit status after command runs + # sh %{grep pattern file} do |ok, res| + # if ! ok + # puts "pattern not found (status = #{res.exitstatus})" + # end + # end + # + def sh(*cmd, &block) + options = (Hash === cmd.last) ? cmd.pop : {} + shell_runner = block_given? ? block : create_shell_runner(cmd) + set_verbose_option(options) + options[:noop] ||= Rake::FileUtilsExt.nowrite_flag + Rake.rake_check_options options, :noop, :verbose + Rake.rake_output_message cmd.join(" ") if options[:verbose] + unless options[:noop] + res = rake_system(*cmd) + status = $? + status = PseudoStatus.new(1) if !res && status.nil? + shell_runner.call(res, status) + end + end + + def create_shell_runner(cmd) + show_command = cmd.join(" ") + show_command = show_command[0,42] + "..." unless $trace + lambda { |ok, status| + ok or fail "Command failed with status (#{status.exitstatus}): [#{show_command}]" + } + end + private :create_shell_runner + + def set_verbose_option(options) + if options[:verbose].nil? + options[:verbose] = Rake::FileUtilsExt.verbose_flag.nil? || Rake::FileUtilsExt.verbose_flag + end + end + private :set_verbose_option + + def rake_system(*cmd) + Rake::AltSystem.system(*cmd) + end + private :rake_system + + # Run a Ruby interpreter with the given arguments. + # + # Example: + # ruby %{-pe '$_.upcase!' 1 then + sh(*([RUBY] + args + [options]), &block) + else + sh("#{RUBY} #{args.first}", options, &block) + end + end + + LN_SUPPORTED = [true] + + # Attempt to do a normal file link, but fall back to a copy if the link + # fails. + def safe_ln(*args) + unless LN_SUPPORTED[0] + cp(*args) + else + begin + ln(*args) + rescue StandardError, NotImplementedError + LN_SUPPORTED[0] = false + cp(*args) + end + end + end + + # Split a file path into individual directory names. + # + # Example: + # split_all("a/b/c") => ['a', 'b', 'c'] + # + def split_all(path) + head, tail = File.split(path) + return [tail] if head == '.' || tail == '/' + return [head, tail] if head == '/' + return split_all(head) + [tail] + end +end diff --git a/lib/rake/file_utils_ext.rb b/lib/rake/file_utils_ext.rb new file mode 100644 index 0000000000..7c22f80687 --- /dev/null +++ b/lib/rake/file_utils_ext.rb @@ -0,0 +1,142 @@ +require 'rake/file_utils' + +module Rake + # + # FileUtilsExt provides a custom version of the FileUtils methods + # that respond to the verbose and nowrite + # commands. + # + module FileUtilsExt + include FileUtils + + class << self + attr_accessor :verbose_flag, :nowrite_flag + end + FileUtilsExt.verbose_flag = nil + FileUtilsExt.nowrite_flag = false + + $fileutils_verbose = true + $fileutils_nowrite = false + + FileUtils::OPT_TABLE.each do |name, opts| + default_options = [] + if opts.include?(:verbose) || opts.include?("verbose") + default_options << ':verbose => FileUtilsExt.verbose_flag' + end + if opts.include?(:noop) || opts.include?("noop") + default_options << ':noop => FileUtilsExt.nowrite_flag' + end + + next if default_options.empty? + module_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{name}( *args, &block ) + super( + *rake_merge_option(args, + #{default_options.join(', ')} + ), &block) + end + EOS + end + + # Get/set the verbose flag controlling output from the FileUtils + # utilities. If verbose is true, then the utility method is + # echoed to standard output. + # + # Examples: + # verbose # return the current value of the + # # verbose flag + # verbose(v) # set the verbose flag to _v_. + # verbose(v) { code } # Execute code with the verbose flag set + # # temporarily to _v_. Return to the + # # original value when code is done. + def verbose(value=nil) + oldvalue = FileUtilsExt.verbose_flag + FileUtilsExt.verbose_flag = value unless value.nil? + if block_given? + begin + yield + ensure + FileUtilsExt.verbose_flag = oldvalue + end + end + FileUtilsExt.verbose_flag + end + + # Get/set the nowrite flag controlling output from the FileUtils + # utilities. If verbose is true, then the utility method is + # echoed to standard output. + # + # Examples: + # nowrite # return the current value of the + # # nowrite flag + # nowrite(v) # set the nowrite flag to _v_. + # nowrite(v) { code } # Execute code with the nowrite flag set + # # temporarily to _v_. Return to the + # # original value when code is done. + def nowrite(value=nil) + oldvalue = FileUtilsExt.nowrite_flag + FileUtilsExt.nowrite_flag = value unless value.nil? + if block_given? + begin + yield + ensure + FileUtilsExt.nowrite_flag = oldvalue + end + end + oldvalue + end + + # Use this function to prevent potentially destructive ruby code + # from running when the :nowrite flag is set. + # + # Example: + # + # when_writing("Building Project") do + # project.build + # end + # + # The following code will build the project under normal + # conditions. If the nowrite(true) flag is set, then the example + # will print: + # + # DRYRUN: Building Project + # + # instead of actually building the project. + # + def when_writing(msg=nil) + if FileUtilsExt.nowrite_flag + $stderr.puts "DRYRUN: #{msg}" if msg + else + yield + end + end + + # Merge the given options with the default values. + def rake_merge_option(args, defaults) + if Hash === args.last + defaults.update(args.last) + args.pop + end + args.push defaults + args + end + + # Send the message to the default rake output (which is $stderr). + def rake_output_message(message) + $stderr.puts(message) + end + + # Check that the options do not contain options not listed in + # +optdecl+. An ArgumentError exception is thrown if non-declared + # options are found. + def rake_check_options(options, *optdecl) + h = options.dup + optdecl.each do |name| + h.delete name + end + raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty? + end + + extend self + end +end diff --git a/lib/rake/gempackagetask.rb b/lib/rake/gempackagetask.rb index 3a99ff2b87..51e92236fc 100644 --- a/lib/rake/gempackagetask.rb +++ b/lib/rake/gempackagetask.rb @@ -1,95 +1,13 @@ -# Define a package task library to aid in the definition of GEM -# packages. +# rake/gempackagetask is deprecated in favor of rubygems/package_task + +warn 'rake/gempackagetask is deprecated. Use rubygems/package_task instead' require 'rubygems' +require 'rubygems/package_task' + require 'rake' -require 'rake/packagetask' -require 'rubygems/user_interaction' -require 'rubygems/builder' module Rake - - # Create a package based upon a Gem spec. Gem packages, as well as - # zip files and tar/gzipped packages can be produced by this task. - # - # In addition to the Rake targets generated by PackageTask, a - # GemPackageTask will also generate the following tasks: - # - # ["package_dir/name-version.gem"] - # Create a Ruby GEM package with the given name and version. - # - # Example using a Ruby GEM spec: - # - # require 'rubygems' - # - # spec = Gem::Specification.new do |s| - # s.platform = Gem::Platform::RUBY - # s.summary = "Ruby based make-like utility." - # s.name = 'rake' - # s.version = PKG_VERSION - # s.requirements << 'none' - # s.require_path = 'lib' - # s.autorequire = 'rake' - # s.files = PKG_FILES - # s.description = < [:gem] - desc "Build the gem file #{gem_file}" - task :gem => ["#{package_dir}/#{gem_file}"] - file "#{package_dir}/#{gem_file}" => [package_dir] + @gem_spec.files do - when_writing("Creating GEM") { - Gem::Builder.new(gem_spec).build - verbose(true) { - mv gem_file, "#{package_dir}/#{gem_file}" - } - } - end - end - - def gem_file - if @gem_spec.platform == Gem::Platform::RUBY - "#{package_name}.gem" - else - "#{package_name}-#{@gem_spec.platform}.gem" - end - end - - end + GemPackageTask = Gem::PackageTask end + diff --git a/lib/rake/invocation_chain.rb b/lib/rake/invocation_chain.rb new file mode 100644 index 0000000000..8a01ab4c29 --- /dev/null +++ b/lib/rake/invocation_chain.rb @@ -0,0 +1,51 @@ +module Rake + + #################################################################### + # InvocationChain tracks the chain of task invocations to detect + # circular dependencies. + class InvocationChain + def initialize(value, tail) + @value = value + @tail = tail + end + + def member?(obj) + @value == obj || @tail.member?(obj) + end + + def append(value) + if member?(value) + fail RuntimeError, "Circular dependency detected: #{to_s} => #{value}" + end + self.class.new(value, self) + end + + def to_s + "#{prefix}#{@value}" + end + + def self.append(value, chain) + chain.append(value) + end + + private + + def prefix + "#{@tail.to_s} => " + end + + class EmptyInvocationChain + def member?(obj) + false + end + def append(value) + InvocationChain.new(value, self) + end + def to_s + "TOP" + end + end + + EMPTY = EmptyInvocationChain.new + end +end diff --git a/lib/rake/invocation_exception_mixin.rb b/lib/rake/invocation_exception_mixin.rb new file mode 100644 index 0000000000..84ff3353ba --- /dev/null +++ b/lib/rake/invocation_exception_mixin.rb @@ -0,0 +1,16 @@ +module Rake + module InvocationExceptionMixin + # Return the invocation chain (list of Rake tasks) that were in + # effect when this exception was detected by rake. May be null if + # no tasks were active. + def chain + @rake_invocation_chain ||= nil + end + + # Set the invocation chain in effect when this exception was + # detected. + def chain=(value) + @rake_invocation_chain = value + end + end +end diff --git a/lib/rake/lib/project.rake b/lib/rake/lib/project.rake new file mode 100644 index 0000000000..a5497328a7 --- /dev/null +++ b/lib/rake/lib/project.rake @@ -0,0 +1,21 @@ +task "create:project" => ["lib", "test", "Rakefile"] + +directory "lib" +directory "test" + +file "Rakefile" do + File.open("Rakefile", "w") do |out| + out.puts %{# -*- ruby -*- + +require 'rake/clean' +require 'rake/testtask' + +task :default => :test + +Rake::TestTask.new do |t| + t.verbose = false + t.test_files = FileList['test/test_*.rb'] +end +} + end +end diff --git a/lib/rake/loaders/makefile.rb b/lib/rake/loaders/makefile.rb index 9a2ac8090e..4ece4323af 100644 --- a/lib/rake/loaders/makefile.rb +++ b/lib/rake/loaders/makefile.rb @@ -2,11 +2,13 @@ module Rake # Makefile loader to be used with the import file loader. class MakefileLoader + include Rake::DSL + SPACE_MARK = "\0" # Load the makefile dependencies in +fn+. def load(fn) - lines = open(fn) {|mf| mf.read} + lines = File.read fn lines.gsub!(/\\ /, SPACE_MARK) lines.gsub!(/#[^\n]*\n/m, "") lines.gsub!(/\\\n/, ' ') @@ -21,7 +23,7 @@ module Rake def process_line(line) file_tasks, args = line.split(':', 2) return if args.nil? - dependents = args.split.map {|arg| respace(arg)} + dependents = args.split.map { |d| respace(d) } file_tasks.scan(/\S+/) do |file_task| file_task = respace(file_task) file file_task => dependents @@ -29,7 +31,7 @@ module Rake end def respace(str) - str.tr(SPACE_MARK, ' ') + str.tr SPACE_MARK, ' ' end end diff --git a/lib/rake/multi_task.rb b/lib/rake/multi_task.rb new file mode 100644 index 0000000000..21c8de732f --- /dev/null +++ b/lib/rake/multi_task.rb @@ -0,0 +1,16 @@ +module Rake + + # Same as a regular task, but the immediate prerequisites are done in + # parallel using Ruby threads. + # + class MultiTask < Task + private + def invoke_prerequisites(args, invocation_chain) + threads = @prerequisites.collect { |p| + Thread.new(p) { |r| application[r, @scope].invoke_with_call_chain(args, invocation_chain) } + } + threads.each { |t| t.join } + end + end + +end diff --git a/lib/rake/name_space.rb b/lib/rake/name_space.rb new file mode 100644 index 0000000000..e1cc0940b8 --- /dev/null +++ b/lib/rake/name_space.rb @@ -0,0 +1,25 @@ +module Rake + + # The NameSpace class will lookup task names in the the scope + # defined by a +namespace+ command. + # + class NameSpace + + # Create a namespace lookup object using the given task manager + # and the list of scopes. + def initialize(task_manager, scope_list) + @task_manager = task_manager + @scope = scope_list.dup + end + + # Lookup a task named +name+ in the namespace. + def [](name) + @task_manager.lookup(name, @scope) + end + + # Return the list of tasks defined in this and nested namespaces. + def tasks + @task_manager.tasks_in_scope(@scope) + end + end +end diff --git a/lib/rake/packagetask.rb b/lib/rake/packagetask.rb index e77345c198..08c1a8c025 100644 --- a/lib/rake/packagetask.rb +++ b/lib/rake/packagetask.rb @@ -72,7 +72,10 @@ module Rake # Zip command for zipped archives. The default is 'zip'. attr_accessor :zip_command - # Create a Package Task with the given name and version. + # Create a Package Task with the given name and version. Use +:noversion+ + # as the version to build a package without a version or to provide a + # fully-versioned package name. + def initialize(name=nil, version=nil) init(name, version) yield self if block_given? diff --git a/lib/rake/pathmap.rb b/lib/rake/pathmap.rb new file mode 100644 index 0000000000..2275724341 --- /dev/null +++ b/lib/rake/pathmap.rb @@ -0,0 +1 @@ +require 'rake/ext/string' diff --git a/lib/rake/pseudo_status.rb b/lib/rake/pseudo_status.rb new file mode 100644 index 0000000000..b58df3da18 --- /dev/null +++ b/lib/rake/pseudo_status.rb @@ -0,0 +1,24 @@ +module Rake + + #################################################################### + # Exit status class for times the system just gives us a nil. + class PseudoStatus + attr_reader :exitstatus + def initialize(code=0) + @exitstatus = code + end + def to_i + @exitstatus << 8 + end + def >>(n) + to_i >> n + end + def stopped? + false + end + def exited? + true + end + end + +end diff --git a/lib/rake/rake_module.rb b/lib/rake/rake_module.rb new file mode 100644 index 0000000000..a9d210c637 --- /dev/null +++ b/lib/rake/rake_module.rb @@ -0,0 +1,29 @@ +require 'rake/application' + +module Rake + + # Rake module singleton methods. + # + class << self + # Current Rake Application + def application + @application ||= Rake::Application.new + end + + # Set the current Rake application object. + def application=(app) + @application = app + end + + # Return the original directory where the Rake application was started. + def original_dir + application.original_dir + end + + # Load a rakefile. + def load_rakefile(path) + load(path) + end + end + +end diff --git a/lib/rake/rake_test_loader.rb b/lib/rake/rake_test_loader.rb index 8d7dad3c94..045a12fdce 100644 --- a/lib/rake/rake_test_loader.rb +++ b/lib/rake/rake_test_loader.rb @@ -1,5 +1,13 @@ -#!/usr/bin/env ruby +require 'rake' # Load the test files from the command line. -ARGV.each { |f| load f unless f =~ /^-/ } +ARGV.each do |f| + next if f =~ /^-/ + + if f =~ /\*/ + FileList[f].to_a.each { |fn| require File.expand_path(fn) } + else + require File.expand_path(f) + end +end diff --git a/lib/rake/rdoctask.rb b/lib/rake/rdoctask.rb index 59758b85cd..5faf836b37 100644 --- a/lib/rake/rdoctask.rb +++ b/lib/rake/rdoctask.rb @@ -1,4 +1,232 @@ -warn 'rake/rdoctask is deprecated. Use rdoc/task instead' +# rake/rdoctask is deprecated in favor of rdoc/task -require 'rdoc/task' +if Rake.application + Rake.application.deprecate('require \'rake/rdoctask\'', 'require \'rdoc/task\' (in RDoc 2.4.2+)', __FILE__) +end + +require 'rubygems' + +begin + gem 'rdoc' + require 'rdoc' + require 'rdoc/task' +rescue LoadError, Gem::LoadError +end + +if defined?(RDoc::Task) then + module Rake + RDocTask = RDoc::Task unless const_defined? :RDocTask + end +else + require 'rake' + require 'rake/tasklib' + + module Rake + + # NOTE: Rake::RDocTask is deprecated in favor of RDoc:Task which is included + # in RDoc 2.4.2+. Use require 'rdoc/task' to require it. + # + # Create a documentation task that will generate the RDoc files for + # a project. + # + # The RDocTask will create the following targets: + # + # [rdoc] + # Main task for this RDOC task. + # + # [:clobber_rdoc] + # Delete all the rdoc files. This target is automatically + # added to the main clobber target. + # + # [:rerdoc] + # Rebuild the rdoc files from scratch, even if they are not out + # of date. + # + # Simple Example: + # + # Rake::RDocTask.new do |rd| + # rd.main = "README.rdoc" + # rd.rdoc_files.include("README.rdoc", "lib/**/*.rb") + # end + # + # The +rd+ object passed to the block is an RDocTask object. See the + # attributes list for the RDocTask class for available customization options. + # + # == Specifying different task names + # + # You may wish to give the task a different name, such as if you are + # generating two sets of documentation. For instance, if you want to have a + # development set of documentation including private methods: + # + # Rake::RDocTask.new(:rdoc_dev) do |rd| + # rd.main = "README.doc" + # rd.rdoc_files.include("README.rdoc", "lib/**/*.rb") + # rd.options << "--all" + # end + # + # The tasks would then be named :rdoc_dev, :clobber_rdoc_dev, and + # :rerdoc_dev. + # + # If you wish to have completely different task names, then pass a Hash as + # first argument. With the :rdoc, :clobber_rdoc and + # :rerdoc options, you can customize the task names to your liking. + # For example: + # + # Rake::RDocTask.new(:rdoc => "rdoc", :clobber_rdoc => "rdoc:clean", :rerdoc => "rdoc:force") + # + # This will create the tasks :rdoc, :rdoc_clean and + # :rdoc:force. + # + class RDocTask < TaskLib + # Name of the main, top level task. (default is :rdoc) + attr_accessor :name + + # Name of directory to receive the html output files. (default is "html") + attr_accessor :rdoc_dir + + # Title of RDoc documentation. (defaults to rdoc's default) + attr_accessor :title + + # Name of file to be used as the main, top level file of the + # RDoc. (default is none) + attr_accessor :main + + # Name of template to be used by rdoc. (defaults to rdoc's default) + attr_accessor :template + + # List of files to be included in the rdoc generation. (default is []) + attr_accessor :rdoc_files + + # Additional list of options to be passed rdoc. (default is []) + attr_accessor :options + + # Whether to run the rdoc process as an external shell (default is false) + attr_accessor :external + + attr_accessor :inline_source + + # Create an RDoc task with the given name. See the RDocTask class overview + # for documentation. + def initialize(name = :rdoc) # :yield: self + if name.is_a?(Hash) + invalid_options = name.keys.map { |k| k.to_sym } - [:rdoc, :clobber_rdoc, :rerdoc] + if !invalid_options.empty? + raise ArgumentError, "Invalid option(s) passed to RDocTask.new: #{invalid_options.join(", ")}" + end + end + + @name = name + @rdoc_files = Rake::FileList.new + @rdoc_dir = 'html' + @main = nil + @title = nil + @template = nil + @external = false + @inline_source = true + @options = [] + yield self if block_given? + define + end + + # Create the tasks defined by this task lib. + def define + if rdoc_task_name != "rdoc" + desc "Build the RDOC HTML Files" + else + desc "Build the #{rdoc_task_name} HTML Files" + end + task rdoc_task_name + + desc "Force a rebuild of the RDOC files" + task rerdoc_task_name => [clobber_task_name, rdoc_task_name] + + desc "Remove rdoc products" + task clobber_task_name do + rm_r rdoc_dir rescue nil + end + + task :clobber => [clobber_task_name] + + directory @rdoc_dir + task rdoc_task_name => [rdoc_target] + file rdoc_target => @rdoc_files + [Rake.application.rakefile] do + rm_r @rdoc_dir rescue nil + @before_running_rdoc.call if @before_running_rdoc + args = option_list + @rdoc_files + if @external + argstring = args.join(' ') + sh %{ruby -Ivendor vendor/rd #{argstring}} + else + require 'rdoc/rdoc' + RDoc::RDoc.new.document(args) + end + end + self + end + + def option_list + result = @options.dup + result << "-o" << @rdoc_dir + result << "--main" << quote(main) if main + result << "--title" << quote(title) if title + result << "-T" << quote(template) if template + result << "--inline-source" if inline_source && !@options.include?("--inline-source") && !@options.include?("-S") + result + end + + def quote(str) + if @external + "'#{str}'" + else + str + end + end + + def option_string + option_list.join(' ') + end + + # The block passed to this method will be called just before running the + # RDoc generator. It is allowed to modify RDocTask attributes inside the + # block. + def before_running_rdoc(&block) + @before_running_rdoc = block + end + + private + + def rdoc_target + "#{rdoc_dir}/index.html" + end + + def rdoc_task_name + case name + when Hash + (name[:rdoc] || "rdoc").to_s + else + name.to_s + end + end + + def clobber_task_name + case name + when Hash + (name[:clobber_rdoc] || "clobber_rdoc").to_s + else + "clobber_#{name}" + end + end + + def rerdoc_task_name + case name + when Hash + (name[:rerdoc] || "rerdoc").to_s + else + "re#{name}" + end + end + + end + end +end diff --git a/lib/rake/ruby182_test_unit_fix.rb b/lib/rake/ruby182_test_unit_fix.rb new file mode 100755 index 0000000000..9e411ed51a --- /dev/null +++ b/lib/rake/ruby182_test_unit_fix.rb @@ -0,0 +1,25 @@ +# Local Rake override to fix bug in Ruby 0.8.2 +module Test # :nodoc: + # Local Rake override to fix bug in Ruby 0.8.2 + module Unit # :nodoc: + # Local Rake override to fix bug in Ruby 0.8.2 + module Collector # :nodoc: + # Local Rake override to fix bug in Ruby 0.8.2 + class Dir # :nodoc: + undef collect_file + def collect_file(name, suites, already_gathered) # :nodoc: + dir = File.dirname(File.expand_path(name)) + $:.unshift(dir) unless $:.first == dir + if(@req) + @req.require(name) + else + require(name) + end + find_test_cases(already_gathered).each{|t| add_suite(suites, t.suite)} + ensure + $:.delete_at $:.rindex(dir) + end + end + end + end +end diff --git a/lib/rake/rule_recursion_overflow_error.rb b/lib/rake/rule_recursion_overflow_error.rb new file mode 100644 index 0000000000..da4318da9d --- /dev/null +++ b/lib/rake/rule_recursion_overflow_error.rb @@ -0,0 +1,20 @@ + +module Rake + + # Error indicating a recursion overflow error in task selection. + class RuleRecursionOverflowError < StandardError + def initialize(*args) + super + @targets = [] + end + + def add_target(target) + @targets << target + end + + def message + super + ": [" + @targets.reverse.join(' => ') + "]" + end + end + +end diff --git a/lib/rake/runtest.rb b/lib/rake/runtest.rb index f6928d57b8..2b98a60cae 100644 --- a/lib/rake/runtest.rb +++ b/lib/rake/runtest.rb @@ -6,12 +6,12 @@ module Rake def run_tests(pattern='test/test*.rb', log_enabled=false) Dir["#{pattern}"].each { |fn| - puts fn if log_enabled + $stderr.puts fn if log_enabled begin - load fn + require fn rescue Exception => ex - puts "Error in #{fn}: #{ex.message}" - puts ex.backtrace + $stderr.puts "Error in #{fn}: #{ex.message}" + $stderr.puts ex.backtrace assert false end } diff --git a/lib/rake/task.rb b/lib/rake/task.rb new file mode 100644 index 0000000000..f977d18711 --- /dev/null +++ b/lib/rake/task.rb @@ -0,0 +1,327 @@ +require 'rake/invocation_exception_mixin' + +module Rake + + # ######################################################################### + # A Task is the basic unit of work in a Rakefile. Tasks have associated + # actions (possibly more than one) and a list of prerequisites. When + # invoked, a task will first ensure that all of its prerequisites have an + # opportunity to run and then it will execute its own actions. + # + # Tasks are not usually created directly using the new method, but rather + # use the +file+ and +task+ convenience methods. + # + class Task + # List of prerequisites for a task. + attr_reader :prerequisites + + # List of actions attached to a task. + attr_reader :actions + + # Application owning this task. + attr_accessor :application + + # Comment for this task. Restricted to a single line of no more than 50 + # characters. + attr_reader :comment + + # Full text of the (possibly multi-line) comment. + attr_reader :full_comment + + # Array of nested namespaces names used for task lookup by this task. + attr_reader :scope + + # File/Line locations of each of the task definitions for this + # task (only valid if the task was defined with the detect + # location option set). + attr_reader :locations + + # Return task name + def to_s + name + end + + def inspect + "<#{self.class} #{name} => [#{prerequisites.join(', ')}]>" + end + + # List of sources for task. + attr_writer :sources + def sources + @sources ||= [] + end + + # List of prerequisite tasks + def prerequisite_tasks + prerequisites.collect { |pre| lookup_prerequisite(pre) } + end + + def lookup_prerequisite(prerequisite_name) + application[prerequisite_name, @scope] + end + private :lookup_prerequisite + + # First source from a rule (nil if no sources) + def source + @sources.first if defined?(@sources) + end + + # Create a task named +task_name+ with no actions or prerequisites. Use + # +enhance+ to add actions and prerequisites. + def initialize(task_name, app) + @name = task_name.to_s + @prerequisites = [] + @actions = [] + @already_invoked = false + @full_comment = nil + @comment = nil + @lock = Monitor.new + @application = app + @scope = app.current_scope + @arg_names = nil + @locations = [] + end + + # Enhance a task with prerequisites or actions. Returns self. + def enhance(deps=nil, &block) + @prerequisites |= deps if deps + @actions << block if block_given? + self + end + + # Name of the task, including any namespace qualifiers. + def name + @name.to_s + end + + # Name of task with argument list description. + def name_with_args # :nodoc: + if arg_description + "#{name}#{arg_description}" + else + name + end + end + + # Argument description (nil if none). + def arg_description # :nodoc: + @arg_names ? "[#{(arg_names || []).join(',')}]" : nil + end + + # Name of arguments for this task. + def arg_names + @arg_names || [] + end + + # Reenable the task, allowing its tasks to be executed if the task + # is invoked again. + def reenable + @already_invoked = false + end + + # Clear the existing prerequisites and actions of a rake task. + def clear + clear_prerequisites + clear_actions + self + end + + # Clear the existing prerequisites of a rake task. + def clear_prerequisites + prerequisites.clear + self + end + + # Clear the existing actions on a rake task. + def clear_actions + actions.clear + self + end + + # Invoke the task if it is needed. Prerequisites are invoked first. + def invoke(*args) + task_args = TaskArguments.new(arg_names, args) + invoke_with_call_chain(task_args, InvocationChain::EMPTY) + end + + # Same as invoke, but explicitly pass a call chain to detect + # circular dependencies. + def invoke_with_call_chain(task_args, invocation_chain) # :nodoc: + new_chain = InvocationChain.append(self, invocation_chain) + @lock.synchronize do + if application.options.trace + $stderr.puts "** Invoke #{name} #{format_trace_flags}" + end + return if @already_invoked + @already_invoked = true + invoke_prerequisites(task_args, new_chain) + execute(task_args) if needed? + end + rescue Exception => ex + add_chain_to(ex, new_chain) + raise ex + end + protected :invoke_with_call_chain + + def add_chain_to(exception, new_chain) + exception.extend(InvocationExceptionMixin) unless exception.respond_to?(:chain) + exception.chain = new_chain if exception.chain.nil? + end + private :add_chain_to + + # Invoke all the prerequisites of a task. + def invoke_prerequisites(task_args, invocation_chain) # :nodoc: + prerequisite_tasks.each { |prereq| + prereq_args = task_args.new_scope(prereq.arg_names) + prereq.invoke_with_call_chain(prereq_args, invocation_chain) + } + end + + # Format the trace flags for display. + def format_trace_flags + flags = [] + flags << "first_time" unless @already_invoked + flags << "not_needed" unless needed? + flags.empty? ? "" : "(" + flags.join(", ") + ")" + end + private :format_trace_flags + + # Execute the actions associated with this task. + def execute(args=nil) + args ||= EMPTY_TASK_ARGS + if application.options.dryrun + $stderr.puts "** Execute (dry run) #{name}" + return + end + if application.options.trace + $stderr.puts "** Execute #{name}" + end + application.enhance_with_matching_rule(name) if @actions.empty? + @actions.each do |act| + case act.arity + when 1 + act.call(self) + else + act.call(self, args) + end + end + end + + # Is this task needed? + def needed? + true + end + + # Timestamp for this task. Basic tasks return the current time for their + # time stamp. Other tasks can be more sophisticated. + def timestamp + prerequisite_tasks.collect { |pre| pre.timestamp }.max || Time.now + end + + # Add a description to the task. The description can consist of an option + # argument list (enclosed brackets) and an optional comment. + def add_description(description) + return if ! description + comment = description.strip + add_comment(comment) if comment && ! comment.empty? + end + + # Writing to the comment attribute is the same as adding a description. + def comment=(description) + add_description(description) + end + + # Add a comment to the task. If a comment already exists, separate + # the new comment with " / ". + def add_comment(comment) + if @full_comment + @full_comment << " / " + else + @full_comment = '' + end + @full_comment << comment + if @full_comment =~ /\A([^.]+?\.)( |$)/ + @comment = $1 + else + @comment = @full_comment + end + end + private :add_comment + + # Set the names of the arguments for this task. +args+ should be + # an array of symbols, one for each argument name. + def set_arg_names(args) + @arg_names = args.map { |a| a.to_sym } + end + + # Return a string describing the internal state of a task. Useful for + # debugging. + def investigation + result = "------------------------------\n" + result << "Investigating #{name}\n" + result << "class: #{self.class}\n" + result << "task needed: #{needed?}\n" + result << "timestamp: #{timestamp}\n" + result << "pre-requisites: \n" + prereqs = prerequisite_tasks + prereqs.sort! {|a,b| a.timestamp <=> b.timestamp} + prereqs.each do |p| + result << "--#{p.name} (#{p.timestamp})\n" + end + latest_prereq = prerequisite_tasks.collect { |pre| pre.timestamp }.max + result << "latest-prerequisite time: #{latest_prereq}\n" + result << "................................\n\n" + return result + end + + # ---------------------------------------------------------------- + # Rake Module Methods + # + class << self + + # Clear the task list. This cause rake to immediately forget all the + # tasks that have been assigned. (Normally used in the unit tests.) + def clear + Rake.application.clear + end + + # List of all defined tasks. + def tasks + Rake.application.tasks + end + + # Return a task with the given name. If the task is not currently + # known, try to synthesize one from the defined rules. If no rules are + # found, but an existing file matches the task name, assume it is a file + # task with no dependencies or actions. + def [](task_name) + Rake.application[task_name] + end + + # TRUE if the task name is already defined. + def task_defined?(task_name) + Rake.application.lookup(task_name) != nil + end + + # Define a task given +args+ and an option block. If a rule with the + # given name already exists, the prerequisites and actions are added to + # the existing task. Returns the defined task. + def define_task(*args, &block) + Rake.application.define_task(self, *args, &block) + end + + # Define a rule for synthesizing tasks. + def create_rule(*args, &block) + Rake.application.create_rule(*args, &block) + end + + # Apply the scope to the task name according to the rules for + # this kind of task. Generic tasks will accept the scope as + # part of the name. + def scope_name(scope, task_name) + (scope + [task_name]).join(':') + end + + end # class << Rake::Task + end # class Rake::Task +end diff --git a/lib/rake/task_argument_error.rb b/lib/rake/task_argument_error.rb new file mode 100644 index 0000000000..3e1dda64db --- /dev/null +++ b/lib/rake/task_argument_error.rb @@ -0,0 +1,7 @@ +module Rake + + # Error indicating an ill-formed task declaration. + class TaskArgumentError < ArgumentError + end + +end diff --git a/lib/rake/task_arguments.rb b/lib/rake/task_arguments.rb new file mode 100644 index 0000000000..ab404d67f5 --- /dev/null +++ b/lib/rake/task_arguments.rb @@ -0,0 +1,74 @@ +module Rake + + #################################################################### + # TaskArguments manage the arguments passed to a task. + # + class TaskArguments + include Enumerable + + attr_reader :names + + # Create a TaskArgument object with a list of named arguments + # (given by :names) and a set of associated values (given by + # :values). :parent is the parent argument object. + def initialize(names, values, parent=nil) + @names = names + @parent = parent + @hash = {} + names.each_with_index { |name, i| + @hash[name.to_sym] = values[i] unless values[i].nil? + } + end + + # Create a new argument scope using the prerequisite argument + # names. + def new_scope(names) + values = names.collect { |n| self[n] } + self.class.new(names, values, self) + end + + # Find an argument value by name or index. + def [](index) + lookup(index.to_sym) + end + + # Specify a hash of default values for task arguments. Use the + # defaults only if there is no specific value for the given + # argument. + def with_defaults(defaults) + @hash = defaults.merge(@hash) + end + + def each(&block) + @hash.each(&block) + end + + def method_missing(sym, *args, &block) + lookup(sym.to_sym) + end + + def to_hash + @hash + end + + def to_s + @hash.inspect + end + + def inspect + to_s + end + + protected + + def lookup(name) + if @hash.has_key?(name) + @hash[name] + elsif @parent + @parent.lookup(name) + end + end + end + + EMPTY_TASK_ARGS = TaskArguments.new([], []) +end diff --git a/lib/rake/task_manager.rb b/lib/rake/task_manager.rb new file mode 100644 index 0000000000..4c3c26aa3a --- /dev/null +++ b/lib/rake/task_manager.rb @@ -0,0 +1,307 @@ +module Rake + + # The TaskManager module is a mixin for managing tasks. + module TaskManager + # Track the last comment made in the Rakefile. + attr_accessor :last_description + alias :last_comment :last_description # Backwards compatibility + + def initialize + super + @tasks = Hash.new + @rules = Array.new + @scope = Array.new + @last_description = nil + end + + def create_rule(*args, &block) + pattern, _, deps = resolve_args(args) + pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern + @rules << [pattern, deps, block] + end + + def define_task(task_class, *args, &block) + task_name, arg_names, deps = resolve_args(args) + task_name = task_class.scope_name(@scope, task_name) + deps = [deps] unless deps.respond_to?(:to_ary) + deps = deps.collect {|d| d.to_s } + task = intern(task_class, task_name) + task.set_arg_names(arg_names) unless arg_names.empty? + if Rake::TaskManager.record_task_metadata + add_location(task) + task.add_description(get_description(task)) + end + task.enhance(deps, &block) + end + + # Lookup a task. Return an existing task if found, otherwise + # create a task of the current type. + def intern(task_class, task_name) + @tasks[task_name.to_s] ||= task_class.new(task_name, self) + end + + # Find a matching task for +task_name+. + def [](task_name, scopes=nil) + task_name = task_name.to_s + self.lookup(task_name, scopes) or + enhance_with_matching_rule(task_name) or + synthesize_file_task(task_name) or + fail "Don't know how to build task '#{task_name}'" + end + + def synthesize_file_task(task_name) + return nil unless File.exist?(task_name) + define_task(Rake::FileTask, task_name) + end + + # Resolve the arguments for a task/rule. Returns a triplet of + # [task_name, arg_name_list, prerequisites]. + def resolve_args(args) + if args.last.is_a?(Hash) + deps = args.pop + resolve_args_with_dependencies(args, deps) + else + resolve_args_without_dependencies(args) + end + end + + # Resolve task arguments for a task or rule when there are no + # dependencies declared. + # + # The patterns recognized by this argument resolving function are: + # + # task :t + # task :t, [:a] + # task :t, :a (deprecated) + # + def resolve_args_without_dependencies(args) + task_name = args.shift + if args.size == 1 && args.first.respond_to?(:to_ary) + arg_names = args.first.to_ary + else + arg_names = args + end + [task_name, arg_names, []] + end + private :resolve_args_without_dependencies + + # Resolve task arguments for a task or rule when there are + # dependencies declared. + # + # The patterns recognized by this argument resolving function are: + # + # task :t => [:d] + # task :t, [a] => [:d] + # task :t, :needs => [:d] (deprecated) + # task :t, :a, :needs => [:d] (deprecated) + # + def resolve_args_with_dependencies(args, hash) # :nodoc: + fail "Task Argument Error" if hash.size != 1 + key, value = hash.map { |k, v| [k,v] }.first + if args.empty? + task_name = key + arg_names = [] + deps = value + elsif key == :needs + Rake.application.deprecate( + "task :t, arg, :needs => [deps]", + "task :t, [args] => [deps]", + caller.detect { |c| c !~ /\blib\/rake\b/ }) + task_name = args.shift + arg_names = args + deps = value + else + task_name = args.shift + arg_names = key + deps = value + end + deps = [deps] unless deps.respond_to?(:to_ary) + [task_name, arg_names, deps] + end + private :resolve_args_with_dependencies + + # If a rule can be found that matches the task name, enhance the + # task with the prerequisites and actions from the rule. Set the + # source attribute of the task appropriately for the rule. Return + # the enhanced task or nil of no rule was found. + def enhance_with_matching_rule(task_name, level=0) + fail Rake::RuleRecursionOverflowError, + "Rule Recursion Too Deep" if level >= 16 + @rules.each do |pattern, extensions, block| + if pattern.match(task_name) + task = attempt_rule(task_name, extensions, block, level) + return task if task + end + end + nil + rescue Rake::RuleRecursionOverflowError => ex + ex.add_target(task_name) + fail ex + end + + # List of all defined tasks in this application. + def tasks + @tasks.values.sort_by { |t| t.name } + end + + # List of all the tasks defined in the given scope (and its + # sub-scopes). + def tasks_in_scope(scope) + prefix = scope.join(":") + tasks.select { |t| + /^#{prefix}:/ =~ t.name + } + end + + # Clear all tasks in this application. + def clear + @tasks.clear + @rules.clear + end + + # Lookup a task, using scope and the scope hints in the task name. + # This method performs straight lookups without trying to + # synthesize file tasks or rules. Special scope names (e.g. '^') + # are recognized. If no scope argument is supplied, use the + # current scope. Return nil if the task cannot be found. + def lookup(task_name, initial_scope=nil) + initial_scope ||= @scope + task_name = task_name.to_s + if task_name =~ /^rake:/ + scopes = [] + task_name = task_name.sub(/^rake:/, '') + elsif task_name =~ /^(\^+)/ + scopes = initial_scope[0, initial_scope.size - $1.size] + task_name = task_name.sub(/^(\^+)/, '') + else + scopes = initial_scope + end + lookup_in_scope(task_name, scopes) + end + + # Lookup the task name + def lookup_in_scope(name, scope) + n = scope.size + while n >= 0 + tn = (scope[0,n] + [name]).join(':') + task = @tasks[tn] + return task if task + n -= 1 + end + nil + end + private :lookup_in_scope + + # Return the list of scope names currently active in the task + # manager. + def current_scope + @scope.dup + end + + # Evaluate the block in a nested namespace named +name+. Create + # an anonymous namespace if +name+ is nil. + def in_namespace(name) + name ||= generate_name + @scope.push(name) + ns = NameSpace.new(self, @scope) + yield(ns) + ns + ensure + @scope.pop + end + + private + + # Add a location to the locations field of the given task. + def add_location(task) + loc = find_location + task.locations << loc if loc + task + end + + # Find the location that called into the dsl layer. + def find_location + locations = caller + i = 0 + while locations[i] + return locations[i+1] if locations[i] =~ /rake\/dsl_definition.rb/ + i += 1 + end + nil + end + + # Generate an anonymous namespace name. + def generate_name + @seed ||= 0 + @seed += 1 + "_anon_#{@seed}" + end + + def trace_rule(level, message) + $stderr.puts "#{" "*level}#{message}" if Rake.application.options.trace_rules + end + + # Attempt to create a rule given the list of prerequisites. + def attempt_rule(task_name, extensions, block, level) + sources = make_sources(task_name, extensions) + prereqs = sources.collect { |source| + trace_rule level, "Attempting Rule #{task_name} => #{source}" + if File.exist?(source) || Rake::Task.task_defined?(source) + trace_rule level, "(#{task_name} => #{source} ... EXIST)" + source + elsif parent = enhance_with_matching_rule(source, level+1) + trace_rule level, "(#{task_name} => #{source} ... ENHANCE)" + parent.name + else + trace_rule level, "(#{task_name} => #{source} ... FAIL)" + return nil + end + } + task = FileTask.define_task({task_name => prereqs}, &block) + task.sources = prereqs + task + end + + # Make a list of sources from the list of file name extensions / + # translation procs. + def make_sources(task_name, extensions) + result = extensions.collect { |ext| + case ext + when /%/ + task_name.pathmap(ext) + when %r{/} + ext + when /^\./ + task_name.ext(ext) + when String + ext + when Proc + if ext.arity == 1 + ext.call(task_name) + else + ext.call + end + else + fail "Don't know how to handle rule dependent: #{ext.inspect}" + end + } + result.flatten + end + + + private + + # Return the current description, clearing it in the process. + def get_description(task) + desc = @last_description + @last_description = nil + desc + end + + class << self + attr_accessor :record_task_metadata + TaskManager.record_task_metadata = false + end + end + +end diff --git a/lib/rake/tasklib.rb b/lib/rake/tasklib.rb index a5a4494369..f1e17dad31 100644 --- a/lib/rake/tasklib.rb +++ b/lib/rake/tasklib.rb @@ -5,6 +5,7 @@ module Rake # Base class for Task Libraries. class TaskLib include Cloneable + include Rake::DSL # Make a symbol by pasting two strings together. # diff --git a/lib/rake/testtask.rb b/lib/rake/testtask.rb index c400205ff3..04d3ae473a 100644 --- a/lib/rake/testtask.rb +++ b/lib/rake/testtask.rb @@ -93,33 +93,37 @@ module Rake # Create the tasks defined by this task lib. def define - lib_path = @libs.join(File::PATH_SEPARATOR) desc "Run tests" + (@name==:test ? "" : " for #{@name}") task @name do - run_code = '' - RakeFileUtils.verbose(@verbose) do - run_code = - case @loader - when :direct - "-e 'ARGV.each{|f| load f}'" - when :testrb - "-S testrb" - when :rake - rake_loader - end - @ruby_opts.unshift( "-I\"#{lib_path}\"" ) - @ruby_opts.unshift( "-w" ) if @warning - ruby @ruby_opts.join(" ") + - " \"#{run_code}\" " + - file_list.collect { |fn| "\"#{fn}\"" }.join(' ') + - " #{option_list}" + FileUtilsExt.verbose(@verbose) do + ruby "#{ruby_opts_string} #{run_code} #{file_list_string} #{option_list}" end end self end def option_list # :nodoc: - ENV['TESTOPTS'] || @options || "" + (ENV['TESTOPTS'] || + ENV['TESTOPT'] || + ENV['TEST_OPTS'] || + ENV['TEST_OPT'] || + @options || + "") + end + + def ruby_opts_string + opts = @ruby_opts.dup + opts.unshift( "-I\"#{lib_path}\"" ) unless @libs.empty? + opts.unshift( "-w" ) if @warning + opts.join(" ") + end + + def lib_path + @libs.join(File::PATH_SEPARATOR) + end + + def file_list_string + file_list.collect { |fn| "\"#{fn}\"" }.join(' ') end def file_list # :nodoc: @@ -128,8 +132,32 @@ module Rake else result = [] result += @test_files.to_a if @test_files - result += FileList[ @pattern ].to_a if @pattern - FileList[result] + result << @pattern if @pattern + result + end + end + + def fix # :nodoc: + case ruby_version + when '1.8.2' + "\"#{find_file 'rake/ruby182_test_unit_fix'}\"" + else + nil + end || '' + end + + def ruby_version + RUBY_VERSION + end + + def run_code + case @loader + when :direct + "-e \"ARGV.each{|f| require f}\"" + when :testrb + "-S testrb #{fix}" + when :rake + "-I\"#{rake_lib_dir}\" \"#{rake_loader}\"" end end @@ -146,5 +174,18 @@ module Rake nil end + def rake_lib_dir # :nodoc: + find_dir('rake') or + fail "unable to find rake lib" + end + + def find_dir(fn) # :nodoc: + $LOAD_PATH.each do |path| + file_path = File.join(path, "#{fn}.rb") + return path if File.exist? file_path + end + nil + end + end end diff --git a/lib/rake/version.rb b/lib/rake/version.rb new file mode 100644 index 0000000000..dbad21e52d --- /dev/null +++ b/lib/rake/version.rb @@ -0,0 +1,10 @@ +module Rake + module Version + NUMBERS = [ + MAJOR = 0, + MINOR = 9, + BUILD = 2, + ] + end + VERSION = Version::NUMBERS.join('.') +end diff --git a/lib/rake/win32.rb b/lib/rake/win32.rb index 0ab31c2822..98289a10b4 100644 --- a/lib/rake/win32.rb +++ b/lib/rake/win32.rb @@ -1,46 +1,55 @@ + module Rake + require 'rake/alt_system' # Win 32 interface methods for Rake. Windows specific functionality # will be placed here to collect that knowledge in one spot. module Win32 - class << self - # True if running on a windows system. - if File::ALT_SEPARATOR == '\\' # assume other DOSish systems are extinct. - def windows?; true end - else - def windows?; false end - end + + # Error indicating a problem in locating the home directory on a + # Win32 system. + class Win32HomeError < RuntimeError end class << self + # True if running on a windows system. + def windows? + AltSystem::WINDOWS + end + + # Run a command line on windows. + def rake_system(*cmd) + AltSystem.system(*cmd) + end + # The standard directory containing system wide rake files on # Win 32 systems. Try the following environment variables (in # order): # - # * APPDATA # * HOME # * HOMEDRIVE + HOMEPATH + # * APPDATA # * USERPROFILE # - # If the above are not defined, retruns the personal folder. + # If the above are not defined, the return nil. def win32_system_dir #:nodoc: - win32_shared_path = ENV['APPDATA'] - if !win32_shared_path or win32_shared_path.empty? - win32_shared_path = '~' + win32_shared_path = ENV['HOME'] + if win32_shared_path.nil? && ENV['HOMEDRIVE'] && ENV['HOMEPATH'] + win32_shared_path = ENV['HOMEDRIVE'] + ENV['HOMEPATH'] end - File.expand_path('Rake', win32_shared_path) + + win32_shared_path ||= ENV['APPDATA'] + win32_shared_path ||= ENV['USERPROFILE'] + raise Win32HomeError, "Unable to determine home path environment variable." if + win32_shared_path.nil? or win32_shared_path.empty? + normalize(File.join(win32_shared_path, 'Rake')) end # Normalize a win32 path so that the slashes are all forward slashes. def normalize(path) - path.tr('\\', '/') + path.gsub(/\\/, '/') end - end if windows? - end - if Win32.windows? - def standard_system_dir - Win32.win32_system_dir end end end diff --git a/test/rake/capture_stdout.rb b/test/rake/capture_stdout.rb deleted file mode 100644 index f54ec42f69..0000000000 --- a/test/rake/capture_stdout.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'stringio' - -# Mix-in for capturing standard output. -module CaptureStdout - def capture_stdout - s = StringIO.new - oldstdout = $stdout - $stdout = s - yield - s.string - ensure - $stdout = oldstdout - end - - def capture_stderr - s = StringIO.new - oldstderr = $stderr - $stderr = s - yield - s.string - ensure - $stderr = oldstderr - end -end diff --git a/test/rake/check_expansion.rb b/test/rake/check_expansion.rb deleted file mode 100644 index 659cf71e51..0000000000 --- a/test/rake/check_expansion.rb +++ /dev/null @@ -1,5 +0,0 @@ -if ARGV[0] != ARGV[1] - exit 1 -else - exit 0 -end diff --git a/test/rake/check_no_expansion.rb b/test/rake/check_no_expansion.rb deleted file mode 100644 index 24f586a7f5..0000000000 --- a/test/rake/check_no_expansion.rb +++ /dev/null @@ -1,5 +0,0 @@ -if ARGV[0] != ARGV[1] - exit 0 -else - exit 1 -end diff --git a/test/rake/data/chains/Rakefile b/test/rake/data/chains/Rakefile deleted file mode 100644 index 31bdc2578d..0000000000 --- a/test/rake/data/chains/Rakefile +++ /dev/null @@ -1,15 +0,0 @@ -# -*- ruby -*- - -task :default => "play.app" - -file "play.scpt" => "base" do |t| - cp t.prerequisites.first, t.name -end - -rule ".app" => ".scpt" do |t| - cp t.source, t.name -end - -file 'base' do - touch 'base' -end diff --git a/test/rake/data/default/Rakefile b/test/rake/data/default/Rakefile deleted file mode 100644 index 22ed5e0ca9..0000000000 --- a/test/rake/data/default/Rakefile +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env ruby - -if ENV['TESTTOPSCOPE'] - puts "TOPSCOPE" -end - -task :default do - puts "DEFAULT" -end - -task :other => [:default] do - puts "OTHER" -end - -task :task_scope do - if ENV['TESTTASKSCOPE'] - puts "TASKSCOPE" - end -end diff --git a/test/rake/data/dryrun/Rakefile b/test/rake/data/dryrun/Rakefile deleted file mode 100644 index 0a68a0d606..0000000000 --- a/test/rake/data/dryrun/Rakefile +++ /dev/null @@ -1,22 +0,0 @@ -# - -task :default => ["temp_main"] - -file "temp_main" => [:all_apps] do touch "temp_main" end - -task :all_apps => [:one, :two] -task :one => ["temp_one"] -task :two => ["temp_two"] - -file "temp_one" do |t| - touch "temp_one" -end -file "temp_two" do |t| - touch "temp_two" -end - -task :clean do - ["temp_one", "temp_two", "temp_main"].each do |file| - rm_f file - end -end diff --git a/test/rake/data/file_creation_task/Rakefile b/test/rake/data/file_creation_task/Rakefile deleted file mode 100644 index 94641b6972..0000000000 --- a/test/rake/data/file_creation_task/Rakefile +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env ruby - -N = 2 - -task :default => :run - -BUILD_DIR = 'build' -task :clean do - rm_rf 'build' - rm_rf 'src' -end - -task :run - -TARGET_DIR = 'build/copies' - -FileList['src/*'].each do |src| - directory TARGET_DIR - target = File.join TARGET_DIR, File.basename(src) - file target => [src, TARGET_DIR] do - cp src, target - # sleep 3 if src !~ /foo#{N-1}$/ # I'm commenting out this sleep, it doesn't seem to do anything. - end - task :run => target -end - -task :prep => :clean do - mkdir_p 'src' - N.times do |n| - puts "DBG: Touching src/foo#{n}" - touch "src/foo#{n}" - end -end diff --git a/test/rake/data/imports/Rakefile b/test/rake/data/imports/Rakefile deleted file mode 100644 index 6a60f612bc..0000000000 --- a/test/rake/data/imports/Rakefile +++ /dev/null @@ -1,19 +0,0 @@ -# -*- ruby -*- - -require 'rake/loaders/makefile' - -task :default - -task :other do - puts "OTHER" -end - -file "dynamic_deps" do |t| - open(t.name, "w") do |f| f.puts "puts 'DYNAMIC'" end -end - -import "dynamic_deps" -import "static_deps" -import "static_deps" -import "deps.mf" -puts "FIRST" diff --git a/test/rake/data/imports/deps.mf b/test/rake/data/imports/deps.mf deleted file mode 100644 index 04643d0d8e..0000000000 --- a/test/rake/data/imports/deps.mf +++ /dev/null @@ -1 +0,0 @@ -default: other diff --git a/test/rake/data/multidesc/Rakefile b/test/rake/data/multidesc/Rakefile deleted file mode 100644 index 5342481922..0000000000 --- a/test/rake/data/multidesc/Rakefile +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env ruby - -task :b - -desc "A" -task :a - -desc "B" -task :b - -desc "A2" -task :a - -task :c - -desc "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -task :d diff --git a/test/rake/data/namespace/Rakefile b/test/rake/data/namespace/Rakefile deleted file mode 100644 index 6de98edae9..0000000000 --- a/test/rake/data/namespace/Rakefile +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env ruby - -desc "copy" -task :copy do - puts "COPY" -end - -namespace "nest" do - desc "nest copy" - task :copy do - puts "NEST COPY" - end - task :xx => :copy -end - -anon_ns = namespace do - desc "anonymous copy task" - task :copy do - puts "ANON COPY" - end -end - -desc "Top level task to run the anonymous version of copy" -task :anon => anon_ns[:copy] - -namespace "very" do - namespace "nested" do - task "run" => "rake:copy" - end -end - -namespace "a" do - desc "Run task in the 'a' namespace" - task "run" do - puts "IN A" - end -end - -namespace "b" do - desc "Run task in the 'b' namespace" - task "run" => "a:run" do - puts "IN B" - end -end - -namespace "file1" do - file "xyz.rb" do - puts "XYZ1" - end -end - -namespace "file2" do - file "xyz.rb" do - puts "XYZ2" - end -end - diff --git a/test/rake/data/rakelib/test1.rake b/test/rake/data/rakelib/test1.rake deleted file mode 100644 index bb1c419b40..0000000000 --- a/test/rake/data/rakelib/test1.rake +++ /dev/null @@ -1,3 +0,0 @@ -task :default do - puts "TEST1" -end diff --git a/test/rake/data/rbext/rakefile.rb b/test/rake/data/rbext/rakefile.rb deleted file mode 100644 index 670604d733..0000000000 --- a/test/rake/data/rbext/rakefile.rb +++ /dev/null @@ -1,3 +0,0 @@ -task :default do - puts "OK" -end diff --git a/test/rake/data/sample.mf b/test/rake/data/sample.mf deleted file mode 100644 index 778e9d456f..0000000000 --- a/test/rake/data/sample.mf +++ /dev/null @@ -1,14 +0,0 @@ -# Comments -a: a1 a2 a3 a4 -b: b1 b2 b3 \ - b4 b5 b6\ -# Mid: Comment -b7 - - a : a5 a6 a7 -c: c1 -d: d1 d2 \ - -e f : e1 f1 - -g\ 0: g1 g\ 2 g\ 3 g4 diff --git a/test/rake/data/statusreturn/Rakefile b/test/rake/data/statusreturn/Rakefile deleted file mode 100644 index 68023052f8..0000000000 --- a/test/rake/data/statusreturn/Rakefile +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env ruby - -task :exit5 do - exit(5) -end - -task :normal do -end diff --git a/test/rake/data/unittest/Rakefile b/test/rake/data/unittest/Rakefile deleted file mode 100644 index 9c3b8ac11c..0000000000 --- a/test/rake/data/unittest/Rakefile +++ /dev/null @@ -1 +0,0 @@ -# Empty Rakefile for Unit Test diff --git a/test/rake/data/unittest/subdir/.gitignore b/test/rake/data/unittest/subdir/.gitignore deleted file mode 100644 index d6b7ef32c8..0000000000 --- a/test/rake/data/unittest/subdir/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/test/rake/filecreation.rb b/test/rake/file_creation.rb similarity index 51% rename from test/rake/filecreation.rb rename to test/rake/file_creation.rb index e4c6e347da..facc57a038 100644 --- a/test/rake/filecreation.rb +++ b/test/rake/file_creation.rb @@ -1,15 +1,18 @@ module FileCreation - OLDFILE = "testdata/old" - NEWFILE = "testdata/new" + OLDFILE = "old" + NEWFILE = "new" def create_timed_files(oldfile, *newfiles) - return if File.exist?(oldfile) && newfiles.all? { |newfile| File.exist?(newfile) } - old_time = create_file(oldfile) + return if (File.exist?(oldfile) && + newfiles.all? { |newfile| + File.exist?(newfile) && File.stat(newfile).mtime > File.stat(oldfile).mtime + }) + now = Time.now + + create_file(oldfile, now - 60) + newfiles.each do |newfile| - while create_file(newfile) <= old_time - sleep(0.1) - File.delete(newfile) rescue nil - end + create_file(newfile, now) end end @@ -18,9 +21,10 @@ module FileCreation File.stat(dirname).mtime end - def create_file(name) + def create_file(name, file_time=nil) create_dir(File.dirname(name)) FileUtils.touch(name) unless File.exist?(name) + File.utime(file_time, file_time, name) unless file_time.nil? File.stat(name).mtime end diff --git a/test/rake/helper.rb b/test/rake/helper.rb new file mode 100644 index 0000000000..70b06d7fe2 --- /dev/null +++ b/test/rake/helper.rb @@ -0,0 +1,500 @@ +require 'rubygems' +require 'minitest/unit' +require 'flexmock/test_unit_integration' +require 'minitest/autorun' +require 'rake' +require 'tmpdir' +require File.expand_path('../file_creation', __FILE__) + +class Rake::TestCase < MiniTest::Unit::TestCase + include FlexMock::ArgumentTypes + include FlexMock::MockContainer + + include FileCreation + + include Rake::DSL + + class TaskManager + include Rake::TaskManager + end + + def setup + ARGV.clear + + @orig_PWD = Dir.pwd + @orig_APPDATA = ENV['APPDATA'] + @orig_HOME = ENV['HOME'] + @orig_HOMEDRIVE = ENV['HOMEDRIVE'] + @orig_HOMEPATH = ENV['HOMEPATH'] + @orig_RAKE_COLUMNS = ENV['RAKE_COLUMNS'] + @orig_RAKE_SYSTEM = ENV['RAKE_SYSTEM'] + @orig_RAKEOPT = ENV['RAKEOPT'] + @orig_USERPROFILE = ENV['USERPROFILE'] + ENV.delete 'RAKE_COLUMNS' + ENV.delete 'RAKE_SYSTEM' + ENV.delete 'RAKEOPT' + + tmpdir = Dir.chdir Dir.tmpdir do Dir.pwd end + @tempdir = File.join tmpdir, "test_rake_#{$$}" + + FileUtils.mkdir_p @tempdir + + Dir.chdir @tempdir + + Rake.application = Rake::Application.new + Rake::TaskManager.record_task_metadata = true + end + + def teardown + flexmock_teardown + + Dir.chdir @orig_PWD + FileUtils.rm_rf @tempdir + + if @orig_APPDATA then + ENV['APPDATA'] = @orig_APPDATA + else + ENV.delete 'APPDATA' + end + + ENV['HOME'] = @orig_HOME + ENV['HOMEDRIVE'] = @orig_HOMEDRIVE + ENV['HOMEPATH'] = @orig_HOMEPATH + ENV['RAKE_COLUMNS'] = @orig_RAKE_COLUMNS + ENV['RAKE_SYSTEM'] = @orig_RAKE_SYSTEM + ENV['RAKEOPT'] = @orig_RAKEOPT + ENV['USERPROFILE'] = @orig_USERPROFILE + end + + def ignore_deprecations + Rake.application.options.ignore_deprecate = true + yield + ensure + Rake.application.options.ignore_deprecate = false + end + + def rake_system_dir + @system_dir = 'system' + + FileUtils.mkdir_p @system_dir + + open File.join(@system_dir, 'sys1.rake'), 'w' do |io| + io << <<-SYS +task "sys1" do + puts "SYS1" +end + SYS + end + + ENV['RAKE_SYSTEM'] = @system_dir + end + + def rakefile contents + open 'Rakefile', 'w' do |io| + io << contents + end + end + + def rakefile_access + rakefile <<-ACCESS +TOP_LEVEL_CONSTANT = 0 + +def a_top_level_function +end + +task :default => [:work, :obj, :const] + +task :work do + begin + a_top_level_function + puts "GOOD:M Top level methods can be called in tasks" + rescue NameError => ex + puts "BAD:M Top level methods can not be called in tasks" + end +end + +# TODO: remove `disabled_' when DeprecatedObjectDSL removed +task :obj +task :disabled_obj do + begin + Object.new.instance_eval { task :xyzzy } + puts "BAD:D Rake DSL are polluting objects" + rescue StandardError => ex + puts "GOOD:D Rake DSL are not polluting objects" + end +end + +task :const do + begin + TOP_LEVEL_CONSTANT + puts "GOOD:C Top level constants are available in tasks" + rescue StandardError => ex + puts "BAD:C Top level constants are NOT available in tasks" + end +end + ACCESS + end + + def rakefile_chains + rakefile <<-DEFAULT +task :default => "play.app" + +file "play.scpt" => "base" do |t| + cp t.prerequisites.first, t.name +end + +rule ".app" => ".scpt" do |t| + cp t.source, t.name +end + +file 'base' do + touch 'base' +end + DEFAULT + end + + def rakefile_comments + rakefile <<-COMMENTS +# comment for t1 +task :t1 do +end + +# no comment or task because there's a blank line + +task :t2 do +end + +desc "override comment for t3" +# this is not the description +multitask :t3 do +end + +# this is not the description +desc "override comment for t4" +file :t4 do +end + COMMENTS + end + + def rakefile_default + rakefile <<-DEFAULT +if ENV['TESTTOPSCOPE'] + puts "TOPSCOPE" +end + +task :default do + puts "DEFAULT" +end + +task :other => [:default] do + puts "OTHER" +end + +task :task_scope do + if ENV['TESTTASKSCOPE'] + puts "TASKSCOPE" + end +end + DEFAULT + end + + def rakefile_dryrun + rakefile <<-DRYRUN +task :default => ["temp_main"] + +file "temp_main" => [:all_apps] do touch "temp_main" end + +task :all_apps => [:one, :two] +task :one => ["temp_one"] +task :two => ["temp_two"] + +file "temp_one" do |t| + touch "temp_one" +end +file "temp_two" do |t| + touch "temp_two" +end + +task :clean do + ["temp_one", "temp_two", "temp_main"].each do |file| + rm_f file + end +end + DRYRUN + + FileUtils.touch 'temp_main' + FileUtils.touch 'temp_two' + end + + def rakefile_extra + rakefile 'task :default' + + FileUtils.mkdir_p 'rakelib' + + open File.join('rakelib', 'extra.rake'), 'w' do |io| + io << <<-EXTRA_RAKE +# Added for testing + +namespace :extra do + desc "An Extra Task" + task :extra do + puts "Read all about it" + end +end + EXTRA_RAKE + end + end + + def rakefile_file_creation + rakefile <<-'FILE_CREATION' +N = 2 + +task :default => :run + +BUILD_DIR = 'build' +task :clean do + rm_rf 'build' + rm_rf 'src' +end + +task :run + +TARGET_DIR = 'build/copies' + +FileList['src/*'].each do |src| + directory TARGET_DIR + target = File.join TARGET_DIR, File.basename(src) + file target => [src, TARGET_DIR] do + cp src, target + # sleep 3 if src !~ /foo#{N-1}$/ # I'm commenting out this sleep, it doesn't seem to do anything. + end + task :run => target +end + +task :prep => :clean do + mkdir_p 'src' + N.times do |n| + puts "DBG: Touching src/foo#{n}" + touch "src/foo#{n}" + end +end + FILE_CREATION + end + + def rakefile_imports + rakefile <<-IMPORTS +require 'rake/loaders/makefile' + +task :default + +task :other do + puts "OTHER" +end + +file "dynamic_deps" do |t| + open(t.name, "w") do |f| f.puts "puts 'DYNAMIC'" end +end + +import "dynamic_deps" +import "static_deps" +import "static_deps" +import "deps.mf" +puts "FIRST" + IMPORTS + + open 'deps.mf', 'w' do |io| + io << <<-DEPS +default: other + DEPS + end + + open "static_deps", "w" do |f| + f.puts 'puts "STATIC"' + end + end + + def rakefile_multidesc + rakefile <<-MULTIDESC +task :b + +desc "A" +task :a + +desc "B" +task :b + +desc "A2" +task :a + +task :c + +desc "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +task :d + MULTIDESC + end + + def rakefile_namespace + rakefile <<-NAMESPACE +desc "copy" +task :copy do + puts "COPY" +end + +namespace "nest" do + desc "nest copy" + task :copy do + puts "NEST COPY" + end + task :xx => :copy +end + +anon_ns = namespace do + desc "anonymous copy task" + task :copy do + puts "ANON COPY" + end +end + +desc "Top level task to run the anonymous version of copy" +task :anon => anon_ns[:copy] + +namespace "very" do + namespace "nested" do + task "run" => "rake:copy" + end +end + +namespace "a" do + desc "Run task in the 'a' namespace" + task "run" do + puts "IN A" + end +end + +namespace "b" do + desc "Run task in the 'b' namespace" + task "run" => "a:run" do + puts "IN B" + end +end + +namespace "file1" do + file "xyz.rb" do + puts "XYZ1" + end +end + +namespace "file2" do + file "xyz.rb" do + puts "XYZ2" + end +end + +namespace "scopedep" do + task :prepare do + touch "scopedep.rb" + puts "PREPARE" + end + file "scopedep.rb" => [:prepare] do + puts "SCOPEDEP" + end +end + NAMESPACE + end + + def rakefile_nosearch + FileUtils.touch 'dummy' + end + + def rakefile_rakelib + FileUtils.mkdir_p 'rakelib' + + Dir.chdir 'rakelib' do + open 'test1.rb', 'w' do |io| + io << <<-TEST1 +task :default do + puts "TEST1" +end + TEST1 + end + + open 'test2.rake', 'w' do |io| + io << <<-TEST1 +task :default do + puts "TEST2" +end + TEST1 + end + end + end + + def rakefile_rbext + open 'rakefile.rb', 'w' do |io| + io << 'task :default do puts "OK" end' + end + end + + def rakefile_statusreturn + rakefile <<-STATUSRETURN +task :exit5 do + exit(5) +end + +task :normal do +end + STATUSRETURN + end + + def rakefile_unittest + rakefile '# Empty Rakefile for Unit Test' + + readme = File.join 'subdir', 'README' + FileUtils.mkdir_p File.dirname readme + + FileUtils.touch readme + end + + def rakefile_verbose + rakefile <<-VERBOSE +task :standalone_verbose_true do + verbose true + sh "ruby -e '0'" +end + +task :standalone_verbose_false do + verbose false + sh "ruby -e '0'" +end + +task :inline_verbose_default do + sh "ruby -e '0'" +end + +task :inline_verbose_false do + sh "ruby -e '0'", :verbose => false +end + +task :inline_verbose_true do + sh "ruby -e '0'", :verbose => true +end + +task :block_verbose_true do + verbose(true) do + sh "ruby -e '0'" + end +end + +task :block_verbose_false do + verbose(false) do + sh "ruby -e '0'" + end +end + VERBOSE + end + +end + +# workarounds for 1.8 +$" << 'test/helper.rb' +Test::Unit.run = true if Test::Unit.respond_to? :run= + diff --git a/test/rake/in_environment.rb b/test/rake/in_environment.rb deleted file mode 100644 index f6fd8fb731..0000000000 --- a/test/rake/in_environment.rb +++ /dev/null @@ -1,30 +0,0 @@ -module InEnvironment - private - - # Create an environment for a test. At the completion of the yielded - # block, the environment is restored to its original conditions. - def in_environment(settings) - original_settings = set_env(settings) - yield - ensure - set_env(original_settings) if original_settings - end - - # Set the environment according to the settings hash. - def set_env(settings) # :nodoc: - result = {} - settings.each do |k, v| - result[k] = ENV[k] - if k == 'PWD' - result[k] = Dir.pwd - Dir.chdir(v) - elsif v.nil? - ENV.delete(k) - else - ENV[k] = v - end - end - result - end - -end diff --git a/test/rake/rake_test_setup.rb b/test/rake/rake_test_setup.rb deleted file mode 100644 index de94d56b40..0000000000 --- a/test/rake/rake_test_setup.rb +++ /dev/null @@ -1,9 +0,0 @@ -# Common setup for all test files. - -# require 'flexmock/test_unit' - -module TestMethods - def assert_exception(ex, msg=nil, &block) - assert_raise(ex, msg, &block) - end -end diff --git a/test/rake/reqfile.rb b/test/rake/reqfile.rb deleted file mode 100644 index 5372544b66..0000000000 --- a/test/rake/reqfile.rb +++ /dev/null @@ -1,3 +0,0 @@ -# For --require testing - -TESTING_REQUIRE << 1 diff --git a/test/rake/reqfile2.rb b/test/rake/reqfile2.rb deleted file mode 100644 index 65993907ac..0000000000 --- a/test/rake/reqfile2.rb +++ /dev/null @@ -1,3 +0,0 @@ -# For --require testing - -TESTING_REQUIRE << 2 diff --git a/test/rake/reqfile3.rb b/test/rake/reqfile3.rb deleted file mode 100644 index 11fc16b2ab..0000000000 --- a/test/rake/reqfile3.rb +++ /dev/null @@ -1,3 +0,0 @@ -# For --require testing - -TESTING_REQUIRE << 3 diff --git a/test/rake/shellcommand.rb b/test/rake/shellcommand.rb deleted file mode 100755 index 58db8a0871..0000000000 --- a/test/rake/shellcommand.rb +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby - -exit((ARGV[0] || "0").to_i) diff --git a/test/rake/test_application.rb b/test/rake/test_application.rb deleted file mode 100644 index 27c7d8df05..0000000000 --- a/test/rake/test_application.rb +++ /dev/null @@ -1,687 +0,0 @@ -require 'test/unit' -require 'rake' -require_relative 'capture_stdout' -require_relative 'in_environment' - -TESTING_REQUIRE = [ ] - -###################################################################### -class Rake::TestApplication < Test::Unit::TestCase - include CaptureStdout - include InEnvironment - BASEDIR = File.dirname(__FILE__) - - def defmock(*names, &block) - class << (@mock ||= Object.new); self; end.class_eval do - names.each do |name| - define_method(name, block) - end - end - @mock - end - - def setup - @app = Rake::Application.new - @app.options.rakelib = [] - end - - def test_constant_warning - err = capture_stderr do @app.instance_eval { const_warning("Task") } end - assert_match(/warning/i, err) - assert_match(/deprecated/i, err) - assert_match(/Task/i, err) - end - - def test_display_tasks - @app.options.show_task_pattern = // - @app.last_description = "COMMENT" - @app.define_task(Rake::Task, "t") - out = capture_stdout do @app.instance_eval { display_tasks_and_comments } end - assert_match(/^rake t/, out) - assert_match(/# COMMENT/, out) - end - - def test_display_tasks_with_long_comments - in_environment('RAKE_COLUMNS' => '80') do - @app.options.show_task_pattern = // - @app.last_description = "1234567890" * 8 - @app.define_task(Rake::Task, "t") - out = capture_stdout do @app.instance_eval { display_tasks_and_comments } end - assert_match(/^rake t/, out) - assert_match(/# 12345678901234567890123456789012345678901234567890123456789012345\.\.\./, out) - end - end - - def test_display_tasks_with_task_name_wider_than_tty_display - in_environment('RAKE_COLUMNS' => '80') do - @app.options.show_task_pattern = // - description = "something short" - task_name = "task name" * 80 - @app.last_description = "something short" - @app.define_task(Rake::Task, task_name ) - out = capture_stdout do @app.instance_eval { display_tasks_and_comments } end - # Ensure the entire task name is output and we end up showing no description - assert_match(/rake #{task_name} # .../, out) - end - end - - def test_display_tasks_with_very_long_task_name_to_a_non_tty_shows_name_and_comment - @app.options.show_task_pattern = // - @app.tty_output = false - description = "something short" - task_name = "task name" * 80 - @app.last_description = "something short" - @app.define_task(Rake::Task, task_name ) - out = capture_stdout do @app.instance_eval { display_tasks_and_comments } end - # Ensure the entire task name is output and we end up showing no description - assert_match(/rake #{task_name} # #{description}/, out) - end - - def test_display_tasks_with_long_comments_to_a_non_tty_shows_entire_comment - @app.options.show_task_pattern = // - @app.tty_output = false - @app.last_description = "1234567890" * 8 - @app.define_task(Rake::Task, "t") - out = capture_stdout do @app.instance_eval { display_tasks_and_comments } end - assert_match(/^rake t/, out) - assert_match(/# #{@app.last_description}/, out) - end - - def test_display_tasks_with_long_comments_to_a_non_tty_with_columns_set_truncates_comments - in_environment("RAKE_COLUMNS" => '80') do - @app.options.show_task_pattern = // - @app.tty_output = false - @app.last_description = "1234567890" * 8 - @app.define_task(Rake::Task, "t") - out = capture_stdout do @app.instance_eval { display_tasks_and_comments } end - assert_match(/^rake t/, out) - assert_match(/# 12345678901234567890123456789012345678901234567890123456789012345\.\.\./, out) - end - end - - def test_display_tasks_with_full_descriptions - @app.options.show_task_pattern = // - @app.options.full_description = true - @app.last_description = "COMMENT" - @app.define_task(Rake::Task, "t") - out = capture_stdout do @app.instance_eval { display_tasks_and_comments } end - assert_match(/^rake t$/, out) - assert_match(/^ {4}COMMENT$/, out) - end - - def test_finding_rakefile - in_environment("PWD" => File.join(BASEDIR, "data/unittest")) do - assert_match(/Rakefile/i, @app.instance_eval { have_rakefile }) - end - end - - def test_not_finding_rakefile - @app.instance_eval { @rakefiles = ['NEVER_FOUND'] } - assert( ! @app.instance_eval do have_rakefile end ) - assert_nil @app.rakefile - end - - def test_load_rakefile - in_environment("PWD" => File.join(BASEDIR, "data/unittest")) do - @app.instance_eval do - handle_options - options.silent = true - load_rakefile - end - assert_equal "rakefile", @app.rakefile.downcase - assert_match(%r(unittest$), Dir.pwd) - end - end - - def test_load_rakefile_from_subdir - in_environment("PWD" => File.join(BASEDIR, "data/unittest/subdir")) do - @app.instance_eval do - handle_options - options.silent = true - load_rakefile - end - assert_equal "rakefile", @app.rakefile.downcase - assert_match(%r(unittest$), Dir.pwd) - end - end - - def test_load_rakefile_not_found - in_environment("PWD" => "/", "RAKE_SYSTEM" => 'not_exist') do - @app.instance_eval do - handle_options - options.silent = true - end - ex = assert_raise(RuntimeError) do - @app.instance_eval do raw_load_rakefile end - end - assert_match(/no rakefile found/i, ex.message) - end - end - - def test_load_from_system_rakefile - system_dir = File.expand_path('../data/default', __FILE__) - in_environment('RAKE_SYSTEM' => system_dir) do - @app.options.rakelib = [] - @app.instance_eval do - handle_options - options.silent = true - options.load_system = true - options.rakelib = [] - load_rakefile - end - assert_equal system_dir, @app.system_dir - assert_nil @app.rakefile - end - end - - def test_windows - assert ! (@app.windows? && @app.unix?) - end - - def test_loading_imports - args = [] - mock = defmock(:load) {|*a| args << a} - @app.instance_eval do - add_loader("dummy", mock) - add_import("x.dummy") - load_imports - end - assert_equal([["x.dummy"]], args) - end - - def test_building_imported_files_on_demand - args = [] - callback = false - mock = defmock(:load) {|*a| args << a} - @app.instance_eval do - intern(Rake::Task, "x.dummy").enhance do callback = true end - add_loader("dummy", mock) - add_import("x.dummy") - load_imports - end - assert_equal([["x.dummy"]], args) - assert(callback) - end - - def test_handle_options_should_strip_options_from_ARGV - assert !@app.options.trace - - valid_option = '--trace' - ARGV.clear - ARGV << valid_option - - @app.handle_options - - assert !ARGV.include?(valid_option) - assert @app.options.trace - end - - def test_good_run - ran = false - ARGV.clear - ARGV << '--rakelib=""' - @app.options.silent = true - @app.instance_eval do - intern(Rake::Task, "default").enhance { ran = true } - end - in_environment("PWD" => File.join(BASEDIR, "data/default")) do - @app.run - end - assert ran - end - - def test_display_task_run - ran = false - ARGV.clear - ARGV << '-f' << '-s' << '--tasks' << '--rakelib=""' - @app.last_description = "COMMENT" - @app.define_task(Rake::Task, "default") - out = capture_stdout { @app.run } - assert @app.options.show_tasks - assert ! ran - assert_match(/rake default/, out) - assert_match(/# COMMENT/, out) - end - - def test_display_prereqs - ran = false - ARGV.clear - ARGV << '-f' << '-s' << '--prereqs' << '--rakelib=""' - @app.last_description = "COMMENT" - t = @app.define_task(Rake::Task, "default") - t.enhance([:a, :b]) - @app.define_task(Rake::Task, "a") - @app.define_task(Rake::Task, "b") - out = capture_stdout { @app.run } - assert @app.options.show_prereqs - assert ! ran - assert_match(/rake a$/, out) - assert_match(/rake b$/, out) - assert_match(/rake default\n( *(a|b)\n){2}/m, out) - end - - def test_bad_run - @app.intern(Rake::Task, "default").enhance { fail } - ARGV.clear - ARGV << '-f' << '-s' << '--rakelib=""' - assert_raise(SystemExit) { - err = capture_stderr { @app.run } - assert_match(/see full trace/, err) - } - ensure - ARGV.clear - end - - def test_bad_run_with_trace - @app.intern(Rake::Task, "default").enhance { fail } - ARGV.clear - ARGV << '-f' << '-s' << '-t' - assert_raise(SystemExit) { - err = capture_stderr { capture_stdout { @app.run } } - assert_no_match(/see full trace/, err) - } - ensure - ARGV.clear - end - - def test_run_with_bad_options - @app.intern(Rake::Task, "default").enhance { fail } - ARGV.clear - ARGV << '-f' << '-s' << '--xyzzy' - assert_raise(SystemExit) { - err = capture_stderr { capture_stdout { @app.run } } - } - ensure - ARGV.clear - end -end - - -###################################################################### -class Rake::TestApplicationOptions < Test::Unit::TestCase - include CaptureStdout - - def setup - clear_argv - RakeFileUtils.verbose_flag = false - RakeFileUtils.nowrite_flag = false - TESTING_REQUIRE.clear - end - - def teardown - clear_argv - RakeFileUtils.verbose_flag = false - RakeFileUtils.nowrite_flag = false - end - - def clear_argv - while ! ARGV.empty? - ARGV.pop - end - end - - def test_default_options - opts = command_line - assert_nil opts.classic_namespace - assert_nil opts.dryrun - assert_nil opts.full_description - assert_nil opts.ignore_system - assert_nil opts.load_system - assert_nil opts.nosearch - assert_equal ['rakelib'], opts.rakelib - assert_nil opts.show_prereqs - assert_nil opts.show_task_pattern - assert_nil opts.show_tasks - assert_nil opts.silent - assert_nil opts.trace - assert_equal ['rakelib'], opts.rakelib - assert ! RakeFileUtils.verbose_flag - assert ! RakeFileUtils.nowrite_flag - end - - def test_dry_run - flags('--dry-run', '-n') do |opts| - assert opts.dryrun - assert opts.trace - assert RakeFileUtils.verbose_flag - assert RakeFileUtils.nowrite_flag - end - end - - def test_describe - flags('--describe') do |opts| - assert opts.full_description - assert opts.show_tasks - assert_equal(//.to_s, opts.show_task_pattern.to_s) - end - end - - def test_describe_with_pattern - flags('--describe=X') do |opts| - assert opts.full_description - assert opts.show_tasks - assert_equal(/X/.to_s, opts.show_task_pattern.to_s) - end - end - - def test_execute - $xyzzy = 0 - flags('--execute=$xyzzy=1', '-e $xyzzy=1') do |opts| - assert_equal 1, $xyzzy - assert_equal :exit, @exit - $xyzzy = 0 - end - end - - def test_execute_and_continue - $xyzzy = 0 - flags('--execute-continue=$xyzzy=1', '-E $xyzzy=1') do |opts| - assert_equal 1, $xyzzy - assert_not_equal :exit, @exit - $xyzzy = 0 - end - end - - def test_execute_and_print - $xyzzy = 0 - flags('--execute-print=$xyzzy="pugh"', '-p $xyzzy="pugh"') do |opts| - assert_equal 'pugh', $xyzzy - assert_equal :exit, @exit - assert_match(/^pugh$/, @out) - $xyzzy = 0 - end - end - - def test_help - flags('--help', '-H', '-h') do |opts| - assert_match(/\Arake/, @out) - assert_match(/\boptions\b/, @out) - assert_match(/\btargets\b/, @out) - assert_equal :exit, @exit - assert_equal :exit, @exit - end - end - - def test_libdir - flags(['--libdir', 'xx'], ['-I', 'xx'], ['-Ixx']) do |opts| - $:.include?('xx') - end - ensure - $:.delete('xx') - end - - def test_rakefile - flags(['--rakefile', 'RF'], ['--rakefile=RF'], ['-f', 'RF'], ['-fRF']) do |opts| - assert_equal ['RF'], @app.instance_eval { @rakefiles } - end - end - - def test_rakelib - flags(['--rakelibdir', 'A:B:C'], ['--rakelibdir=A:B:C'], ['-R', 'A:B:C'], ['-RA:B:C']) do |opts| - assert_equal ['A', 'B', 'C'], opts.rakelib - end - end - - def test_require - flags(['--require', File.expand_path('../reqfile', __FILE__)], - "-r#{File.expand_path('../reqfile2', __FILE__)}", - "-r#{File.expand_path('../reqfile3', __FILE__)}") do |opts| - end - assert TESTING_REQUIRE.include?(1) - assert TESTING_REQUIRE.include?(2) - assert TESTING_REQUIRE.include?(3) - assert_equal 3, TESTING_REQUIRE.size - end - - def test_missing_require - ex = assert_raise(LoadError) do - flags(['--require', File.expand_path('../missing', __FILE__)]) do |opts| - end - end - assert_match(/cannot load such file/, ex.message) - assert_match(/#{File.basename(File.dirname(__FILE__))}\/missing/, ex.message) - end - - def test_prereqs - flags('--prereqs', '-P') do |opts| - assert opts.show_prereqs - end - end - - def test_quiet - flags('--quiet', '-q') do |opts| - assert ! RakeFileUtils.verbose_flag - assert ! opts.silent - end - end - - def test_no_search - flags('--nosearch', '--no-search', '-N') do |opts| - assert opts.nosearch - end - end - - def test_silent - flags('--silent', '-s') do |opts| - assert ! RakeFileUtils.verbose_flag - assert opts.silent - end - end - - def test_system - flags('--system', '-g') do |opts| - assert opts.load_system - end - end - - def test_no_system - flags('--no-system', '-G') do |opts| - assert opts.ignore_system - end - end - - def test_trace - flags('--trace', '-t') do |opts| - assert opts.trace - assert RakeFileUtils.verbose_flag - assert ! RakeFileUtils.nowrite_flag - end - end - - def test_trace_rules - flags('--rules') do |opts| - assert opts.trace_rules - end - end - - def test_tasks - flags('--tasks', '-T') do |opts| - assert opts.show_tasks - assert_equal(//.to_s, opts.show_task_pattern.to_s) - end - flags(['--tasks', 'xyz'], ['-Txyz']) do |opts| - assert opts.show_tasks - assert_equal(/xyz/, opts.show_task_pattern) - end - end - - def test_verbose - flags('--verbose', '-V') do |opts| - assert RakeFileUtils.verbose_flag - assert ! opts.silent - end - end - - def test_version - flags('--version', '-V') do |opts| - assert_match(/\bversion\b/, @out) - assert_match(/\b#{RAKEVERSION}\b/, @out) - assert_equal :exit, @exit - end - end - - def test_classic_namespace - flags(['--classic-namespace'], ['-C', '-T', '-P', '-n', '-s', '-t']) do |opts| - assert opts.classic_namespace - assert_equal opts.show_tasks, $show_tasks - assert_equal opts.show_prereqs, $show_prereqs - assert_equal opts.trace, $trace - assert_equal opts.dryrun, $dryrun - assert_equal opts.silent, $silent - end - end - - def test_bad_option - capture_stderr do - ex = assert_raise(OptionParser::InvalidOption) do - flags('--bad-option') - end - if ex.message =~ /^While/ # Ruby 1.9 error message - assert_match(/while parsing/i, ex.message) - else # Ruby 1.8 error message - assert_match(/(invalid|unrecognized) option/i, ex.message) - assert_match(/--bad-option/, ex.message) - end - end - end - - def test_task_collection - command_line("a", "b") - assert_equal ["a", "b"], @tasks.sort - end - - def test_default_task_collection - command_line() - assert_equal ["default"], @tasks - end - - def test_environment_definition - ENV.delete('TESTKEY') - command_line("a", "TESTKEY=12") - assert_equal ["a"], @tasks.sort - assert '12', ENV['TESTKEY'] - end - - private - - def flags(*sets) - sets.each do |set| - ARGV.clear - @out = capture_stdout { - @exit = catch(:system_exit) { opts = command_line(*set) } - } - yield(@app.options) if block_given? - end - end - - def command_line(*options) - options.each do |opt| ARGV << opt end - @app = Rake::Application.new - def @app.exit(*args) - throw :system_exit, :exit - end - @app.instance_eval do - handle_options - collect_tasks - end - @tasks = @app.top_level_tasks - @app.options - end -end - -class Rake::TestTaskArgumentParsing < Test::Unit::TestCase - def setup - @app = Rake::Application.new - end - - def test_name_only - name, args = @app.parse_task_string("name") - assert_equal "name", name - assert_equal [], args - end - - def test_empty_args - name, args = @app.parse_task_string("name[]") - assert_equal "name", name - assert_equal [], args - end - - def test_one_argument - name, args = @app.parse_task_string("name[one]") - assert_equal "name", name - assert_equal ["one"], args - end - - def test_two_arguments - name, args = @app.parse_task_string("name[one,two]") - assert_equal "name", name - assert_equal ["one", "two"], args - end - - def test_can_handle_spaces_between_args - name, args = @app.parse_task_string("name[one, two,\tthree , \tfour]") - assert_equal "name", name - assert_equal ["one", "two", "three", "four"], args - end - - def test_keeps_embedded_spaces - name, args = @app.parse_task_string("name[a one ana, two]") - assert_equal "name", name - assert_equal ["a one ana", "two"], args - end - -end - -class Rake::TestTaskArgumentParsing < Test::Unit::TestCase - include InEnvironment - - def test_terminal_width_using_env - app = Rake::Application.new - in_environment('RAKE_COLUMNS' => '1234') do - assert_equal 1234, app.terminal_width - end - end - - def test_terminal_width_using_stty - app = Rake::Application.new - def app.unix?() true end - def app.dynamic_width_stty() 1235 end - def app.dynamic_width_tput() 0 end - in_environment('RAKE_COLUMNS' => nil) do - assert_equal 1235, app.terminal_width - end - end - - def test_terminal_width_using_tput - app = Rake::Application.new - def app.unix?() true end - def app.dynamic_width_stty() 0 end - def app.dynamic_width_tput() 1236 end - in_environment('RAKE_COLUMNS' => nil) do - assert_equal 1236, app.terminal_width - end - end - - def test_terminal_width_using_hardcoded_80 - app = Rake::Application.new - def app.unix?() false end - in_environment('RAKE_COLUMNS' => nil) do - assert_equal 80, app.terminal_width - end - end - - def test_terminal_width_with_failure - app = Rake::Application.new - called = false - class << app; self; end.class_eval do - define_method(:unix?) {|*a| - called = a - raise RuntimeError - } - end - in_environment('RAKE_COLUMNS' => nil) do - assert_equal 80, app.terminal_width - end - assert_equal([], called) - end -end diff --git a/test/rake/test_invocation_chain.rb b/test/rake/test_invocation_chain.rb deleted file mode 100644 index 2b06493773..0000000000 --- a/test/rake/test_invocation_chain.rb +++ /dev/null @@ -1,75 +0,0 @@ -require 'test/unit' -require 'rake' - -###################################################################### -class Rake::TestAnEmptyInvocationChain < Test::Unit::TestCase - - def setup - @empty = Rake::InvocationChain::EMPTY - end - - def test_should_be_able_to_add_members - assert_nothing_raised do - @empty.append("A") - end - end - - def test_to_s - assert_equal "TOP", @empty.to_s - end -end - -###################################################################### -class Rake::TestAnInvocationChainWithOneMember < Test::Unit::TestCase - - def setup - @empty = Rake::InvocationChain::EMPTY - @first_member = "A" - @chain = @empty.append(@first_member) - end - - def test_should_report_first_member_as_a_member - assert @chain.member?(@first_member) - end - - def test_should_fail_when_adding_original_member - ex = assert_raise RuntimeError do - @chain.append(@first_member) - end - assert_match(/circular +dependency/i, ex.message) - assert_match(/A.*=>.*A/, ex.message) - end - - def test_to_s - assert_equal "TOP => A", @chain.to_s - end - -end - -###################################################################### -class Rake::TestAnInvocationChainWithMultipleMember < Test::Unit::TestCase - - def setup - @first_member = "A" - @second_member = "B" - ch = Rake::InvocationChain::EMPTY.append(@first_member) - @chain = ch.append(@second_member) - end - - def test_should_report_first_member_as_a_member - assert @chain.member?(@first_member) - end - - def test_should_report_second_member_as_a_member - assert @chain.member?(@second_member) - end - - def test_should_fail_when_adding_original_member - ex = assert_raise RuntimeError do - @chain.append(@first_member) - end - assert_match(/A.*=>.*B.*=>.*A/, ex.message) - end -end - - diff --git a/test/rake/test_package_task.rb b/test/rake/test_package_task.rb deleted file mode 100644 index 0ed20e42c0..0000000000 --- a/test/rake/test_package_task.rb +++ /dev/null @@ -1,115 +0,0 @@ -require 'tmpdir' -require 'fileutils' -require 'test/unit' -require 'rake/packagetask' - -class Rake::TestPackageTask < Test::Unit::TestCase - include Rake - - def test_create - pwd = Dir.pwd - tmpdir = Dir.mktmpdir("rake") - Dir.chdir(tmpdir) - Dir.mkdir("bin") - open("bin/rake", "wb") {} - pkg = Rake::PackageTask.new("pkgr", "1.2.3") { |p| - p.package_files << "install.rb" - p.package_files.include( - '[A-Z]*', - 'bin/**/*', - 'lib/**/*.rb', - 'test/**/*.rb', - 'doc/**/*', - 'build/rubyapp.rb', - '*.blurb') - p.package_files.exclude(/\bCVS\b/) - p.package_files.exclude(/~$/) - p.package_dir = 'pkg' - p.need_tar = true - p.need_tar_gz = true - p.need_tar_bz2 = true - p.need_zip = true - } - assert_equal "pkg", pkg.package_dir - assert pkg.package_files.include?("bin/rake") - assert "pkgr", pkg.name - assert "1.2.3", pkg.version - assert Task[:package] - assert Task['pkg/pkgr-1.2.3.tgz'] - assert Task['pkg/pkgr-1.2.3.tar.gz'] - assert Task['pkg/pkgr-1.2.3.tar.bz2'] - assert Task['pkg/pkgr-1.2.3.zip'] - assert Task["pkg/pkgr-1.2.3"] - assert Task[:clobber_package] - assert Task[:repackage] - ensure - Dir.chdir(pwd) - FileUtils.rm_rf(tmpdir) - end - - def test_missing_version - assert_raise(RuntimeError) { - pkg = Rake::PackageTask.new("pkgr") { |p| } - } - end - - def test_no_version - pkg = Rake::PackageTask.new("pkgr", :noversion) { |p| } - assert "pkgr", pkg.send(:package_name) - end - - def test_clone - pkg = Rake::PackageTask.new("x", :noversion) - p2 = pkg.clone - pkg.package_files << "y" - p2.package_files << "x" - assert_equal ["y"], pkg.package_files - assert_equal ["x"], p2.package_files - end -end - - -require 'rake/gempackagetask' - -class Rake::TestGemPackageTask < Test::Unit::TestCase - def test_gem_package - gem = Gem::Specification.new do |g| - g.name = "pkgr" - g.version = "1.2.3" - g.files = FileList["x"].resolve - end - pkg = Rake::GemPackageTask.new(gem) do |p| - p.package_files << "y" - end - assert_equal ["x", "y"], pkg.package_files - assert_equal "pkgr-1.2.3.gem", pkg.gem_file - end - - def test_gem_package_with_current_platform - gem = Gem::Specification.new do |g| - g.name = "pkgr" - g.version = "1.2.3" - g.files = FileList["x"].resolve - g.platform = Gem::Platform::CURRENT - end - pkg = Rake::GemPackageTask.new(gem) do |p| - p.package_files << "y" - end - assert_equal ["x", "y"], pkg.package_files - assert_match(/^pkgr-1\.2\.3-(\S+)\.gem$/, pkg.gem_file) - end - - def test_gem_package_with_ruby_platform - gem = Gem::Specification.new do |g| - g.name = "pkgr" - g.version = "1.2.3" - g.files = FileList["x"].resolve - g.platform = Gem::Platform::RUBY - end - pkg = Rake::GemPackageTask.new(gem) do |p| - p.package_files << "y" - end - assert_equal ["x", "y"], pkg.package_files - assert_equal "pkgr-1.2.3.gem", pkg.gem_file - end -end diff --git a/test/rake/test_rake.rb b/test/rake/test_rake.rb index 44fecee559..b2a3928b26 100644 --- a/test/rake/test_rake.rb +++ b/test/rake/test_rake.rb @@ -1,7 +1,6 @@ -require 'test/unit' -require 'rake' +require File.expand_path('../helper', __FILE__) -class Rake::TestRake < Test::Unit::TestCase +class TestRake < Rake::TestCase def test_each_dir_parent assert_equal ['a'], alldirs('a') assert_equal ['a/b', 'a'], alldirs('a/b') @@ -27,13 +26,15 @@ class Rake::TestRake < Test::Unit::TestCase old_app = Rake.application fake_app = Object.new Rake.application = fake_app + assert_equal fake_app, Rake.application + ensure Rake.application = old_app end def test_original_dir_reports_current_dir - assert_equal Dir.pwd, Rake.original_dir + assert_equal @tempdir, Rake.original_dir end end diff --git a/test/rake/test_rake_application.rb b/test/rake/test_rake_application.rb new file mode 100644 index 0000000000..c030c30292 --- /dev/null +++ b/test/rake/test_rake_application.rb @@ -0,0 +1,403 @@ +require File.expand_path('../helper', __FILE__) + +class TestRakeApplication < Rake::TestCase + + def setup + super + + @app = Rake.application + @app.options.rakelib = [] + end + + def test_constant_warning + _, err = capture_io do @app.instance_eval { const_warning("Task") } end + assert_match(/warning/i, err) + assert_match(/deprecated/i, err) + assert_match(/Task/i, err) + end + + def test_display_tasks + @app.options.show_tasks = :tasks + @app.options.show_task_pattern = // + @app.last_description = "COMMENT" + @app.define_task(Rake::Task, "t") + out, = capture_io do @app.instance_eval { display_tasks_and_comments } end + assert_match(/^rake t/, out) + assert_match(/# COMMENT/, out) + end + + def test_display_tasks_with_long_comments + @app.terminal_columns = 80 + @app.options.show_tasks = :tasks + @app.options.show_task_pattern = // + @app.last_description = "1234567890" * 8 + @app.define_task(Rake::Task, "t") + + out, = capture_io do @app.instance_eval { display_tasks_and_comments } end + + assert_match(/^rake t/, out) + assert_match(/# 12345678901234567890123456789012345678901234567890123456789012345\.\.\./, out) + end + + def test_display_tasks_with_task_name_wider_than_tty_display + @app.terminal_columns = 80 + @app.options.show_tasks = :tasks + @app.options.show_task_pattern = // + task_name = "task name" * 80 + @app.last_description = "something short" + @app.define_task(Rake::Task, task_name ) + + out, = capture_io do @app.instance_eval { display_tasks_and_comments } end + + # Ensure the entire task name is output and we end up showing no description + assert_match(/rake #{task_name} # .../, out) + end + + def test_display_tasks_with_very_long_task_name_to_a_non_tty_shows_name_and_comment + @app.options.show_tasks = :tasks + @app.options.show_task_pattern = // + @app.tty_output = false + description = "something short" + task_name = "task name" * 80 + @app.last_description = "something short" + @app.define_task(Rake::Task, task_name ) + + out, = capture_io do @app.instance_eval { display_tasks_and_comments } end + + # Ensure the entire task name is output and we end up showing no description + assert_match(/rake #{task_name} # #{description}/, out) + end + + def test_display_tasks_with_long_comments_to_a_non_tty_shows_entire_comment + @app.options.show_tasks = :tasks + @app.options.show_task_pattern = // + @app.tty_output = false + @app.last_description = "1234567890" * 8 + @app.define_task(Rake::Task, "t") + out, = capture_io do @app.instance_eval { display_tasks_and_comments } end + assert_match(/^rake t/, out) + assert_match(/# #{@app.last_description}/, out) + end + + def test_display_tasks_with_long_comments_to_a_non_tty_with_columns_set_truncates_comments + @app.terminal_columns = 80 + @app.options.show_tasks = :tasks + @app.options.show_task_pattern = // + @app.tty_output = false + @app.last_description = "1234567890" * 8 + @app.define_task(Rake::Task, "t") + + out, = capture_io do @app.instance_eval { display_tasks_and_comments } end + + assert_match(/^rake t/, out) + assert_match(/# 12345678901234567890123456789012345678901234567890123456789012345\.\.\./, out) + end + + def test_describe_tasks + @app.options.show_tasks = :describe + @app.options.show_task_pattern = // + @app.last_description = "COMMENT" + @app.define_task(Rake::Task, "t") + out, = capture_io do @app.instance_eval { display_tasks_and_comments } end + assert_match(/^rake t$/, out) + assert_match(/^ {4}COMMENT$/, out) + end + + def test_show_lines + @app.options.show_tasks = :lines + @app.options.show_task_pattern = // + @app.last_description = "COMMENT" + @app.define_task(Rake::Task, "t") + @app['t'].locations << "HERE:1" + out, = capture_io do @app.instance_eval { display_tasks_and_comments } end + assert_match(/^rake t +[^:]+:\d+ *$/, out) + end + + def test_finding_rakefile + rakefile_default + + assert_match(/Rakefile/i, @app.instance_eval { have_rakefile }) + end + + def test_not_finding_rakefile + @app.instance_eval { @rakefiles = ['NEVER_FOUND'] } + assert( ! @app.instance_eval do have_rakefile end ) + assert_nil @app.rakefile + end + + def test_load_rakefile + rakefile_unittest + + @app.instance_eval do + handle_options + options.silent = true + load_rakefile + end + + assert_equal "rakefile", @app.rakefile.downcase + assert_equal @tempdir, Dir.pwd + end + + def test_load_rakefile_doesnt_print_rakefile_directory_from_same_dir + rakefile_unittest + + _, err = capture_io do + @app.instance_eval do + # pretend we started from the unittest dir + @original_dir = File.expand_path(".") + raw_load_rakefile + end + end + + assert_empty err + end + + def test_load_rakefile_from_subdir + rakefile_unittest + Dir.chdir 'subdir' + + @app.instance_eval do + handle_options + options.silent = true + load_rakefile + end + + assert_equal "rakefile", @app.rakefile.downcase + assert_equal @tempdir, Dir.pwd + end + + def test_load_rakefile_prints_rakefile_directory_from_subdir + rakefile_unittest + Dir.chdir 'subdir' + + app = Rake::Application.new + app.options.rakelib = [] + + _, err = capture_io do + app.instance_eval do + raw_load_rakefile + end + end + + assert_equal "(in #{@tempdir}\)\n", err + end + + def test_load_rakefile_doesnt_print_rakefile_directory_from_subdir_if_silent + rakefile_unittest + Dir.chdir 'subdir' + + _, err = capture_io do + @app.instance_eval do + handle_options + options.silent = true + raw_load_rakefile + end + end + + assert_empty err + end + + def test_load_rakefile_not_found + Dir.chdir @tempdir + ENV['RAKE_SYSTEM'] = 'not_exist' + + @app.instance_eval do + handle_options + options.silent = true + end + + ex = assert_raises(RuntimeError) do + @app.instance_eval do raw_load_rakefile end + end + + assert_match(/no rakefile found/i, ex.message) + end + + def test_load_from_system_rakefile + rake_system_dir + + @app.instance_eval do + handle_options + options.silent = true + options.load_system = true + options.rakelib = [] + load_rakefile + end + + assert_equal @system_dir, @app.system_dir + assert_nil @app.rakefile + rescue SystemExit + flunk 'failed to load rakefile' + end + + def test_load_from_calculated_system_rakefile + rakefile_default + flexmock(@app, :standard_system_dir => "__STD_SYS_DIR__") + + ENV['RAKE_SYSTEM'] = nil + + @app.instance_eval do + handle_options + options.silent = true + options.load_system = true + options.rakelib = [] + load_rakefile + end + + assert_equal "__STD_SYS_DIR__", @app.system_dir + rescue SystemExit + flunk 'failed to find system rakefile' + end + + def test_terminal_columns + old_RAKE_COLUMNS = ENV['RAKE_COLUMNS'] + + ENV['RAKE_COLUMNS'] = '42' + + app = Rake::Application.new + + assert_equal 42, app.terminal_columns + ensure + if old_RAKE_COLUMNS then + ENV['RAKE_COLUMNS'].delete + else + ENV['RAKE_COLUMNS'] = old_RAKE_COLUMNS + end + end + + def test_windows + assert ! (@app.windows? && @app.unix?) + end + + def test_loading_imports + mock = flexmock("loader") + mock.should_receive(:load).with("x.dummy").once + @app.instance_eval do + add_loader("dummy", mock) + add_import("x.dummy") + load_imports + end + end + + def test_building_imported_files_on_demand + mock = flexmock("loader") + mock.should_receive(:load).with("x.dummy").once + mock.should_receive(:make_dummy).with_no_args.once + @app.instance_eval do + intern(Rake::Task, "x.dummy").enhance do mock.make_dummy end + add_loader("dummy", mock) + add_import("x.dummy") + load_imports + end + end + + def test_handle_options_should_strip_options_from_ARGV + assert !@app.options.trace + + valid_option = '--trace' + ARGV.clear + ARGV << valid_option + + @app.handle_options + + assert !ARGV.include?(valid_option) + assert @app.options.trace + end + + def test_good_run + ran = false + + ARGV << '--rakelib=""' + + @app.options.silent = true + + @app.instance_eval do + intern(Rake::Task, "default").enhance { ran = true } + end + + rakefile_default + + out, err = capture_io do + @app.run + end + + assert ran + assert_empty err + assert_equal "DEFAULT\n", out + end + + def test_display_task_run + ran = false + ARGV.clear + ARGV << '-f' << '-s' << '--tasks' << '--rakelib=""' + @app.last_description = "COMMENT" + @app.define_task(Rake::Task, "default") + out, = capture_io { @app.run } + assert @app.options.show_tasks + assert ! ran + assert_match(/rake default/, out) + assert_match(/# COMMENT/, out) + end + + def test_display_prereqs + ran = false + ARGV.clear + ARGV << '-f' << '-s' << '--prereqs' << '--rakelib=""' + @app.last_description = "COMMENT" + t = @app.define_task(Rake::Task, "default") + t.enhance([:a, :b]) + @app.define_task(Rake::Task, "a") + @app.define_task(Rake::Task, "b") + out, = capture_io { @app.run } + assert @app.options.show_prereqs + assert ! ran + assert_match(/rake a$/, out) + assert_match(/rake b$/, out) + assert_match(/rake default\n( *(a|b)\n){2}/m, out) + end + + def test_bad_run + @app.intern(Rake::Task, "default").enhance { fail } + ARGV.clear + ARGV << '-f' << '-s' << '--rakelib=""' + assert_raises(SystemExit) { + _, err = capture_io { @app.run } + assert_match(/see full trace/, err) + } + ensure + ARGV.clear + end + + def test_bad_run_with_trace + @app.intern(Rake::Task, "default").enhance { fail } + ARGV.clear + ARGV << '-f' << '-s' << '-t' + assert_raises(SystemExit) { + _, err = capture_io { @app.run } + refute_match(/see full trace/, err) + } + ensure + ARGV.clear + end + + def test_run_with_bad_options + @app.intern(Rake::Task, "default").enhance { fail } + ARGV.clear + ARGV << '-f' << '-s' << '--xyzzy' + assert_raises(SystemExit) { + capture_io { @app.run } + } + ensure + ARGV.clear + end + + def test_deprecation_message + _, err = capture_io do + @app.deprecate("a", "b", "c") + end + assert_match(/'a' is deprecated/i, err) + assert_match(/use 'b' instead/i, err) + assert_match(/at c$/i, err) + end +end + diff --git a/test/rake/test_rake_application_options.rb b/test/rake/test_rake_application_options.rb new file mode 100644 index 0000000000..c1471f443e --- /dev/null +++ b/test/rake/test_rake_application_options.rb @@ -0,0 +1,335 @@ +require File.expand_path('../helper', __FILE__) + +TESTING_REQUIRE = [ ] + +class TestRakeApplicationOptions < Rake::TestCase + + def setup + super + + clear_argv + Rake::FileUtilsExt.verbose_flag = false + Rake::FileUtilsExt.nowrite_flag = false + TESTING_REQUIRE.clear + end + + def teardown + clear_argv + Rake::FileUtilsExt.verbose_flag = false + Rake::FileUtilsExt.nowrite_flag = false + + super + end + + def clear_argv + while ! ARGV.empty? + ARGV.pop + end + end + + def test_default_options + opts = command_line + assert_nil opts.classic_namespace + assert_nil opts.dryrun + assert_nil opts.ignore_system + assert_nil opts.load_system + assert_nil opts.nosearch + assert_equal ['rakelib'], opts.rakelib + assert_nil opts.show_prereqs + assert_nil opts.show_task_pattern + assert_nil opts.show_tasks + assert_nil opts.silent + assert_nil opts.trace + assert_equal ['rakelib'], opts.rakelib + assert ! Rake::FileUtilsExt.verbose_flag + assert ! Rake::FileUtilsExt.nowrite_flag + end + + def test_dry_run + flags('--dry-run', '-n') do |opts| + assert opts.dryrun + assert opts.trace + assert Rake::FileUtilsExt.verbose_flag + assert Rake::FileUtilsExt.nowrite_flag + end + end + + def test_describe + flags('--describe') do |opts| + assert_equal :describe, opts.show_tasks + assert_equal(//.to_s, opts.show_task_pattern.to_s) + end + end + + def test_describe_with_pattern + flags('--describe=X') do |opts| + assert_equal :describe, opts.show_tasks + assert_equal(/X/.to_s, opts.show_task_pattern.to_s) + end + end + + def test_execute + $xyzzy = 0 + flags('--execute=$xyzzy=1', '-e $xyzzy=1') do |opts| + assert_equal 1, $xyzzy + assert_equal :exit, @exit + $xyzzy = 0 + end + end + + def test_execute_and_continue + $xyzzy = 0 + flags('--execute-continue=$xyzzy=1', '-E $xyzzy=1') do |opts| + assert_equal 1, $xyzzy + refute_equal :exit, @exit + $xyzzy = 0 + end + end + + def test_execute_and_print + $xyzzy = 0 + out, = capture_io do + flags('--execute-print=$xyzzy="pugh"', '-p $xyzzy="pugh"') do |opts| + assert_equal 'pugh', $xyzzy + assert_equal :exit, @exit + $xyzzy = 0 + end + end + + assert_match(/^pugh$/, out) + end + + def test_help + out, = capture_io do + flags '--help', '-H', '-h' + end + + assert_match(/\Arake/, out) + assert_match(/\boptions\b/, out) + assert_match(/\btargets\b/, out) + assert_equal :exit, @exit + end + + def test_libdir + flags(['--libdir', 'xx'], ['-I', 'xx'], ['-Ixx']) do |opts| + $:.include?('xx') + end + ensure + $:.delete('xx') + end + + def test_rakefile + flags(['--rakefile', 'RF'], ['--rakefile=RF'], ['-f', 'RF'], ['-fRF']) do |opts| + assert_equal ['RF'], @app.instance_eval { @rakefiles } + end + end + + def test_rakelib + flags(['--rakelibdir', 'A:B:C'], ['--rakelibdir=A:B:C'], ['-R', 'A:B:C'], ['-RA:B:C']) do |opts| + assert_equal ['A', 'B', 'C'], opts.rakelib + end + end + + def test_require + $LOAD_PATH.unshift @tempdir + + open 'reqfile.rb', 'w' do |io| io << 'TESTING_REQUIRE << 1' end + open 'reqfile2.rb', 'w' do |io| io << 'TESTING_REQUIRE << 2' end + open 'reqfile3.rake', 'w' do |io| io << 'TESTING_REQUIRE << 3' end + + flags(['--require', 'reqfile'], '-rreqfile2', '-rreqfile3') + + assert_includes TESTING_REQUIRE, 1 + assert_includes TESTING_REQUIRE, 2 + assert_includes TESTING_REQUIRE, 3 + + assert_equal 3, TESTING_REQUIRE.size + ensure + $LOAD_PATH.delete @tempdir + end + + def test_missing_require + ex = assert_raises(LoadError) do + flags(['--require', 'test/missing']) do |opts| + end + end + assert_match(/such file/, ex.message) + assert_match(/test\/missing/, ex.message) + end + + def test_prereqs + flags('--prereqs', '-P') do |opts| + assert opts.show_prereqs + end + end + + def test_quiet + flags('--quiet', '-q') do |opts| + assert ! Rake::FileUtilsExt.verbose_flag + assert ! opts.silent + end + end + + def test_no_search + flags('--nosearch', '--no-search', '-N') do |opts| + assert opts.nosearch + end + end + + def test_silent + flags('--silent', '-s') do |opts| + assert ! Rake::FileUtilsExt.verbose_flag + assert opts.silent + end + end + + def test_system + flags('--system', '-g') do |opts| + assert opts.load_system + end + end + + def test_no_system + flags('--no-system', '-G') do |opts| + assert opts.ignore_system + end + end + + def test_trace + flags('--trace', '-t') do |opts| + assert opts.trace + assert Rake::FileUtilsExt.verbose_flag + assert ! Rake::FileUtilsExt.nowrite_flag + end + end + + def test_trace_rules + flags('--rules') do |opts| + assert opts.trace_rules + end + end + + def test_tasks + flags('--tasks', '-T') do |opts| + assert_equal :tasks, opts.show_tasks + assert_equal(//.to_s, opts.show_task_pattern.to_s) + end + flags(['--tasks', 'xyz'], ['-Txyz']) do |opts| + assert_equal :tasks, opts.show_tasks + assert_equal(/xyz/.to_s, opts.show_task_pattern.to_s) + end + end + + def test_where + flags('--where', '-W') do |opts| + assert_equal :lines, opts.show_tasks + assert_equal(//.to_s, opts.show_task_pattern.to_s) + end + flags(['--where', 'xyz'], ['-Wxyz']) do |opts| + assert_equal :lines, opts.show_tasks + assert_equal(/xyz/.to_s, opts.show_task_pattern.to_s) + end + end + + def test_no_deprecated_messages + flags('--no-deprecation-warnings', '-X') do |opts| + assert opts.ignore_deprecate + end + end + + def test_verbose + out, = capture_io do + flags('--verbose', '-V') do |opts| + assert Rake::FileUtilsExt.verbose_flag + assert ! opts.silent + end + end + + assert_equal "rake, version #{Rake::VERSION}\n", out + end + + def test_version + out, = capture_io do + flags '--version', '-V' + end + + assert_match(/\bversion\b/, out) + assert_match(/\b#{RAKEVERSION}\b/, out) + assert_equal :exit, @exit + end + + def test_classic_namespace + _, err = capture_io do + flags(['--classic-namespace'], + ['-C', '-T', '-P', '-n', '-s', '-t']) do |opts| + assert opts.classic_namespace + assert_equal opts.show_tasks, $show_tasks + assert_equal opts.show_prereqs, $show_prereqs + assert_equal opts.trace, $trace + assert_equal opts.dryrun, $dryrun + assert_equal opts.silent, $silent + end + end + + assert_match(/deprecated/, err) + end + + def test_bad_option + _, err = capture_io do + ex = assert_raises(OptionParser::InvalidOption) do + flags('--bad-option') + end + + if ex.message =~ /^While/ # Ruby 1.9 error message + assert_match(/while parsing/i, ex.message) + else # Ruby 1.8 error message + assert_match(/(invalid|unrecognized) option/i, ex.message) + assert_match(/--bad-option/, ex.message) + end + end + + assert_equal '', err + end + + def test_task_collection + command_line("a", "b") + assert_equal ["a", "b"], @tasks.sort + end + + def test_default_task_collection + command_line() + assert_equal ["default"], @tasks + end + + def test_environment_definition + ENV.delete('TESTKEY') + command_line("a", "TESTKEY=12") + assert_equal ["a"], @tasks.sort + assert '12', ENV['TESTKEY'] + end + + def flags(*sets) + sets.each do |set| + ARGV.clear + + @exit = catch(:system_exit) { command_line(*set) } + + yield(@app.options) if block_given? + end + end + + def command_line(*options) + options.each do |opt| ARGV << opt end + @app = Rake::Application.new + def @app.exit(*args) + throw :system_exit, :exit + end + @app.instance_eval do + handle_options + collect_tasks + end + @tasks = @app.top_level_tasks + @app.options + end +end + diff --git a/test/rake/test_clean.rb b/test/rake/test_rake_clean.rb similarity index 69% rename from test/rake/test_clean.rb rename to test/rake/test_rake_clean.rb index 0e8b81c2c7..1541c69359 100644 --- a/test/rake/test_clean.rb +++ b/test/rake/test_rake_clean.rb @@ -1,9 +1,11 @@ -require 'test/unit' +require File.expand_path('../helper', __FILE__) require 'rake/clean' -class Rake::TestClean < Test::Unit::TestCase +class TestRakeClean < Rake::TestCase include Rake def test_clean + load 'rake/clean.rb', true + assert Task['clean'], "Should define clean" assert Task['clobber'], "Should define clobber" assert Task['clobber'].prerequisites.include?("clean"), diff --git a/test/rake/test_definitions.rb b/test/rake/test_rake_definitions.rb similarity index 74% rename from test/rake/test_definitions.rb rename to test/rake/test_rake_definitions.rb index a5440dcadc..839c40419e 100644 --- a/test/rake/test_definitions.rb +++ b/test/rake/test_rake_definitions.rb @@ -1,15 +1,14 @@ -require 'test/unit' +require File.expand_path('../helper', __FILE__) require 'fileutils' -require 'rake' -require_relative 'filecreation' -###################################################################### -class Rake::TestDefinitions < Test::Unit::TestCase +class TestRakeDefinitions < Rake::TestCase include Rake - EXISTINGFILE = "testdata/existing" + EXISTINGFILE = "existing" def setup + super + Task.clear end @@ -24,10 +23,10 @@ class Rake::TestDefinitions < Test::Unit::TestCase def test_file_task done = false - file "testdata/one" => "testdata/two" do done = true end - file "testdata/two" - file "testdata/three" => ["testdata/one", "testdata/two"] - check_tasks("testdata/one", "testdata/two", "testdata/three") + file "one" => "two" do done = true end + file "two" + file "three" => ["one", "two"] + check_tasks("one", "two", "three") assert done, "Should be done" end @@ -56,8 +55,8 @@ class Rake::TestDefinitions < Test::Unit::TestCase end def test_missing_dependencies - task :x => ["testdata/missing"] - assert_raise(RuntimeError) { Task[:x].invoke } + task :x => ["missing"] + assert_raises(RuntimeError) { Task[:x].invoke } end def test_implicit_file_dependencies diff --git a/test/rake/test_rake_directory_task.rb b/test/rake/test_rake_directory_task.rb new file mode 100644 index 0000000000..7f641f53ca --- /dev/null +++ b/test/rake/test_rake_directory_task.rb @@ -0,0 +1,45 @@ +require File.expand_path('../helper', __FILE__) +require 'fileutils' + +class TestRakeDirectoryTask < Rake::TestCase + include Rake + + def test_directory + desc "DESC" + + directory "a/b/c" + + assert_equal FileCreationTask, Task["a"].class + assert_equal FileCreationTask, Task["a/b"].class + assert_equal FileCreationTask, Task["a/b/c"].class + + assert_nil Task["a"].comment + assert_nil Task["a/b"].comment + assert_equal "DESC", Task["a/b/c"].comment + + verbose(false) { + Task['a/b'].invoke + } + + assert File.exist?("a/b") + refute File.exist?("a/b/c") + end + + if Rake::Win32.windows? + def test_directory_win32 + desc "WIN32 DESC" + directory 'c:/a/b/c' + assert_equal FileCreationTask, Task['c:'].class + assert_equal FileCreationTask, Task['c:/a'].class + assert_equal FileCreationTask, Task['c:/a/b/c'].class + assert_nil Task['c:/'].comment + assert_equal "WIN32 DESC", Task['c:/a/b/c'].comment + assert_nil Task['c:/a/b'].comment + verbose(false) { + Task['c:/a/b'].invoke + } + assert File.exist?('c:/a/b') + refute File.exist?('c:/a/b/c') + end + end +end diff --git a/test/rake/test_rake_dsl.rb b/test/rake/test_rake_dsl.rb new file mode 100644 index 0000000000..294ff2bd0d --- /dev/null +++ b/test/rake/test_rake_dsl.rb @@ -0,0 +1,73 @@ +require File.expand_path('../helper', __FILE__) + +class TestRakeDsl < Rake::TestCase + + def setup + super + Rake::Task.clear + end + + def test_namespace_command + namespace "n" do + task "t" + end + refute_nil Rake::Task["n:t"] + end + + def test_namespace_command_with_bad_name + ex = assert_raises(ArgumentError) do + namespace 1 do end + end + assert_match(/string/i, ex.message) + assert_match(/symbol/i, ex.message) + end + + def test_namespace_command_with_a_string_like_object + name = Object.new + def name.to_str + "bob" + end + namespace name do + task "t" + end + refute_nil Rake::Task["bob:t"] + end + + class Foo + def initialize + task :foo_deprecated_a => "foo_deprecated_b" do + print "a" + end + file "foo_deprecated_b" do + print "b" + end + end + end + + def test_deprecated_object_dsl + out, err = capture_io do + Foo.new + Rake.application.invoke_task :foo_deprecated_a + end + assert_equal("ba", out) + assert_match(/deprecated/, err) + assert_match(/Foo\#task/, err) + assert_match(/Foo\#file/, err) + assert_match(/test_rake_dsl\.rb:\d+/, err) + end + + def test_deprecated_object_dsl_with_suppressed_warnings + Rake.application.options.ignore_deprecate = true + out, err = capture_io do + Foo.new + Rake.application.invoke_task :foo_deprecated_a + end + assert_equal("ba", out) + refute_match(/deprecated/, err) + refute_match(/Foo\#task/, err) + refute_match(/Foo\#file/, err) + refute_match(/test_rake_dsl\.rb:\d+/, err) + ensure + Rake.application.options.ignore_deprecate = false + end +end diff --git a/test/rake/test_earlytime.rb b/test/rake/test_rake_early_time.rb similarity index 83% rename from test/rake/test_earlytime.rb rename to test/rake/test_rake_early_time.rb index d94523cffd..18c4dad324 100644 --- a/test/rake/test_earlytime.rb +++ b/test/rake/test_rake_early_time.rb @@ -1,10 +1,8 @@ -require 'test/unit' -require 'rake' +require File.expand_path('../helper', __FILE__) -class Rake::TestEarlyTime < Test::Unit::TestCase +class TestRakeEarlyTime < Rake::TestCase def test_create early = Rake::EarlyTime.instance - time = Time.mktime(1970, 1, 1, 0, 0, 0) assert early <= Time.now assert early < Time.now assert early != Time.now diff --git a/test/rake/test_extension.rb b/test/rake/test_rake_extension.rb similarity index 87% rename from test/rake/test_extension.rb rename to test/rake/test_rake_extension.rb index 3b10a4c6ed..18d55f19f9 100644 --- a/test/rake/test_extension.rb +++ b/test/rake/test_rake_extension.rb @@ -1,9 +1,7 @@ -require 'test/unit' -require 'rake' +require File.expand_path('../helper', __FILE__) require 'stringio' -###################################################################### -class Rake::TestExtension < Test::Unit::TestCase +class TestRakeExtension < Rake::TestCase module Redirect def error_redirect diff --git a/test/rake/test_file_creation_task.rb b/test/rake/test_rake_file_creation_task.rb similarity index 87% rename from test/rake/test_file_creation_task.rb rename to test/rake/test_rake_file_creation_task.rb index 1f2af90399..d486d2f0d4 100644 --- a/test/rake/test_file_creation_task.rb +++ b/test/rake/test_rake_file_creation_task.rb @@ -1,21 +1,17 @@ -require 'test/unit' +require File.expand_path('../helper', __FILE__) require 'fileutils' -require 'rake' -require_relative 'filecreation' ###################################################################### -class Rake::TestFileCreationTask < Test::Unit::TestCase +class TestRakeFileCreationTask < Rake::TestCase include Rake - include FileCreation + include Rake::DSL - DUMMY_DIR = 'testdata/dummy_dir' + DUMMY_DIR = 'dummy_dir' def setup - Task.clear - end + super - def teardown - FileUtils.rm_rf DUMMY_DIR + Task.clear end def test_file_needed diff --git a/test/rake/test_filelist.rb b/test/rake/test_rake_file_list.rb similarity index 65% rename from test/rake/test_filelist.rb rename to test/rake/test_rake_file_list.rb index 2b1ad44237..08939fb6ed 100644 --- a/test/rake/test_filelist.rb +++ b/test/rake/test_rake_file_list.rb @@ -1,28 +1,34 @@ -require 'test/unit' -require 'tmpdir' -require 'rake' +require File.expand_path('../helper', __FILE__) -require_relative 'capture_stdout' - -class Rake::TestFileList < Test::Unit::TestCase +class TestRakeFileList < Rake::TestCase FileList = Rake::FileList - include CaptureStdout def setup - @oldwd = Dir.pwd - @tmpwd = Dir.mktmpdir - Dir.chdir(@tmpwd) - create_test_data + super + + FileUtils.mkdir "CVS" rescue nil + FileUtils.mkdir ".svn" rescue nil + @cdir = "cfiles" + FileUtils.mkdir @cdir rescue nil + FileUtils.touch ".dummy" + FileUtils.touch "x.bak" + FileUtils.touch "x~" + FileUtils.touch "core" + FileUtils.touch "x.c" + FileUtils.touch "xyz.c" + FileUtils.touch "abc.c" + FileUtils.touch "abc.h" + FileUtils.touch "abc.x" + FileUtils.touch "existing" + + open 'xyzzy.txt', 'w' do |io| + io.puts 'x' + io.puts 'XYZZY' + end + end - def teardown -# FileList.select_default_ignore_patterns - FileUtils.rm_rf("testdata") - Dir.chdir(@oldwd) - FileUtils.rm_rf(@tmpwd) - end - - def test_delgating_methods_do_not_include_to_a_or_to_ary + def test_delegating_methods_do_not_include_to_a_or_to_ary assert ! FileList::DELEGATING_METHODS.include?("to_a"), "should not include to_a" assert ! FileList::DELEGATING_METHODS.include?(:to_a), "should not include to_a" assert ! FileList::DELEGATING_METHODS.include?("to_ary"), "should not include to_ary" @@ -35,8 +41,8 @@ class Rake::TestFileList < Test::Unit::TestCase end def test_create_with_args - fl = FileList.new("testdata/*.c", "x") - assert_equal ["testdata/abc.c", "testdata/x.c", "testdata/xyz.c", "x"].sort, + fl = FileList.new("*.c", "x") + assert_equal ["abc.c", "x.c", "xyz.c", "x"].sort, fl.sort end @@ -46,14 +52,14 @@ class Rake::TestFileList < Test::Unit::TestCase end def test_create_with_brackets - fl = FileList["testdata/*.c", "x"] - assert_equal ["testdata/abc.c", "testdata/x.c", "testdata/xyz.c", "x"].sort, + fl = FileList["*.c", "x"] + assert_equal ["abc.c", "x.c", "xyz.c", "x"].sort, fl.sort end def test_create_with_brackets_and_filelist - fl = FileList[FileList["testdata/*.c", "x"]] - assert_equal ["testdata/abc.c", "testdata/x.c", "testdata/xyz.c", "x"].sort, + fl = FileList[FileList["*.c", "x"]] + assert_equal ["abc.c", "x.c", "xyz.c", "x"].sort, fl.sort end @@ -63,8 +69,8 @@ class Rake::TestFileList < Test::Unit::TestCase end def test_include_with_another_filelist - fl = FileList.new.include(FileList["testdata/*.c", "x"]) - assert_equal ["testdata/abc.c", "testdata/x.c", "testdata/xyz.c", "x"].sort, + fl = FileList.new.include(FileList["*.c", "x"]) + assert_equal ["abc.c", "x.c", "xyz.c", "x"].sort, fl.sort end @@ -92,86 +98,87 @@ class Rake::TestFileList < Test::Unit::TestCase def test_match fl = FileList.new - fl.include(File.expand_path('../test*.rb', __FILE__)) - assert fl.include?(__FILE__) - assert fl.size > 3 - fl.each { |fn| assert_match(/\.rb$/, fn) } + fl.include '*.c' + + assert_equal %w[abc.c x.c xyz.c], fl.sort end def test_add_matching fl = FileList.new fl << "a.java" - fl.include(File.dirname(__FILE__)+"/*.rb") - assert_equal "a.java", fl[0] - assert fl.size > 2 - assert fl.include?(__FILE__) + fl.include '*.c' + + assert_equal %w[a.java abc.c x.c xyz.c], fl.sort end def test_multiple_patterns - create_test_data fl = FileList.new - fl.include('*.c', '*xist*') + fl.include('*.z', '*foo*') + assert_equal [], fl - fl.include('testdata/*.c', 'testdata/*xist*') - assert_equal [ - 'testdata/x.c', 'testdata/xyz.c', 'testdata/abc.c', 'testdata/existing' - ].sort, fl.sort + + fl.include('*.c', '*xist*') + assert_equal %w[x.c xyz.c abc.c existing].sort, fl.sort end def test_square_bracket_pattern fl = FileList.new - fl.include("testdata/abc.[ch]") + fl.include("abc.[ch]") assert fl.size == 2 - assert fl.include?("testdata/abc.c") - assert fl.include?("testdata/abc.h") + assert fl.include?("abc.c") + assert fl.include?("abc.h") end def test_curly_bracket_pattern fl = FileList.new - fl.include("testdata/abc.{c,h}") + fl.include("abc.{c,h}") assert fl.size == 2 - assert fl.include?("testdata/abc.c") - assert fl.include?("testdata/abc.h") + assert fl.include?("abc.c") + assert fl.include?("abc.h") end def test_reject fl = FileList.new - fl.include %w(testdata/x.c testdata/abc.c testdata/xyz.c testdata/existing) - fl.reject! { |fn| fn =~ %r{/x} } - assert_equal [ - 'testdata/abc.c', 'testdata/existing' - ], fl + fl.include %w(x.c abc.c xyz.c existing) + fl.reject! { |fn| fn =~ /^x/ } + assert_equal %w[abc.c existing], fl end def test_exclude - fl = FileList['testdata/x.c', 'testdata/abc.c', 'testdata/xyz.c', 'testdata/existing'] + fl = FileList['x.c', 'abc.c', 'xyz.c', 'existing'] fl.each { |fn| touch fn, :verbose => false } - x = fl.exclude(%r{/x.+\.}) + + x = fl.exclude(%r{^x.+\.}) + assert_equal FileList, x.class - assert_equal %w(testdata/x.c testdata/abc.c testdata/existing), fl + assert_equal %w(x.c abc.c existing), fl assert_equal fl.object_id, x.object_id - fl.exclude('testdata/*.c') - assert_equal ['testdata/existing'], fl - fl.exclude('testdata/existing') + + fl.exclude('*.c') + + assert_equal ['existing'], fl + + fl.exclude('existing') + assert_equal [], fl end def test_excluding_via_block - fl = FileList['testdata/a.c', 'testdata/b.c', 'testdata/xyz.c'] + fl = FileList['a.c', 'b.c', 'xyz.c'] fl.exclude { |fn| fn.pathmap('%n') == 'xyz' } assert fl.exclude?("xyz.c"), "Should exclude xyz.c" - assert_equal ['testdata/a.c', 'testdata/b.c'], fl + assert_equal ['a.c', 'b.c'], fl end def test_exclude_return_on_create - fl = FileList['testdata/*'].exclude(/.*\.[hcx]$/) - assert_equal ['testdata/existing', 'testdata/cfiles'].sort, fl.sort + fl = FileList['*'].exclude(/.*\.[hcx]$/) + assert_equal %w[cfiles existing xyzzy.txt], fl.sort assert_equal FileList, fl.class end def test_exclude_with_string_return_on_create - fl = FileList['testdata/*'].exclude('testdata/abc.c') - assert_equal %w(testdata/existing testdata/cfiles testdata/x.c testdata/abc.h testdata/abc.x testdata/xyz.c).sort, fl.sort + fl = FileList['*'].exclude('abc.c') + assert_equal %w[abc.h abc.x cfiles existing x.c xyz.c xyzzy.txt], fl.sort assert_equal FileList, fl.class end @@ -179,8 +186,8 @@ class Rake::TestFileList < Test::Unit::TestCase fl = FileList.new fl.clear_exclude fl.include("**/*~", "**/*.bak", "**/core") - assert fl.member?("testdata/core"), "Should include core" - assert fl.member?("testdata/x.bak"), "Should include .bak files" + assert fl.member?("core"), "Should include core" + assert fl.member?("x.bak"), "Should include .bak files" end def test_unique @@ -207,54 +214,54 @@ class Rake::TestFileList < Test::Unit::TestCase end def test_to_s_pending - fl = FileList['testdata/abc.*'] + fl = FileList['abc.*'] result = fl.to_s - assert_match(%r{testdata/abc\.c}, result) - assert_match(%r{testdata/abc\.h}, result) - assert_match(%r{testdata/abc\.x}, result) - assert_match(%r{(testdata/abc\..\b ?){2}}, result) + assert_match(%r{abc\.c}, result) + assert_match(%r{abc\.h}, result) + assert_match(%r{abc\.x}, result) + assert_match(%r{(abc\..\b ?){2}}, result) end def test_inspect_pending - fl = FileList['testdata/abc.*'] + fl = FileList['abc.*'] result = fl.inspect - assert_match(%r{"testdata/abc\.c"}, result) - assert_match(%r{"testdata/abc\.h"}, result) - assert_match(%r{"testdata/abc\.x"}, result) - assert_match(%r|^\[("testdata/abc\..", ){2}"testdata/abc\.."\]$|, result) + assert_match(%r{"abc\.c"}, result) + assert_match(%r{"abc\.h"}, result) + assert_match(%r{"abc\.x"}, result) + assert_match(%r|^\[("abc\..", ){2}"abc\.."\]$|, result) end def test_sub - fl = FileList["testdata/*.c"] + fl = FileList["*.c"] f2 = fl.sub(/\.c$/, ".o") assert_equal FileList, f2.class - assert_equal ["testdata/abc.o", "testdata/x.o", "testdata/xyz.o"].sort, + assert_equal ["abc.o", "x.o", "xyz.o"].sort, f2.sort f3 = fl.gsub(/\.c$/, ".o") assert_equal FileList, f3.class - assert_equal ["testdata/abc.o", "testdata/x.o", "testdata/xyz.o"].sort, + assert_equal ["abc.o", "x.o", "xyz.o"].sort, f3.sort end def test_claim_to_be_a_kind_of_array - fl = FileList['testdata/*.c'] + fl = FileList['*.c'] assert fl.is_a?(Array) assert fl.kind_of?(Array) end def test_claim_to_be_a_kind_of_filelist - fl = FileList['testdata/*.c'] + fl = FileList['*.c'] assert fl.is_a?(FileList) assert fl.kind_of?(FileList) end def test_claim_to_be_a_filelist_instance - fl = FileList['testdata/*.c'] + fl = FileList['*.c'] assert fl.instance_of?(FileList) end def test_dont_claim_to_be_an_array_instance - fl = FileList['testdata/*.c'] + fl = FileList['*.c'] assert ! fl.instance_of?(Array) end @@ -310,73 +317,89 @@ class Rake::TestFileList < Test::Unit::TestCase end def test_gsub - create_test_data - fl = FileList["testdata/*.c"] + fl = FileList["*.c"] f2 = fl.gsub(/a/, "A") - assert_equal ["testdAtA/Abc.c", "testdAtA/x.c", "testdAtA/xyz.c"].sort, + assert_equal ["Abc.c", "x.c", "xyz.c"].sort, f2.sort end def test_gsub! - create_test_data - f = FileList["testdata/*.c"] + f = FileList["*.c"] f.gsub!(/a/, "A") - assert_equal ["testdAtA/Abc.c", "testdAtA/x.c", "testdAtA/xyz.c"].sort, + assert_equal ["Abc.c", "x.c", "xyz.c"].sort, f.sort end + def test_egrep_returns_0_if_no_matches + files = FileList['test/lib/*_test.rb'].exclude("test/lib/filelist_test.rb") + assert_equal 0, files.egrep(/XYZZY/) { } + end + def test_egrep_with_output - files = FileList[File.expand_path('../test*.rb', __FILE__)] - the_line_number = __LINE__ + 1 - out = capture_stdout do files.egrep(/PUGH/) end - assert_match(/:#{the_line_number}:/, out) + files = FileList['*.txt'] + + out, = capture_io do + files.egrep(/XYZZY/) + end + + assert_equal "xyzzy.txt:2:XYZZY\n", out end def test_egrep_with_block - files = FileList[File.expand_path('../test*.rb', __FILE__)] - found = false - the_line_number = __LINE__ + 1 - files.egrep(/XYZZY/) do |fn, ln, line | - assert_equal __FILE__, fn - assert_equal the_line_number, ln - assert_match(/files\.egrep/, line) - found = true + files = FileList['*.txt'] + found = nil + + files.egrep(/XYZZY/) do |fn, ln, line| + found = [fn, ln, line] end - assert found, "should have found a matching line" + + assert_equal ["xyzzy.txt", 2, "XYZZY\n"], found + end + + def test_egrep_with_error + files = FileList['*.txt'] + + _, err = capture_io do + files.egrep(/XYZZY/) do |fn, ln, line | + raise "_EGREP_FAILURE_" + end + end + + assert_equal "Error while processing 'xyzzy.txt': _EGREP_FAILURE_\n", err end def test_existing - fl = FileList['testdata/abc.c', 'testdata/notthere.c'] - assert_equal ["testdata/abc.c"], fl.existing + fl = FileList['abc.c', 'notthere.c'] + assert_equal ["abc.c"], fl.existing assert fl.existing.is_a?(FileList) end def test_existing! - fl = FileList['testdata/abc.c', 'testdata/notthere.c'] + fl = FileList['abc.c', 'notthere.c'] result = fl.existing! - assert_equal ["testdata/abc.c"], fl + assert_equal ["abc.c"], fl assert_equal fl.object_id, result.object_id end def test_ignore_special - f = FileList['testdata/*'] - assert ! f.include?("testdata/CVS"), "Should not contain CVS" - assert ! f.include?("testdata/.svn"), "Should not contain .svn" - assert ! f.include?("testdata/.dummy"), "Should not contain dot files" - assert ! f.include?("testdata/x.bak"), "Should not contain .bak files" - assert ! f.include?("testdata/x~"), "Should not contain ~ files" - assert ! f.include?("testdata/core"), "Should not contain core files" + f = FileList['*'] + assert ! f.include?("CVS"), "Should not contain CVS" + assert ! f.include?(".svn"), "Should not contain .svn" + assert ! f.include?(".dummy"), "Should not contain dot files" + assert ! f.include?("x.bak"), "Should not contain .bak files" + assert ! f.include?("x~"), "Should not contain ~ files" + assert ! f.include?("core"), "Should not contain core files" end def test_clear_ignore_patterns - f = FileList['testdata/*', 'testdata/.svn'] + f = FileList['*', '.svn'] f.clear_exclude - assert f.include?("testdata/abc.c") - assert f.include?("testdata/xyz.c") - assert f.include?("testdata/CVS") - assert f.include?("testdata/.svn") - assert f.include?("testdata/x.bak") - assert f.include?("testdata/x~") + assert f.include?("abc.c") + assert f.include?("xyz.c") + assert f.include?("CVS") + assert f.include?(".svn") + assert f.include?("x.bak") + assert f.include?("x~") end def test_exclude_with_alternate_file_seps @@ -414,8 +437,8 @@ class Rake::TestFileList < Test::Unit::TestCase end def test_flatten - assert_equal ['a', 'testdata/x.c', 'testdata/xyz.c', 'testdata/abc.c'].sort, - ['a', FileList['testdata/*.c']].flatten.sort + assert_equal ['a', 'x.c', 'xyz.c', 'abc.c'].sort, + ['a', FileList['*.c']].flatten.sort end def test_clone_and_dup @@ -449,7 +472,7 @@ class Rake::TestFileList < Test::Unit::TestCase a = FileList['a', 'b', 'c'] a.freeze c = a.clone - assert_raise(TypeError, RuntimeError) do + assert_raises(TypeError, RuntimeError) do c << 'more' end end @@ -592,7 +615,7 @@ class Rake::TestFileList < Test::Unit::TestCase end def test_file_utils_can_use_filelists - cfiles = FileList['testdata/*.c'] + cfiles = FileList['*.c'] cp cfiles, @cdir, :verbose => false @@ -601,25 +624,5 @@ class Rake::TestFileList < Test::Unit::TestCase assert File.exist?(File.join(@cdir, 'x.c')) end - def create_test_data - verbose(false) do - - mkdir "testdata" unless File.exist? "testdata" - mkdir "testdata/CVS" rescue nil - mkdir "testdata/.svn" rescue nil - @cdir = "testdata/cfiles" - mkdir @cdir rescue nil - touch "testdata/.dummy" - touch "testdata/x.bak" - touch "testdata/x~" - touch "testdata/core" - touch "testdata/x.c" - touch "testdata/xyz.c" - touch "testdata/abc.c" - touch "testdata/abc.h" - touch "testdata/abc.x" - touch "testdata/existing" - end - end - end + diff --git a/test/rake/test_rake_file_list_path_map.rb b/test/rake/test_rake_file_list_path_map.rb new file mode 100644 index 0000000000..5935dc2689 --- /dev/null +++ b/test/rake/test_rake_file_list_path_map.rb @@ -0,0 +1,8 @@ +require File.expand_path('../helper', __FILE__) + +class TestRakeFileListPathMap < Rake::TestCase + def test_file_list_supports_pathmap + assert_equal ['a', 'b'], FileList['dir/a.rb', 'dir/b.rb'].pathmap("%n") + end +end + diff --git a/test/rake/test_file_task.rb b/test/rake/test_rake_file_task.rb similarity index 52% rename from test/rake/test_file_task.rb rename to test/rake/test_rake_file_task.rb index 132bd88853..e586551237 100644 --- a/test/rake/test_file_task.rb +++ b/test/rake/test_rake_file_task.rb @@ -1,14 +1,12 @@ -require 'test/unit' +require File.expand_path('../helper', __FILE__) require 'fileutils' -require 'rake' -require_relative 'filecreation' -###################################################################### -class Rake::TestFileTask < Test::Unit::TestCase +class TestRakeFileTask < Rake::TestCase include Rake - include FileCreation def setup + super + Task.clear @runs = Array.new FileUtils.rm_f NEWFILE @@ -16,22 +14,26 @@ class Rake::TestFileTask < Test::Unit::TestCase end def test_file_need - name = "testdata/dummy" + name = "dummy" file name + ftask = Task[name] + assert_equal name.to_s, ftask.name File.delete(ftask.name) rescue nil + assert ftask.needed?, "file should be needed" + open(ftask.name, "w") { |f| f.puts "HI" } + assert_equal nil, ftask.prerequisites.collect{|n| Task[n].timestamp}.max assert ! ftask.needed?, "file should not be needed" + ensure File.delete(ftask.name) rescue nil end def test_file_times_new_depends_on_old - until File.exist?(OLDFILE) && File.exist?(NEWFILE) - create_timed_files(OLDFILE, NEWFILE) - end + create_timed_files(OLDFILE, NEWFILE) t1 = Rake.application.intern(FileTask, NEWFILE).enhance([OLDFILE]) t2 = Rake.application.intern(FileTask, OLDFILE) @@ -40,9 +42,7 @@ class Rake::TestFileTask < Test::Unit::TestCase end def test_file_times_old_depends_on_new - until File.exist?(OLDFILE) && File.exist?(NEWFILE) - create_timed_files(OLDFILE, NEWFILE) - end + create_timed_files(OLDFILE, NEWFILE) t1 = Rake.application.intern(FileTask,OLDFILE).enhance([NEWFILE]) t2 = Rake.application.intern(FileTask, NEWFILE) @@ -66,11 +66,19 @@ class Rake::TestFileTask < Test::Unit::TestCase end def test_existing_file_depends_on_non_existing_file + @ran = false + create_file(OLDFILE) delete_file(NEWFILE) - file NEWFILE + file NEWFILE do + @ran = true + end + file OLDFILE => NEWFILE - assert_nothing_raised do Task[OLDFILE].invoke end + + Task[OLDFILE].invoke + + assert @ran end # I have currently disabled this test. I'm not convinced that @@ -92,52 +100,3 @@ class Rake::TestFileTask < Test::Unit::TestCase end -###################################################################### -class Rake::TestDirectoryTask < Test::Unit::TestCase - include Rake - - def setup - rm_rf "testdata2", :verbose=>false - end - - def teardown - rm_rf "testdata2", :verbose=>false - end - - def test_directory - desc "DESC" - directory "testdata2/a/b/c" - assert_equal FileCreationTask, Task["testdata2"].class - assert_equal FileCreationTask, Task["testdata2/a"].class - assert_equal FileCreationTask, Task["testdata2/a/b/c"].class - assert_nil Task["testdata2"].comment - assert_equal "DESC", Task["testdata2/a/b/c"].comment - assert_nil Task["testdata2/a/b"].comment - verbose(false) { - Task['testdata2/a/b'].invoke - } - assert File.exist?("testdata2/a/b") - assert ! File.exist?("testdata2/a/b/c") - end - - if Rake::Win32.windows? - def test_directory_win32 - desc "WIN32 DESC" - FileUtils.mkdir_p("testdata2") - Dir.chdir("testdata2") do - directory 'c:/testdata2/a/b/c' - assert_equal FileCreationTask, Task['c:/testdata2'].class - assert_equal FileCreationTask, Task['c:/testdata2/a'].class - assert_equal FileCreationTask, Task['c:/testdata2/a/b/c'].class - assert_nil Task['c:/testdata2'].comment - assert_equal "WIN32 DESC", Task['c:/testdata2/a/b/c'].comment - assert_nil Task['c:/testdata2/a/b'].comment - verbose(false) { - Task['c:/testdata2/a/b'].invoke - } - assert File.exist?('c:/testdata2/a/b') - assert ! File.exist?('c:/testdata2/a/b/c') - end - end - end -end diff --git a/test/rake/test_fileutils.rb b/test/rake/test_rake_file_utils.rb similarity index 52% rename from test/rake/test_fileutils.rb rename to test/rake/test_rake_file_utils.rb index b6106c0978..fb7a4a5307 100644 --- a/test/rake/test_fileutils.rb +++ b/test/rake/test_rake_file_utils.rb @@ -1,65 +1,47 @@ -require 'rake' -require 'test/unit' -require_relative 'filecreation' +require File.expand_path('../helper', __FILE__) require 'fileutils' require 'stringio' -class Rake::TestFileUtils < Test::Unit::TestCase - include FileCreation - BASEDIR = File.dirname(__FILE__) - ShellCommand = "#{BASEDIR}/shellcommand.rb" - ENV_RUBY = ENV['RUBY'] - - def setup - if ruby = ENV_RUBY - @oldruby = FileUtils.class_eval {remove_const :RUBY} - FileUtils.class_eval {const_set(:RUBY, ruby)} - else - @oldruby = nil - end - end +class TestRakeFileUtils < Rake::TestCase def teardown - FileUtils.rm_rf("testdata") FileUtils::LN_SUPPORTED[0] = true - if @oldruby - ruby = @oldruby - FileUtils.class_eval {remove_const :RUBY} - FileUtils.class_eval {const_set(:RUBY, ruby)} - end + + super end def test_rm_one_file - create_file("testdata/a") - FileUtils.rm_rf "testdata/a" - assert ! File.exist?("testdata/a") + create_file("a") + FileUtils.rm_rf "a" + refute File.exist?("a") end def test_rm_two_files - create_file("testdata/a") - create_file("testdata/b") - FileUtils.rm_rf ["testdata/a", "testdata/b"] - assert ! File.exist?("testdata/a") - assert ! File.exist?("testdata/b") + create_file("a") + create_file("b") + FileUtils.rm_rf ["a", "b"] + refute File.exist?("a") + refute File.exist?("b") end def test_rm_filelist - list = Rake::FileList.new << "testdata/a" << "testdata/b" + list = Rake::FileList.new << "a" << "b" list.each { |fn| create_file(fn) } FileUtils.rm_r list - assert ! File.exist?("testdata/a") - assert ! File.exist?("testdata/b") + refute File.exist?("a") + refute File.exist?("b") end def test_ln - create_dir("testdata") - open("testdata/a", "w") { |f| f.puts "TEST_LN" } - RakeFileUtils.safe_ln("testdata/a", "testdata/b", :verbose => false) - assert_equal "TEST_LN\n", open("testdata/b") { |f| f.read } + open("a", "w") { |f| f.puts "TEST_LN" } + + Rake::FileUtilsExt.safe_ln("a", "b", :verbose => false) + + assert_equal "TEST_LN\n", File.read('b') end class BadLink - include RakeFileUtils + include Rake::FileUtilsExt attr_reader :cp_args def initialize(klass) @failure_class = klass @@ -92,7 +74,7 @@ class Rake::TestFileUtils < Test::Unit::TestCase def test_safe_ln_fails_on_script_error FileUtils::LN_SUPPORTED[0] = true c = BadLink.new(ScriptError) - assert_raise(ScriptError) do c.safe_ln "a", "b" end + assert_raises(ScriptError) do c.safe_ln "a", "b" end end def test_verbose @@ -118,21 +100,21 @@ class Rake::TestFileUtils < Test::Unit::TestCase end def test_file_utils_methods_are_available_at_top_level - create_file("testdata/a") - verbose(false) do - rm_rf "testdata/a" - end - assert ! File.exist?("testdata/a") + create_file("a") + rm_rf "a" + refute File.exist?("a") end def test_fileutils_methods_dont_leak obj = Object.new - assert_raise(NoMethodError) { obj.copy } # from FileUtils - assert_raise(NoMethodError) { obj.ruby } # from RubyFileUtils + assert_raises(NoMethodError) { obj.copy } # from FileUtils + assert_raises(NoMethodError) { obj.ruby "-v" } # from RubyFileUtils end def test_sh - verbose(false) { sh %{#{RUBY} #{ShellCommand}} } + shellcommand + + verbose(false) { sh %{#{FileUtils::RUBY} shellcommand.rb} } assert true, "should not fail" end @@ -148,37 +130,48 @@ class Rake::TestFileUtils < Test::Unit::TestCase def self.run(*args) new.run(*args) end + def self.ruby(*args) + Sh.run(RUBY, *args) + end end def test_sh_with_a_single_string_argument + check_expansion + ENV['RAKE_TEST_SH'] = 'someval' verbose(false) { - sh %{#{RUBY} #{BASEDIR}/check_expansion.rb #{env_var} someval} + sh %{#{FileUtils::RUBY} check_expansion.rb #{env_var} someval} } end def test_sh_with_multiple_arguments + check_no_expansion ENV['RAKE_TEST_SH'] = 'someval' + verbose(false) { - Sh.run RUBY, File.expand_path('../check_no_expansion.rb', __FILE__), env_var, 'someval' + Sh.ruby 'check_no_expansion.rb', env_var, 'someval' } end def test_sh_failure - assert_raise(RuntimeError) { - verbose(false) { Sh.run "#{RUBY} #{File.expand_path('../shellcommand.rb', __FILE__)} 1" } + shellcommand + + assert_raises(RuntimeError) { + verbose(false) { Sh.run %{#{FileUtils::RUBY} shellcommand.rb 1} } } end def test_sh_special_handling + shellcommand + count = 0 verbose(false) { - sh(%{#{RUBY} #{ShellCommand}}) do |ok, res| + sh(%{#{FileUtils::RUBY} shellcommand.rb}) do |ok, res| assert(ok) assert_equal 0, res.exitstatus count += 1 end - sh(%{#{RUBY} #{ShellCommand} 1}) do |ok, res| + sh(%{#{FileUtils::RUBY} shellcommand.rb 1}) do |ok, res| assert(!ok) assert_equal 1, res.exitstatus count += 1 @@ -188,75 +181,113 @@ class Rake::TestFileUtils < Test::Unit::TestCase end def test_sh_noop - verbose(false) { sh %{#{ShellCommand} 1}, :noop=>true } + shellcommand + + verbose(false) { sh %{shellcommand.rb 1}, :noop=>true } assert true, "should not fail" end def test_sh_bad_option - ex = assert_raise(ArgumentError) { - verbose(false) { sh %{#{ShellCommand}}, :bad_option=>true } + shellcommand + + ex = assert_raises(ArgumentError) { + verbose(false) { sh %{shellcommand.rb}, :bad_option=>true } } assert_match(/bad_option/, ex.message) end def test_sh_verbose - out = redirect_stderr { + shellcommand + + _, err = capture_io do verbose(true) { - sh %{#{ShellCommand}}, :noop=>true + sh %{shellcommand.rb}, :noop=>true } - } - assert_match(/^#{Regexp.quote(ShellCommand)}$/o, out) + end + + assert_equal "shellcommand.rb\n", err end def test_sh_no_verbose - out = redirect_stderr { + shellcommand + + _, err = capture_io do verbose(false) { - sh %{#{ShellCommand}}, :noop=>true + sh %{shellcommand.rb}, :noop=>true } - } - assert_equal '', out + end + + assert_equal '', err end def test_ruby_with_a_single_string_argument + check_expansion + ENV['RAKE_TEST_SH'] = 'someval' + verbose(false) { - ruby %{#{BASEDIR}/check_expansion.rb #{env_var} someval} + ruby %{check_expansion.rb #{env_var} someval} } end def test_ruby_with_multiple_arguments + check_no_expansion + ENV['RAKE_TEST_SH'] = 'someval' verbose(false) { - ruby "#{BASEDIR}/check_no_expansion.rb", env_var, 'someval' + ruby 'check_no_expansion.rb', env_var, 'someval' } end def test_split_all - assert_equal ['a'], RakeFileUtils.split_all('a') - assert_equal ['..'], RakeFileUtils.split_all('..') - assert_equal ['/'], RakeFileUtils.split_all('/') - assert_equal ['a', 'b'], RakeFileUtils.split_all('a/b') - assert_equal ['/', 'a', 'b'], RakeFileUtils.split_all('/a/b') - assert_equal ['..', 'a', 'b'], RakeFileUtils.split_all('../a/b') + assert_equal ['a'], Rake::FileUtilsExt.split_all('a') + assert_equal ['..'], Rake::FileUtilsExt.split_all('..') + assert_equal ['/'], Rake::FileUtilsExt.split_all('/') + assert_equal ['a', 'b'], Rake::FileUtilsExt.split_all('a/b') + assert_equal ['/', 'a', 'b'], Rake::FileUtilsExt.split_all('/a/b') + assert_equal ['..', 'a', 'b'], Rake::FileUtilsExt.split_all('../a/b') end - private - - def redirect_stderr - old_err = $stderr - $stderr = StringIO.new - yield - $stderr.string - ensure - $stderr = old_err + def command name, text + open name, 'w', 0750 do |io| + io << text + end end - def windows? - ! File::ALT_SEPARATOR.nil? + def check_no_expansion + command 'check_no_expansion.rb', <<-CHECK_EXPANSION +if ARGV[0] != ARGV[1] + exit 0 +else + exit 1 +end + CHECK_EXPANSION + end + + def check_expansion + command 'check_expansion.rb', <<-CHECK_EXPANSION +if ARGV[0] != ARGV[1] + exit 1 +else + exit 0 +end + CHECK_EXPANSION + end + + def shellcommand + command 'shellcommand.rb', <<-SHELLCOMMAND +#!/usr/bin/env ruby + +exit((ARGV[0] || "0").to_i) + SHELLCOMMAND end def env_var windows? ? '%RAKE_TEST_SH%' : '$RAKE_TEST_SH' end + def windows? + ! File::ALT_SEPARATOR.nil? + end + end diff --git a/test/rake/contrib/test_ftp.rb b/test/rake/test_rake_ftp_file.rb similarity index 87% rename from test/rake/contrib/test_ftp.rb rename to test/rake/test_rake_ftp_file.rb index 0346dd71ca..7f41faf0dd 100644 --- a/test/rake/contrib/test_ftp.rb +++ b/test/rake/test_rake_ftp_file.rb @@ -1,19 +1,22 @@ +require File.expand_path('../helper', __FILE__) require 'date' require 'time' -require 'test/unit' require 'rake/contrib/ftptools' -class Rake::TestFtpFile < Test::Unit::TestCase - class FakeDate - def self.today - Date.new(2003,10,3) - end - def self.now - Time.local(2003,10,3,12,00,00) - end +class FakeDate + def self.today + Date.new(2003,10,3) end + def self.now + Time.local(2003,10,3,12,00,00) + end +end + +class TestRakeFtpFile < Rake::TestCase def setup + super + Rake::FtpFile.class_eval { @date_class = FakeDate; @time_class = FakeDate } end @@ -53,3 +56,4 @@ class Rake::TestFtpFile < Test::Unit::TestCase assert !file.directory? end end + diff --git a/test/rake/test_rake_functional.rb b/test/rake/test_rake_functional.rb new file mode 100644 index 0000000000..e27d3cf68b --- /dev/null +++ b/test/rake/test_rake_functional.rb @@ -0,0 +1,514 @@ +begin + old_verbose = $VERBOSE + $VERBOSE = nil + require 'session' +rescue LoadError + if File::ALT_SEPARATOR + puts "Unable to run functional tests on MS Windows. Skipping." + else + puts "Unable to run functional tests -- please run \"gem install session\"" + end +ensure + $VERBOSE = old_verbose +end + +if defined?(Session) + if File::ALT_SEPARATOR + puts "Unable to run functional tests on MS Windows. Skipping." + end +end + +require File.expand_path('../helper', __FILE__) +require 'fileutils' + +# Version 2.1.9 of session has a bug where the @debug instance +# variable is not initialized, causing warning messages. This snippet +# of code fixes that problem. +module Session + class AbstractSession + alias old_initialize initialize + def initialize(*args) + @debug = nil + old_initialize(*args) + end + end +end if defined? Session + +class TestRakeFunctional < Rake::TestCase + + def setup + @rake_path = File.expand_path("bin/rake") + lib_path = File.expand_path("lib") + @ruby_options = ["-I#{lib_path}", "-I."] + @verbose = ENV['VERBOSE'] + + if @verbose + puts + puts + puts '-' * 80 + puts @__name__ + puts '-' * 80 + end + + super + end + + def test_rake_default + rakefile_default + + rake + + assert_match(/^DEFAULT$/, @out) + assert_status + end + + def test_rake_error_on_bad_task + rakefile_default + + rake "xyz" + + assert_match(/rake aborted/, @err) + assert_status(1) + end + + def test_env_available_at_top_scope + rakefile_default + + rake "TESTTOPSCOPE=1" + + assert_match(/^TOPSCOPE$/, @out) + assert_status + end + + def test_env_available_at_task_scope + rakefile_default + + rake "TESTTASKSCOPE=1 task_scope" + + assert_match(/^TASKSCOPE$/, @out) + assert_status + end + + def test_multi_desc + ENV['RAKE_COLUMNS'] = '80' + rakefile_multidesc + + rake "-T" + + assert_match %r{^rake a *# A / A2 *$}, @out + assert_match %r{^rake b *# B *$}, @out + refute_match %r{^rake c}, @out + assert_match %r{^rake d *# x{65}\.\.\.$}, @out + end + + def test_long_description + rakefile_multidesc + + rake "--describe" + + assert_match %r{^rake a\n *A / A2 *$}m, @out + assert_match %r{^rake b\n *B *$}m, @out + assert_match %r{^rake d\n *x{80}}m, @out + refute_match %r{^rake c\n}m, @out + end + + def test_proper_namespace_access + rakefile_access + + rake + + assert_not_match %r{^BAD:}, @out + end + + def test_rbext + rakefile_rbext + + rake "-N" + + assert_match %r{^OK$}, @out + end + + def test_system + rake_system_dir + + rake '-g', "sys1" + + assert_match %r{^SYS1}, @out + end + + def test_system_excludes_rakelib_files_too + rake_system_dir + + rake '-g', "sys1", '-T', 'extra' + + refute_match %r{extra:extra}, @out + end + + def test_by_default_rakelib_files_are_included + rake_system_dir + rakefile_extra + + rake '-T', 'extra', '--trace' + + assert_match %r{extra:extra}, @out + end + + def test_implicit_system + rake_system_dir + Dir.chdir @tempdir + + rake "sys1", "--trace" + + assert_match %r{^SYS1}, @out + end + + def test_no_system + rake_system_dir + rakefile_extra + + rake '-G', "sys1" + + assert_match %r{^Don't know how to build task}, @err # emacs wart: ' + end + + def test_nosearch_with_rakefile_uses_local_rakefile + rakefile_default + + rake "--nosearch" + + assert_match %r{^DEFAULT}, @out + end + + def test_nosearch_without_rakefile_finds_system + rakefile_nosearch + rake_system_dir + + rake "--nosearch", "sys1" + + assert_match %r{^SYS1}, @out + end + + def test_nosearch_without_rakefile_and_no_system_fails + rakefile_nosearch + ENV['RAKE_SYSTEM'] = 'not_exist' + + rake "--nosearch" + + assert_match %r{^No Rakefile found}, @err + end + + def test_invalid_command_line_options + rakefile_default + + rake "--bad-options" + + assert_match %r{invalid +option}i, @err + end + + def test_inline_verbose_default_should_show_command + rakefile_verbose + + rake "inline_verbose_default" + + assert_match(/ruby -e/, @err) + end + + def test_inline_verbose_true_should_show_command + rakefile_verbose + + rake "inline_verbose_true" + + assert_match(/ruby -e/, @err) + end + + def test_inline_verbose_false_should_not_show_command + rakefile_verbose + + rake "inline_verbose_false" + + refute_match(/ruby -e/, @err) + end + + def test_block_verbose_false_should_not_show_command + rakefile_verbose + + rake "block_verbose_false" + + refute_match(/ruby -e/, @err) + end + + def test_block_verbose_true_should_show_command + rakefile_verbose + + rake "block_verbose_true" + + assert_match(/ruby -e/, @err) + end + + def test_standalone_verbose_true_should_show_command + rakefile_verbose + + rake "standalone_verbose_true" + + assert_match(/ruby -e/, @err) + end + + def test_standalone_verbose_false_should_not_show_command + rakefile_verbose + + rake "standalone_verbose_false" + + refute_match(/ruby -e/, @err) + end + + def test_dry_run + rakefile_default + + rake "-n", "other" + + assert_match %r{Execute \(dry run\) default}, @err + assert_match %r{Execute \(dry run\) other}, @err + refute_match %r{DEFAULT}, @out + refute_match %r{OTHER}, @out + end + + # Test for the trace/dry_run bug found by Brian Chandler + def test_dry_run_bug + rakefile_dryrun + + rake + + FileUtils.rm_f 'temp_one' + + rake "--dry-run" + + refute_match(/No such file/, @out) + + assert_status + end + + # Test for the trace/dry_run bug found by Brian Chandler + def test_trace_bug + rakefile_dryrun + + rake + + FileUtils.rm_f 'temp_one' + + rake "--trace" + + refute_match(/No such file/, @out) + assert_status + end + + def test_imports + rakefile_imports + + rake + + assert File.exist?(File.join(@tempdir, 'dynamic_deps')), + "'dynamic_deps' file should exist" + assert_match(/^FIRST$\s+^DYNAMIC$\s+^STATIC$\s+^OTHER$/, @out) + assert_status + end + + def test_rules_chaining_to_file_task + rakefile_chains + + rake + + assert File.exist?(File.join(@tempdir, 'play.app')), + "'play.app' file should exist" + assert_status + end + + def test_file_creation_task + rakefile_file_creation + + rake "prep" + rake "run" + rake "run" + + assert(@err !~ /^cp src/, "Should not recopy data") + end + + def test_dash_f_with_no_arg_foils_rakefile_lookup + rakefile_rakelib + + rake "-I rakelib -rtest1 -f" + + assert_match(/^TEST1$/, @out) + end + + def test_dot_rake_files_can_be_loaded_with_dash_r + rakefile_rakelib + + rake "-I rakelib -rtest2 -f" + + assert_match(/^TEST2$/, @out) + end + + def test_can_invoke_task_in_toplevel_namespace + rakefile_namespace + + rake "copy" + + assert_match(/^COPY$/, @out) + end + + def test_can_invoke_task_in_nested_namespace + rakefile_namespace + + rake "nest:copy" + + assert_match(/^NEST COPY$/, @out) + end + + def test_tasks_can_reference_task_in_same_namespace + rakefile_namespace + + rake "nest:xx" + + assert_match(/^NEST COPY$/m, @out) + end + + def test_tasks_can_reference_task_in_other_namespaces + rakefile_namespace + + rake "b:run" + + assert_match(/^IN A\nIN B$/m, @out) + end + + def test_anonymous_tasks_can_be_invoked_indirectly + rakefile_namespace + + rake "anon" + + assert_match(/^ANON COPY$/m, @out) + end + + def test_rake_namespace_refers_to_toplevel + rakefile_namespace + + rake "very:nested:run" + + assert_match(/^COPY$/m, @out) + end + + def test_file_task_are_not_scoped_by_namespaces + rakefile_namespace + + rake "xyz.rb" + + assert_match(/^XYZ1\nXYZ2$/m, @out) + end + + def test_file_task_dependencies_scoped_by_namespaces + rakefile_namespace + + rake "scopedep.rb" + + assert_match(/^PREPARE\nSCOPEDEP$/m, @out) + end + + def test_rake_returns_status_error_values + rakefile_statusreturn + + rake "exit5" + + assert_status 5 + end + + def test_rake_returns_no_status_error_on_normal_exit + rakefile_statusreturn + + rake "normal" + + assert_status 0 + end + + def test_comment_before_task_acts_like_desc + rakefile_comments + + rake "-T" + + refute_match(/comment for t1/, @out) + end + + def test_comment_separated_from_task_by_blank_line_is_not_picked_up + rakefile_comments + + rake "-T" + + assert_not_match("t2", @out) + end + + def test_comment_after_desc_is_ignored + rakefile_comments + + rake "-T" + + assert_match("override comment for t3", @out) + end + + def test_comment_before_desc_is_ignored + rakefile_comments + + rake "-T" + + assert_match("override comment for t4", @out) + end + + def test_correct_number_of_tasks_reported + rakefile_comments + + rake "-T" + + assert_equal(2, @out.split(/\n/).grep(/t\d/).size) + end + + def test_file_list_is_requirable_separately + ruby "-rrake/file_list", "-e 'puts Rake::FileList[\"a\"].size'" + assert_equal "1\n", @out + assert_equal 0, @status + end + + private + + def assert_not_match(pattern, string, comment="'#{pattern}' was found (incorrectly) in '#{string}.inspect") + assert_nil Regexp.new(pattern).match(string), comment + end + + # Run a shell Ruby command with command line options (using the + # default test options). Output is captured in @out, @err and + # @status. + def ruby(*option_list) + run_ruby(@ruby_options + option_list) + end + + # Run a command line rake with the give rake options. Default + # command line ruby options are included. Output is captured in + # @out, @err and @status. + def rake(*rake_options) + run_ruby @ruby_options + [@rake_path] + rake_options + end + + # Low level ruby command runner ... + def run_ruby(option_list) + shell = Session::Shell.new + command = "#{Gem.ruby} #{option_list.join ' '}" + puts "COMMAND: [#{command}]" if @verbose + @out, @err = shell.execute command + @status = shell.exit_status + puts "STATUS: [#{@status}]" if @verbose + puts "OUTPUT: [#{@out}]" if @verbose + puts "ERROR: [#{@err}]" if @verbose + puts "PWD: [#{Dir.pwd}]" if @verbose + shell.close + end + + def assert_status(expected_status=0) + assert_equal expected_status, @status + end +end if defined?(Session) diff --git a/test/rake/test_rake_invocation_chain.rb b/test/rake/test_rake_invocation_chain.rb new file mode 100644 index 0000000000..1aab1eac81 --- /dev/null +++ b/test/rake/test_rake_invocation_chain.rb @@ -0,0 +1,52 @@ +require File.expand_path('../helper', __FILE__) + +class TestRakeInvocationChain < Rake::TestCase + + def setup + super + + @empty = Rake::InvocationChain::EMPTY + + @first_member = "A" + @second_member = "B" + @one = @empty.append(@first_member) + @two = @one.append(@second_member) + end + + def test_append + chain = @empty.append("A") + + assert_equal 'TOP => A', chain.to_s # HACK + end + + def test_append_one_circular + ex = assert_raises RuntimeError do + @one.append(@first_member) + end + assert_match(/circular +dependency/i, ex.message) + assert_match(/A.*=>.*A/, ex.message) + end + + def test_append_two_circular + ex = assert_raises RuntimeError do + @two.append(@first_member) + end + assert_match(/A.*=>.*B.*=>.*A/, ex.message) + end + + def test_member_eh_one + assert @one.member?(@first_member) + end + + def test_member_eh_two + assert @two.member?(@first_member) + assert @two.member?(@second_member) + end + + def test_to_s_empty + assert_equal "TOP", @empty.to_s + assert_equal "TOP => A", @one.to_s + end + +end + diff --git a/test/rake/test_makefile_loader.rb b/test/rake/test_rake_makefile_loader.rb similarity index 66% rename from test/rake/test_makefile_loader.rb rename to test/rake/test_rake_makefile_loader.rb index 15933b1f9b..bd3c99ba71 100644 --- a/test/rake/test_makefile_loader.rb +++ b/test/rake/test_rake_makefile_loader.rb @@ -1,14 +1,34 @@ -require 'test/unit' -require 'rake' +require File.expand_path('../helper', __FILE__) require 'rake/loaders/makefile' -class Rake::TestMakefileLoader < Test::Unit::TestCase +class TestRakeMakefileLoader < Rake::TestCase include Rake def test_parse + Dir.chdir @tempdir + + open 'sample.mf', 'w' do |io| + io << <<-'SAMPLE_MF' +# Comments +a: a1 a2 a3 a4 +b: b1 b2 b3 \ + b4 b5 b6\ +# Mid: Comment +b7 + + a : a5 a6 a7 +c: c1 +d: d1 d2 \ + +e f : e1 f1 + +g\ 0: g1 g\ 2 g\ 3 g4 + SAMPLE_MF + end + Task.clear loader = Rake::MakefileLoader.new - loader.load("#{File.dirname(__FILE__)}/data/sample.mf") + loader.load 'sample.mf' %w(a b c d).each do |t| assert Task.task_defined?(t), "#{t} should be a defined task" end diff --git a/test/rake/test_multitask.rb b/test/rake/test_rake_multi_task.rb similarity index 63% rename from test/rake/test_multitask.rb rename to test/rake/test_rake_multi_task.rb index 0413d03db3..cac235b9d3 100644 --- a/test/rake/test_multitask.rb +++ b/test/rake/test_rake_multi_task.rb @@ -1,18 +1,27 @@ -require 'test/unit' -require 'rake' +require File.expand_path('../helper', __FILE__) +require 'thread' -###################################################################### -class Rake::TestMultiTask < Test::Unit::TestCase +class TestRakeMultiTask < Rake::TestCase include Rake + include Rake::DSL def setup + super + Task.clear @runs = Array.new + @mutex = Mutex.new + end + + def add_run(obj) + @mutex.synchronize do + @runs << obj + end end def test_running_multitasks - task :a do 3.times do |i| @runs << "A#{i}"; sleep 0.01; end end - task :b do 3.times do |i| @runs << "B#{i}"; sleep 0.01; end end + task :a do 3.times do |i| add_run("A#{i}"); sleep 0.01; end end + task :b do 3.times do |i| add_run("B#{i}"); sleep 0.01; end end multitask :both => [:a, :b] Task[:both].invoke assert_equal 6, @runs.size @@ -23,9 +32,9 @@ class Rake::TestMultiTask < Test::Unit::TestCase end def test_all_multitasks_wait_on_slow_prerequisites - task :slow do 3.times do |i| @runs << "S#{i}"; sleep 0.05 end end - task :a => [:slow] do 3.times do |i| @runs << "A#{i}"; sleep 0.01 end end - task :b => [:slow] do 3.times do |i| @runs << "B#{i}"; sleep 0.01 end end + task :slow do 3.times do |i| add_run("S#{i}"); sleep 0.05 end end + task :a => [:slow] do 3.times do |i| add_run("A#{i}"); sleep 0.01 end end + task :b => [:slow] do 3.times do |i| add_run("B#{i}"); sleep 0.01 end end multitask :both => [:a, :b] Task[:both].invoke assert_equal 9, @runs.size @@ -40,4 +49,3 @@ class Rake::TestMultiTask < Test::Unit::TestCase end end - diff --git a/test/rake/test_namespace.rb b/test/rake/test_rake_name_space.rb similarity index 85% rename from test/rake/test_namespace.rb rename to test/rake/test_rake_name_space.rb index bd513b6a10..b1f2ed00b2 100644 --- a/test/rake/test_namespace.rb +++ b/test/rake/test_rake_name_space.rb @@ -1,7 +1,6 @@ -require 'test/unit' -require 'rake' +require File.expand_path('../helper', __FILE__) -class Rake::TestNameSpace < Test::Unit::TestCase +class TestRakeNameSpace < Rake::TestCase class TM include Rake::TaskManager @@ -10,7 +9,7 @@ class Rake::TestNameSpace < Test::Unit::TestCase def test_namespace_creation mgr = TM.new ns = Rake::NameSpace.new(mgr, []) - assert_not_nil ns + refute_nil ns end def test_namespace_lookup @@ -19,7 +18,7 @@ class Rake::TestNameSpace < Test::Unit::TestCase mgr.define_task(Rake::Task, "t") end - assert_not_nil ns["t"] + refute_nil ns["t"] assert_equal mgr["n:t"], ns["t"] end diff --git a/test/rake/test_rake_package_task.rb b/test/rake/test_rake_package_task.rb new file mode 100644 index 0000000000..87cb57c105 --- /dev/null +++ b/test/rake/test_rake_package_task.rb @@ -0,0 +1,79 @@ +require File.expand_path('../helper', __FILE__) +require 'rake/packagetask' + +class TestRakePackageTask < Rake::TestCase + + def test_initialize + touch 'install.rb' + touch 'a.c' + touch 'b.c' + mkdir 'CVS' + touch 'a.rb~' + + pkg = Rake::PackageTask.new("pkgr", "1.2.3") { |p| + p.package_files << "install.rb" + p.package_files.include '*.c' + p.package_files.exclude(/\bCVS\b/) + p.package_files.exclude(/~$/) + p.package_dir = 'pkg' + p.need_tar = true + p.need_tar_gz = true + p.need_tar_bz2 = true + p.need_zip = true + } + + assert_equal "pkg", pkg.package_dir + + assert_includes pkg.package_files, 'a.c' + + assert_equal 'pkgr', pkg.name + assert_equal '1.2.3', pkg.version + assert Rake::Task[:package] + assert Rake::Task['pkg/pkgr-1.2.3.tgz'] + assert Rake::Task['pkg/pkgr-1.2.3.tar.gz'] + assert Rake::Task['pkg/pkgr-1.2.3.tar.bz2'] + assert Rake::Task['pkg/pkgr-1.2.3.zip'] + assert Rake::Task['pkg/pkgr-1.2.3'] + assert Rake::Task[:clobber_package] + assert Rake::Task[:repackage] + end + + def test_initialize_no_version + e = assert_raises RuntimeError do + Rake::PackageTask.new 'pkgr' + end + + assert_equal 'Version required (or :noversion)', e.message + end + + def test_initialize_noversion + pkg = Rake::PackageTask.new 'pkgr', :noversion + + assert_equal 'pkg', pkg.package_dir + assert_equal 'pkgr', pkg.name + assert_equal nil, pkg.version + end + + def test_clone + pkg = Rake::PackageTask.new("x", :noversion) + p2 = pkg.clone + pkg.package_files << "y" + p2.package_files << "x" + assert_equal ["y"], pkg.package_files + assert_equal ["x"], p2.package_files + end + + def test_package_name + pkg = Rake::PackageTask.new 'a', '1' + + assert_equal 'a-1', pkg.package_name + end + + def test_package_name_noversion + pkg = Rake::PackageTask.new 'a', :noversion + + assert_equal 'a', pkg.package_name + end + +end + diff --git a/test/rake/test_pathmap.rb b/test/rake/test_rake_path_map.rb similarity index 75% rename from test/rake/test_pathmap.rb rename to test/rake/test_rake_path_map.rb index 2775b91039..32ffb854b1 100644 --- a/test/rake/test_pathmap.rb +++ b/test/rake/test_rake_path_map.rb @@ -1,8 +1,6 @@ -require 'test/unit' -require 'rake' +require File.expand_path('../helper', __FILE__) -# ==================================================================== -class Rake::TestPathMap < Test::Unit::TestCase +class TestRakePathMap < Rake::TestCase def test_returns_self_with_no_args assert_equal "abc.rb", "abc.rb".pathmap @@ -84,7 +82,7 @@ class Rake::TestPathMap < Test::Unit::TestCase end def test_undefined_percent_causes_error - ex = assert_raise(ArgumentError) { + assert_raises(ArgumentError) { "dir/abc.rb".pathmap("%z") } end @@ -128,7 +126,7 @@ class Rake::TestPathMap < Test::Unit::TestCase end def test_pattern_with_invalid_operator - ex = assert_raise(ArgumentError) do + ex = assert_raises(ArgumentError) do "abc.xyz".pathmap("%{src,bin}z") end assert_match(/unknown.*pathmap.*spec.*z/i, ex.message) @@ -157,51 +155,3 @@ class Rake::TestPathMap < Test::Unit::TestCase end end -class Rake::TestPathMapExplode < Test::Unit::TestCase - def setup - String.class_eval { public :pathmap_explode } - end - - def teardown - String.class_eval { protected :pathmap_explode } - end - - def test_explode - assert_equal ['a'], 'a'.pathmap_explode - assert_equal ['a', 'b'], 'a/b'.pathmap_explode - assert_equal ['a', 'b', 'c'], 'a/b/c'.pathmap_explode - assert_equal ['/', 'a'], '/a'.pathmap_explode - assert_equal ['/', 'a', 'b'], '/a/b'.pathmap_explode - assert_equal ['/', 'a', 'b', 'c'], '/a/b/c'.pathmap_explode - if File::ALT_SEPARATOR - assert_equal ['c:.', 'a'], 'c:a'.pathmap_explode - assert_equal ['c:.', 'a', 'b'], 'c:a/b'.pathmap_explode - assert_equal ['c:.', 'a', 'b', 'c'], 'c:a/b/c'.pathmap_explode - assert_equal ['c:/', 'a'], 'c:/a'.pathmap_explode - assert_equal ['c:/', 'a', 'b'], 'c:/a/b'.pathmap_explode - assert_equal ['c:/', 'a', 'b', 'c'], 'c:/a/b/c'.pathmap_explode - end - end -end - -class Rake::TestPathMapPartial < Test::Unit::TestCase - def test_pathmap_partial - @path = "1/2/file" - def @path.call(n) - pathmap_partial(n) - end - assert_equal("1", @path.call(1)) - assert_equal("1/2", @path.call(2)) - assert_equal("1/2", @path.call(3)) - assert_equal(".", @path.call(0)) - assert_equal("2", @path.call(-1)) - assert_equal("1/2", @path.call(-2)) - assert_equal("1/2", @path.call(-3)) - end -end - -class Rake::TestFileListPathMap < Test::Unit::TestCase - def test_file_list_supports_pathmap - assert_equal ['a', 'b'], FileList['dir/a.rb', 'dir/b.rb'].pathmap("%n") - end -end diff --git a/test/rake/test_rake_path_map_explode.rb b/test/rake/test_rake_path_map_explode.rb new file mode 100644 index 0000000000..a79235ee74 --- /dev/null +++ b/test/rake/test_rake_path_map_explode.rb @@ -0,0 +1,34 @@ +require File.expand_path('../helper', __FILE__) + +class TestRakePathMapExplode < Rake::TestCase + def setup + super + + String.class_eval { public :pathmap_explode } + end + + def teardown + String.class_eval { protected :pathmap_explode } + + super + end + + def test_explode + assert_equal ['a'], 'a'.pathmap_explode + assert_equal ['a', 'b'], 'a/b'.pathmap_explode + assert_equal ['a', 'b', 'c'], 'a/b/c'.pathmap_explode + assert_equal ['/', 'a'], '/a'.pathmap_explode + assert_equal ['/', 'a', 'b'], '/a/b'.pathmap_explode + assert_equal ['/', 'a', 'b', 'c'], '/a/b/c'.pathmap_explode + + if File::ALT_SEPARATOR + assert_equal ['c:.', 'a'], 'c:a'.pathmap_explode + assert_equal ['c:.', 'a', 'b'], 'c:a/b'.pathmap_explode + assert_equal ['c:.', 'a', 'b', 'c'], 'c:a/b/c'.pathmap_explode + assert_equal ['c:/', 'a'], 'c:/a'.pathmap_explode + assert_equal ['c:/', 'a', 'b'], 'c:/a/b'.pathmap_explode + assert_equal ['c:/', 'a', 'b', 'c'], 'c:/a/b/c'.pathmap_explode + end + end +end + diff --git a/test/rake/test_rake_path_map_partial.rb b/test/rake/test_rake_path_map_partial.rb new file mode 100644 index 0000000000..566e681bb1 --- /dev/null +++ b/test/rake/test_rake_path_map_partial.rb @@ -0,0 +1,18 @@ +require File.expand_path('../helper', __FILE__) + +class TestRakePathMapPartial < Rake::TestCase + def test_pathmap_partial + @path = "1/2/file" + def @path.call(n) + pathmap_partial(n) + end + assert_equal("1", @path.call(1)) + assert_equal("1/2", @path.call(2)) + assert_equal("1/2", @path.call(3)) + assert_equal(".", @path.call(0)) + assert_equal("2", @path.call(-1)) + assert_equal("1/2", @path.call(-2)) + assert_equal("1/2", @path.call(-3)) + end +end + diff --git a/test/rake/test_pseudo_status.rb b/test/rake/test_rake_pseudo_status.rb similarity index 68% rename from test/rake/test_pseudo_status.rb rename to test/rake/test_rake_pseudo_status.rb index 8bafd5f80a..51b3fef34e 100644 --- a/test/rake/test_pseudo_status.rb +++ b/test/rake/test_rake_pseudo_status.rb @@ -1,23 +1,21 @@ -require 'test/unit' -require 'rake' +require File.expand_path('../helper', __FILE__) -require_relative 'capture_stdout' - -class Rake::TestPseudoStatus < Test::Unit::TestCase +class TestRakePseudoStatus < Rake::TestCase def test_with_zero_exit_status s = Rake::PseudoStatus.new assert_equal 0, s.exitstatus assert_equal 0, s.to_i assert_equal 0, s >> 8 - assert ! s.stopped? + refute s.stopped? assert s.exited? end + def test_with_99_exit_status s = Rake::PseudoStatus.new(99) assert_equal 99, s.exitstatus assert_equal 25344, s.to_i assert_equal 99, s >> 8 - assert ! s.stopped? + refute s.stopped? assert s.exited? end end diff --git a/test/rake/test_rake_rdoc_task.rb b/test/rake/test_rake_rdoc_task.rb new file mode 100644 index 0000000000..0d24ef04a3 --- /dev/null +++ b/test/rake/test_rake_rdoc_task.rb @@ -0,0 +1,83 @@ +require File.expand_path('../helper', __FILE__) +begin + old_stderr = $stderr + dev_null = File.exist?('/dev/null') ? '/dev/null' : 'NUL' + $stderr = open dev_null, 'w' + require 'rake/rdoctask' +ensure + $stderr.close + $stderr = old_stderr +end + +class TestRakeRDocTask < Rake::TestCase + include Rake + + def setup + super + + Task.clear + end + + def test_tasks_creation + Rake::RDocTask.new + assert Task[:rdoc] + assert Task[:clobber_rdoc] + assert Task[:rerdoc] + end + + def test_tasks_creation_with_custom_name_symbol + rd = Rake::RDocTask.new(:rdoc_dev) + assert Task[:rdoc_dev] + assert Task[:clobber_rdoc_dev] + assert Task[:rerdoc_dev] + assert_equal :rdoc_dev, rd.name + end + + def test_tasks_creation_with_custom_name_string + rd = Rake::RDocTask.new("rdoc_dev") + assert Task[:rdoc_dev] + assert Task[:clobber_rdoc_dev] + assert Task[:rerdoc_dev] + assert_equal "rdoc_dev", rd.name + end + + def test_tasks_creation_with_custom_name_hash + options = { :rdoc => "rdoc", :clobber_rdoc => "rdoc:clean", :rerdoc => "rdoc:force" } + rd = Rake::RDocTask.new(options) + assert Task[:"rdoc"] + assert Task[:"rdoc:clean"] + assert Task[:"rdoc:force"] + assert_raises(RuntimeError) { Task[:clobber_rdoc] } + assert_equal options, rd.name + end + + def test_tasks_creation_with_custom_name_hash_will_use_default_if_an_option_isnt_given + Rake::RDocTask.new(:clobber_rdoc => "rdoc:clean") + assert Task[:rdoc] + assert Task[:"rdoc:clean"] + assert Task[:rerdoc] + end + + def test_tasks_creation_with_custom_name_hash_raises_exception_if_invalid_option_given + assert_raises(ArgumentError) do + Rake::RDocTask.new(:foo => "bar") + end + + begin + Rake::RDocTask.new(:foo => "bar") + rescue ArgumentError => e + assert_match(/foo/, e.message) + end + end + + def test_inline_source_option_is_only_appended_if_option_not_already_given + rd = Rake::RDocTask.new + rd.options << '--inline-source' + assert_equal 1, rd.option_list.grep('--inline-source').size + + rd = Rake::RDocTask.new + rd.options << '-S' + assert_equal 1, rd.option_list.grep('-S').size + assert_equal 0, rd.option_list.grep('--inline-source').size + end +end diff --git a/test/rake/test_rake_require.rb b/test/rake/test_rake_require.rb new file mode 100644 index 0000000000..d229edbc2b --- /dev/null +++ b/test/rake/test_rake_require.rb @@ -0,0 +1,40 @@ +require File.expand_path('../helper', __FILE__) + +class TestRakeRequire < Rake::TestCase + + def test_can_load_rake_library + rakefile_rakelib + app = Rake::Application.new + + assert app.instance_eval { + rake_require("test2", ['rakelib'], []) + } + end + + def test_wont_reload_rake_library + rakefile_rakelib + app = Rake::Application.new + + paths = ['rakelib'] + loaded_files = [] + app.rake_require("test2", paths, loaded_files) + + assert ! app.instance_eval { + rake_require("test2", paths, loaded_files) + } + end + + def test_throws_error_if_library_not_found + rakefile_rakelib + + app = Rake::Application.new + ex = assert_raises(LoadError) { + assert app.instance_eval { + rake_require("testx", ['rakelib'], []) + } + } + assert_match(/(can *not|can't)\s+find/i, ex.message) + assert_match(/testx/, ex.message) + end +end + diff --git a/test/rake/test_rules.rb b/test/rake/test_rake_rules.rb similarity index 66% rename from test/rake/test_rules.rb rename to test/rake/test_rake_rules.rb index 34df180ff4..3f6e352322 100644 --- a/test/rake/test_rules.rb +++ b/test/rake/test_rake_rules.rb @@ -1,36 +1,23 @@ -require 'test/unit' -require 'tmpdir' +require File.expand_path('../helper', __FILE__) require 'fileutils' -require 'rake' -require_relative 'filecreation' -###################################################################### -class Rake::TestRules < Test::Unit::TestCase +class TestRakeRules < Rake::TestCase include Rake - include FileCreation - SRCFILE = "testdata/abc.c" - SRCFILE2 = "testdata/xyz.c" - FTNFILE = "testdata/abc.f" - OBJFILE = "testdata/abc.o" - FOOFILE = "testdata/foo" - DOTFOOFILE = "testdata/.foo" + SRCFILE = "abc.c" + SRCFILE2 = "xyz.c" + FTNFILE = "abc.f" + OBJFILE = "abc.o" + FOOFILE = "foo" + DOTFOOFILE = ".foo" def setup - @oldpwd = Dir.pwd - @tmpdir = Dir.mktmpdir("rake") - Dir.chdir(@tmpdir) - Dir.mkdir("testdata") + super + Task.clear @runs = [] end - def teardown - FileList['testdata/{*,.[^.]*,}'].uniq.each do |f| rm_r(f, :verbose=>false) end - Dir.chdir(@oldpwd) - Dir.rmdir(@tmpdir) - end - def test_multiple_rules1 create_file(FTNFILE) delete_file(SRCFILE) @@ -100,81 +87,70 @@ class Rake::TestRules < Test::Unit::TestCase assert_equal [OBJFILE], @runs end - def test_file_names_beginning_with_dot_can_be_tricked_into_refering_to_file + def test_file_names_beginning_with_dot_can_be_tricked_into_referring_to_file verbose(false) do - chdir("testdata") do - create_file('.foo') - rule '.o' => "./.foo" do |t| - @runs << t.name - end - Task[OBJFILE].invoke - assert_equal [OBJFILE], @runs + create_file('.foo') + rule '.o' => "./.foo" do |t| + @runs << t.name end + Task[OBJFILE].invoke + assert_equal [OBJFILE], @runs end end def test_file_names_beginning_with_dot_can_be_wrapped_in_lambda verbose(false) do - chdir("testdata") do - create_file(".foo") - rule '.o' => lambda{".foo"} do |t| - @runs << "#{t.name} - #{t.source}" - end - Task[OBJFILE].invoke - assert_equal ["#{OBJFILE} - .foo"], @runs + + create_file(".foo") + rule '.o' => lambda{".foo"} do |t| + @runs << "#{t.name} - #{t.source}" end + Task[OBJFILE].invoke + assert_equal ["#{OBJFILE} - .foo"], @runs end end def test_file_names_containing_percent_can_be_wrapped_in_lambda verbose(false) do - chdir("testdata") do - create_file("foo%x") - rule '.o' => lambda{"foo%x"} do |t| - @runs << "#{t.name} - #{t.source}" - end - Task[OBJFILE].invoke - assert_equal ["#{OBJFILE} - foo%x"], @runs + create_file("foo%x") + rule '.o' => lambda{"foo%x"} do |t| + @runs << "#{t.name} - #{t.source}" end + Task[OBJFILE].invoke + assert_equal ["#{OBJFILE} - foo%x"], @runs end end def test_non_extension_rule_name_refers_to_file verbose(false) do - chdir("testdata") do - create_file("abc.c") - rule "abc" => '.c' do |t| - @runs << t.name - end - Task["abc"].invoke - assert_equal ["abc"], @runs + create_file("abc.c") + rule "abc" => '.c' do |t| + @runs << t.name end + Task["abc"].invoke + assert_equal ["abc"], @runs end end def test_pathmap_automatically_applies_to_name verbose(false) do - chdir("testdata") do - create_file("zzabc.c") - rule ".o" => 'zz%{x,a}n.c' do |t| - @runs << "#{t.name} - #{t.source}" - end - Task["xbc.o"].invoke - assert_equal ["xbc.o - zzabc.c"], @runs + create_file("zzabc.c") + rule ".o" => 'zz%{x,a}n.c' do |t| + @runs << "#{t.name} - #{t.source}" end + Task["xbc.o"].invoke + assert_equal ["xbc.o - zzabc.c"], @runs end end def test_plain_strings_are_just_filenames verbose(false) do - chdir("testdata") do - create_file("plainname") - rule ".o" => 'plainname' do |t| - @runs << "#{t.name} - #{t.source}" - end - Task["xbc.o"].invoke - assert_equal ["xbc.o - plainname"], @runs + create_file("plainname") + rule ".o" => 'plainname' do |t| + @runs << "#{t.name} - #{t.source}" end + Task["xbc.o"].invoke + assert_equal ["xbc.o - plainname"], @runs end end @@ -191,12 +167,12 @@ class Rake::TestRules < Test::Unit::TestCase end def test_close_matches_on_name_do_not_trigger_rule - create_file("testdata/x.c") + create_file("x.c") rule '.o' => ['.c'] do |t| @runs << t.name end - assert_raise(RuntimeError) { Task['testdata/x.obj'].invoke } - assert_raise(RuntimeError) { Task['testdata/x.xyo'].invoke } + assert_raises(RuntimeError) { Task['x.obj'].invoke } + assert_raises(RuntimeError) { Task['x.xyo'].invoke } end def test_rule_rebuilds_obj_when_source_is_newer @@ -281,27 +257,26 @@ class Rake::TestRules < Test::Unit::TestCase end def test_rule_with_proc_dependent_will_trigger - ran = false - mkdir_p("testdata/src/jw") - create_file("testdata/src/jw/X.java") + mkdir_p("src/jw") + create_file("src/jw/X.java") rule %r(classes/.*\.class) => [ - proc { |fn| fn.pathmap("%{classes,testdata/src}d/%n.java") } + proc { |fn| fn.pathmap("%{classes,src}d/%n.java") } ] do |task| assert_equal task.name, 'classes/jw/X.class' - assert_equal task.source, 'testdata/src/jw/X.java' + assert_equal task.source, 'src/jw/X.java' @runs << :RULE end Task['classes/jw/X.class'].invoke assert_equal [:RULE], @runs ensure - rm_r("testdata/src", :verbose=>false) rescue nil + rm_r("src", :verbose=>false) rescue nil end def test_proc_returning_lists_are_flattened_into_prereqs ran = false - mkdir_p("testdata/flatten") - create_file("testdata/flatten/a.txt") - task 'testdata/flatten/b.data' do |t| + mkdir_p("flatten") + create_file("flatten/a.txt") + task 'flatten/b.data' do |t| ran = true touch t.name, :verbose => false end @@ -309,43 +284,43 @@ class Rake::TestRules < Test::Unit::TestCase proc { |fn| [ fn.ext("txt"), - "testdata/flatten/b.data" + "flatten/b.data" ] } do |task| end - Task['testdata/flatten/a.html'].invoke + Task['flatten/a.html'].invoke assert ran, "Should have triggered flattened dependency" ensure - rm_r("testdata/flatten", :verbose=>false) rescue nil + rm_r("flatten", :verbose=>false) rescue nil end def test_recursive_rules_will_work_as_long_as_they_terminate actions = [] - create_file("testdata/abc.xml") + create_file("abc.xml") rule '.y' => '.xml' do actions << 'y' end rule '.c' => '.y' do actions << 'c'end rule '.o' => '.c' do actions << 'o'end rule '.exe' => '.o' do actions << 'exe'end - Task["testdata/abc.exe"].invoke + Task["abc.exe"].invoke assert_equal ['y', 'c', 'o', 'exe'], actions end def test_recursive_rules_that_dont_terminate_will_overflow - create_file("testdata/a.a") + create_file("a.a") prev = 'a' ('b'..'z').each do |letter| rule ".#{letter}" => ".#{prev}" do |t| puts "#{t.name}" end prev = letter end - ex = assert_raise(Rake::RuleRecursionOverflowError) { - Task["testdata/a.z"].invoke + ex = assert_raises(Rake::RuleRecursionOverflowError) { + Task["a.z"].invoke } - assert_match(/a\.z => testdata\/a.y/, ex.message) + assert_match(/a\.z => a.y/, ex.message) end def test_rules_with_bad_dependents_will_fail rule "a" => [ 1 ] do |t| puts t.name end - assert_raise(RuntimeError) do Task['a'].invoke end + assert_raises(RuntimeError) do Task['a'].invoke end end end diff --git a/test/rake/test_tasks.rb b/test/rake/test_rake_task.rb similarity index 53% rename from test/rake/test_tasks.rb rename to test/rake/test_rake_task.rb index 110ee26a9e..ebbcb2ac2d 100644 --- a/test/rake/test_tasks.rb +++ b/test/rake/test_rake_task.rb @@ -1,16 +1,20 @@ -require 'test/unit' +require File.expand_path('../helper', __FILE__) require 'fileutils' -require 'rake' -require_relative 'filecreation' -require_relative 'capture_stdout' -###################################################################### -class Rake::TestTasks < Test::Unit::TestCase - include CaptureStdout +class TestRakeTask < Rake::TestCase include Rake def setup + super + Task.clear + Rake::TaskManager.record_task_metadata = true + end + + def teardown + Rake::TaskManager.record_task_metadata = false + + super end def test_create @@ -23,10 +27,13 @@ class Rake::TestTasks < Test::Unit::TestCase assert_equal t, arg assert_nil t.source assert_equal [], t.sources + assert_equal 1, t.locations.size + assert_match(/#{Regexp.quote(__FILE__)}/, t.locations.first) end def test_inspect - t = task(:foo, :needs => [:bar, :baz]) +# t = task(:foo, :needs => [:bar, :baz]) + t = task(:foo => [:bar, :baz]) assert_equal " [bar, baz]>", t.inspect end @@ -46,7 +53,7 @@ class Rake::TestTasks < Test::Unit::TestCase t2 = task(:t2 => [:t1]) { |t| runlist << t.name } assert_equal ["t2"], t1.prerequisites assert_equal ["t1"], t2.prerequisites - ex = assert_raise RuntimeError do + ex = assert_raises RuntimeError do t1.invoke end assert_match(/circular dependency/i, ex.message) @@ -57,10 +64,10 @@ class Rake::TestTasks < Test::Unit::TestCase Rake.application.options.dryrun = true runlist = [] t1 = task(:t1) { |t| runlist << t.name; 3321 } - out = capture_stdout { t1.invoke } - assert_match(/execute .*t1/i, out) - assert_match(/dry run/i, out) - assert_no_match(/invoke/i, out) + _, err = capture_io { t1.invoke } + assert_match(/execute .*t1/i, err) + assert_match(/dry run/i, err) + refute_match(/invoke/i, err) assert_equal [], runlist ensure Rake.application.options.dryrun = false @@ -69,11 +76,11 @@ class Rake::TestTasks < Test::Unit::TestCase def test_tasks_can_be_traced Rake.application.options.trace = true t1 = task(:t1) - out = capture_stdout { + _, err = capture_io { t1.invoke } - assert_match(/invoke t1/i, out) - assert_match(/execute t1/i, out) + assert_match(/invoke t1/i, err) + assert_match(/execute t1/i, err) ensure Rake.application.options.trace = false end @@ -119,7 +126,7 @@ class Rake::TestTasks < Test::Unit::TestCase def test_find task :tfind assert_equal "tfind", Task[:tfind].name - ex = assert_raise(RuntimeError) { Task[:leaves] } + ex = assert_raises(RuntimeError) { Task[:leaves] } assert_equal "Don't know how to build task 'leaves'", ex.message end @@ -170,6 +177,56 @@ class Rake::TestTasks < Test::Unit::TestCase assert_equal ["b", "c"], Task[:a].prerequisites end + def test_prerequiste_tasks_returns_tasks_not_strings + a = task :a => ["b", "c"] + b = task :b + c = task :c + assert_equal [b, c], a.prerequisite_tasks + end + + def test_prerequiste_tasks_fails_if_prerequisites_are_undefined + a = task :a => ["b", "c"] + b = task :b + assert_raises(RuntimeError) do + a.prerequisite_tasks + end + end + + def test_prerequiste_tasks_honors_namespaces + a = b = nil + namespace "X" do + a = task :a => ["b", "c"] + b = task :b + end + c = task :c + + assert_equal [b, c], a.prerequisite_tasks + end + + def test_timestamp_returns_now_if_all_prereqs_have_no_times + a = task :a => ["b", "c"] + b = task :b + c = task :c + + faux_stamp = 100 + flexmock(Time, :now => faux_stamp) + + assert_equal faux_stamp, a.timestamp + end + + def test_timestamp_returns_latest_prereq_timestamp + a = task :a => ["b", "c"] + b = task :b + c = task :c + + faux_stamp = 100 + flexmock(Time, :now => faux_stamp-10) + flexmock(b, :timestamp => faux_stamp - 1) + flexmock(c, :timestamp => faux_stamp) + + assert_equal faux_stamp, a.timestamp + end + def test_investigation_output t1 = task(:t1 => [:t2, :t3]) { |t| runlist << t.name; 3321 } task(:t2) @@ -212,159 +269,3 @@ class Rake::TestTasks < Test::Unit::TestCase end end -###################################################################### -class Rake::TestTaskWithArguments < Test::Unit::TestCase - include CaptureStdout - include Rake - - def setup - Task.clear - end - - def test_no_args_given - t = task :t - assert_equal [], t.arg_names - end - - def test_args_given - t = task :t, :a, :b - assert_equal [:a, :b], t.arg_names - end - - def test_name_and_needs - t = task(:t => [:pre]) - assert_equal "t", t.name - assert_equal [], t.arg_names - assert_equal ["pre"], t.prerequisites - end - - def test_name_and_explicit_needs - t = task(:t, :needs => [:pre]) - assert_equal "t", t.name - assert_equal [], t.arg_names - assert_equal ["pre"], t.prerequisites - end - - def test_name_args_and_explicit_needs - t = task(:t, :x, :y, :needs => [:pre]) - assert_equal "t", t.name - assert_equal [:x, :y], t.arg_names - assert_equal ["pre"], t.prerequisites - end - - def test_illegal_keys_in_task_name_hash - assert_raise RuntimeError do - t = task(:t, :x, :y => 1, :needs => [:pre]) - end - end - - def test_arg_list_is_empty_if_no_args_given - t = task(:t) { |tt, args| assert_equal({}, args.to_hash) } - t.invoke(1, 2, 3) - end - - def test_tasks_can_access_arguments_as_hash - t = task :t, :a, :b, :c do |tt, args| - assert_equal({:a => 1, :b => 2, :c => 3}, args.to_hash) - assert_equal 1, args[:a] - assert_equal 2, args[:b] - assert_equal 3, args[:c] - assert_equal 1, args.a - assert_equal 2, args.b - assert_equal 3, args.c - end - t.invoke(1, 2, 3) - end - - def test_actions_of_various_arity_are_ok_with_args - notes = [] - t = task(:t, :x) do - notes << :a - end - t.enhance do | | - notes << :b - end - t.enhance do |task| - notes << :c - assert_kind_of Task, task - end - t.enhance do |t2, args| - notes << :d - assert_equal t, t2 - assert_equal({:x => 1}, args.to_hash) - end - assert_nothing_raised do t.invoke(1) end - assert_equal [:a, :b, :c, :d], notes - end - - def test_arguments_are_passed_to_block - t = task(:t, :a, :b) { |tt, args| - assert_equal( { :a => 1, :b => 2 }, args.to_hash ) - } - t.invoke(1, 2) - end - - def test_extra_parameters_are_ignored - ENV['b'] = nil - t = task(:t, :a) { |tt, args| - assert_equal 1, args.a - assert_nil args.b - } - t.invoke(1, 2) - end - - def test_arguments_are_passed_to_all_blocks - counter = 0 - t = task :t, :a - task :t do |tt, args| - assert_equal 1, args.a - counter += 1 - end - task :t do |tt, args| - assert_equal 1, args.a - counter += 1 - end - t.invoke(1) - assert_equal 2, counter - end - - def test_block_with_no_parameters_is_ok - t = task(:t) { } - t.invoke(1, 2) - end - - def test_name_with_args - desc "T" - t = task(:tt, :a, :b) - assert_equal "tt", t.name - assert_equal "T", t.comment - assert_equal "[a,b]", t.arg_description - assert_equal "tt[a,b]", t.name_with_args - assert_equal [:a, :b],t.arg_names - end - - def test_named_args_are_passed_to_prereqs - value = nil - pre = task(:pre, :rev) { |t, args| value = args.rev } - t = task(:t, :name, :rev, :needs => [:pre]) - t.invoke("bill", "1.2") - assert_equal "1.2", value - end - - def test_args_not_passed_if_no_prereq_names - pre = task(:pre) { |t, args| - assert_equal({}, args.to_hash) - assert_equal "bill", args.name - } - t = task(:t, :name, :rev, :needs => [:pre]) - t.invoke("bill", "1.2") - end - - def test_args_not_passed_if_no_arg_names - pre = task(:pre, :rev) { |t, args| - assert_equal({}, args.to_hash) - } - t = task(:t, :needs => [:pre]) - t.invoke("bill", "1.2") - end -end diff --git a/test/rake/test_rake_task_argument_parsing.rb b/test/rake/test_rake_task_argument_parsing.rb new file mode 100644 index 0000000000..7a03b2addc --- /dev/null +++ b/test/rake/test_rake_task_argument_parsing.rb @@ -0,0 +1,110 @@ +require File.expand_path('../helper', __FILE__) + +class TestRakeTaskArgumentParsing < Rake::TestCase + def setup + super + + @app = Rake::Application.new + end + + def test_name_only + name, args = @app.parse_task_string("name") + assert_equal "name", name + assert_equal [], args + end + + def test_empty_args + name, args = @app.parse_task_string("name[]") + assert_equal "name", name + assert_equal [], args + end + + def test_one_argument + name, args = @app.parse_task_string("name[one]") + assert_equal "name", name + assert_equal ["one"], args + end + + def test_two_arguments + name, args = @app.parse_task_string("name[one,two]") + assert_equal "name", name + assert_equal ["one", "two"], args + end + + def test_can_handle_spaces_between_args + name, args = @app.parse_task_string("name[one, two,\tthree , \tfour]") + assert_equal "name", name + assert_equal ["one", "two", "three", "four"], args + end + + def test_keeps_embedded_spaces + name, args = @app.parse_task_string("name[a one ana, two]") + assert_equal "name", name + assert_equal ["a one ana", "two"], args + end + + def test_terminal_width_using_env + app = Rake::Application.new + app.terminal_columns = 1234 + + assert_equal 1234, app.terminal_width + end + + def test_terminal_width_using_stty + app = Rake::Application.new + + flexmock(app, + :unix? => true, + :dynamic_width_stty => 1235, + :dynamic_width_tput => 0) + + assert_equal 1235, app.terminal_width + end + + def test_terminal_width_using_tput + app = Rake::Application.new + flexmock(app, + :unix? => true, + :dynamic_width_stty => 0, + :dynamic_width_tput => 1236) + + assert_equal 1236, app.terminal_width + end + + def test_terminal_width_using_hardcoded_80 + app = Rake::Application.new + flexmock(app, :unix? => false) + + assert_equal 80, app.terminal_width + end + + def test_terminal_width_with_failure + app = Rake::Application.new + flexmock(app).should_receive(:unix?).and_throw(RuntimeError) + + assert_equal 80, app.terminal_width + end + + def test_no_rakeopt + ARGV << '--trace' + app = Rake::Application.new + app.init + assert !app.options.silent + end + + def test_rakeopt_with_blank_options + ARGV << '--trace' + app = Rake::Application.new + app.init + assert !app.options.silent + end + + def test_rakeopt_with_silent_options + ENV['RAKEOPT'] = '-s' + app = Rake::Application.new + + app.init + + assert app.options.silent + end +end diff --git a/test/rake/test_task_arguments.rb b/test/rake/test_rake_task_arguments.rb similarity index 85% rename from test/rake/test_task_arguments.rb rename to test/rake/test_rake_task_arguments.rb index 9e907aed7d..7001a4aeb2 100644 --- a/test/rake/test_task_arguments.rb +++ b/test/rake/test_rake_task_arguments.rb @@ -1,16 +1,12 @@ -require 'test/unit' -require 'rake' +require File.expand_path('../helper', __FILE__) ###################################################################### -class Rake::TestTaskArguments < Test::Unit::TestCase - def setup - @backup = ENV.to_hash - ENV.clear - end - +class TestRakeTaskArguments < Rake::TestCase def teardown - ENV.clear - @backup.each {|k, v| ENV[k] = v } + ENV.delete('rev') + ENV.delete('VER') + + super end def test_empty_arg_list_is_empty @@ -53,12 +49,12 @@ class Rake::TestTaskArguments < Test::Unit::TestCase assert_nil ta.cc end - def test_args_can_reference_env_values + def test_args_do_not_reference_env_values ta = Rake::TaskArguments.new(["aa"], [1]) ENV['rev'] = "1.2" ENV['VER'] = "2.3" - assert_equal "1.2", ta.rev - assert_equal "2.3", ta.ver + assert_nil ta.rev + assert_nil ta.ver end def test_creating_new_argument_scopes @@ -84,7 +80,7 @@ class Rake::TestTaskArguments < Test::Unit::TestCase assert_equal 'original_val', ta[:bb] end - def test_default_arguements_that_dont_match_names_are_ignored + def test_default_arguments_that_dont_match_names_are_ignored ta = Rake::TaskArguments.new(["aa", "bb"], [nil, "original_val"]) ta.with_defaults({ "cc" => "default_val" }) assert_nil ta[:cc] diff --git a/test/rake/test_tasklib.rb b/test/rake/test_rake_task_lib.rb similarity index 57% rename from test/rake/test_tasklib.rb rename to test/rake/test_rake_task_lib.rb index c8b9cecdb2..9f3f7e9dae 100644 --- a/test/rake/test_tasklib.rb +++ b/test/rake/test_rake_task_lib.rb @@ -1,8 +1,7 @@ -require 'test/unit' +require File.expand_path('../helper', __FILE__) require 'rake/tasklib' - -class Rake::TestTaskLib < Test::Unit::TestCase +class TestRakeTaskLib < Rake::TestCase def test_paste tl = Rake::TaskLib.new assert_equal :ab, tl.paste(:a, :b) diff --git a/test/rake/test_task_manager.rb b/test/rake/test_rake_task_manager.rb similarity index 71% rename from test/rake/test_task_manager.rb rename to test/rake/test_rake_task_manager.rb index 3ab039ca85..847f784fa8 100644 --- a/test/rake/test_task_manager.rb +++ b/test/rake/test_rake_task_manager.rb @@ -1,28 +1,15 @@ -require 'test/unit' -require 'fileutils' -require 'tmpdir' -require 'rake' +require File.expand_path('../helper', __FILE__) -class Rake::TestTaskManager < Test::Unit::TestCase - class TaskManager - include Rake::TaskManager - end +class TestRakeTaskManager < Rake::TestCase def setup - @oldpwd = Dir.pwd - @tmpdir = Dir.mktmpdir("rake") - Dir.chdir(@tmpdir) - @tm = TaskManager.new - open("README", "wb") {} - end + super - def teardown - Dir.chdir(@oldpwd) - FileUtils.rm_rf(@tmpdir) + @tm = Rake::TestCase::TaskManager.new end def test_create_task_manager - assert_not_nil @tm + refute_nil @tm assert_equal [], @tm.tasks end @@ -59,6 +46,7 @@ class Rake::TestTaskManager < Test::Unit::TestCase t = @tm.define_task(Rake::FileTask, "fn") assert_equal "fn", t.name end + assert_equal ["fn"], @tm.tasks.collect { |t| t.name } end @@ -71,14 +59,17 @@ class Rake::TestTaskManager < Test::Unit::TestCase end def test_name_lookup_with_implicit_file_tasks - t = @tm["README"] - assert_equal "README", t.name + FileUtils.touch 'README.rdoc' + + t = @tm["README.rdoc"] + + assert_equal "README.rdoc", t.name assert Rake::FileTask === t end def test_name_lookup_with_nonexistent_task - assert_raise(RuntimeError) { - t = @tm["DOES NOT EXIST"] + assert_raises(RuntimeError) { + @tm["DOES NOT EXIST"] } end @@ -156,26 +147,3 @@ class Rake::TestTaskManager < Test::Unit::TestCase end -class Rake::TestTaskManagerArgumentResolution < Test::Unit::TestCase - TaskManager = Rake::TestTaskManager::TaskManager - - def test_good_arg_patterns - assert_equal [:t, [], []], task(:t) - assert_equal [:t, [], [:x]], task(:t => :x) - assert_equal [:t, [], [:x, :y]], task(:t => [:x, :y]) - - assert_equal [:t, [:a, :b], []], task(:t, :a, :b) - assert_equal [:t, [], [:x]], task(:t, :needs => :x) - assert_equal [:t, [:a, :b], [:x]], task(:t, :a, :b, :needs => :x) - assert_equal [:t, [:a, :b], [:x, :y]], task(:t, :a, :b, :needs => [:x, :y]) - - assert_equal [:t, [:a, :b], []], task(:t, [:a, :b]) - assert_equal [:t, [:a, :b], [:x]], task(:t, [:a, :b] => :x) - assert_equal [:t, [:a, :b], [:x, :y]], task(:t, [:a, :b] => [:x, :y]) - end - - def task(*args) - tm = TaskManager.new - tm.resolve_args(args) - end -end diff --git a/test/rake/test_rake_task_manager_argument_resolution.rb b/test/rake/test_rake_task_manager_argument_resolution.rb new file mode 100644 index 0000000000..35e0862ef7 --- /dev/null +++ b/test/rake/test_rake_task_manager_argument_resolution.rb @@ -0,0 +1,36 @@ +require File.expand_path('../helper', __FILE__) + +class TestRakeTaskManagerArgumentResolution < Rake::TestCase + + def setup + super + + Rake.application.options.ignore_deprecate = true + end + + def teardown + Rake.application.options.ignore_deprecate = false + + super + end + + def test_good_arg_patterns + assert_equal [:t, [], []], task(:t) + assert_equal [:t, [], [:x]], task(:t => :x) + assert_equal [:t, [], [:x, :y]], task(:t => [:x, :y]) + + assert_equal [:t, [:a, :b], []], task(:t, :a, :b) + assert_equal [:t, [], [:x]], task(:t, :needs => :x) + assert_equal [:t, [:a, :b], [:x]], task(:t, :a, :b, :needs => :x) + assert_equal [:t, [:a, :b], [:x, :y]], task(:t, :a, :b, :needs => [:x, :y]) + + assert_equal [:t, [:a, :b], []], task(:t, [:a, :b]) + assert_equal [:t, [:a, :b], [:x]], task(:t, [:a, :b] => :x) + assert_equal [:t, [:a, :b], [:x, :y]], task(:t, [:a, :b] => [:x, :y]) + end + + def task(*args) + tm = Rake::TestCase::TaskManager.new + tm.resolve_args(args) + end +end diff --git a/test/rake/test_rake_task_with_arguments.rb b/test/rake/test_rake_task_with_arguments.rb new file mode 100644 index 0000000000..bbbc82f99f --- /dev/null +++ b/test/rake/test_rake_task_with_arguments.rb @@ -0,0 +1,162 @@ +require File.expand_path('../helper', __FILE__) + +class TestRakeTaskWithArguments < Rake::TestCase + include Rake + + def setup + super + + Task.clear + Rake::TaskManager.record_task_metadata = true + end + + def teardown + Rake::TaskManager.record_task_metadata = false + + super + end + + def test_no_args_given + t = task :t + assert_equal [], t.arg_names + end + + def test_args_given + t = task :t, :a, :b + assert_equal [:a, :b], t.arg_names + end + + def test_name_and_needs + t = task(:t => [:pre]) + assert_equal "t", t.name + assert_equal [], t.arg_names + assert_equal ["pre"], t.prerequisites + end + + def test_name_args_and_explicit_needs + ignore_deprecations do + t = task(:t, :x, :y, :needs => [:pre]) + assert_equal "t", t.name + assert_equal [:x, :y], t.arg_names + assert_equal ["pre"], t.prerequisites + end + end + + def test_illegal_keys_in_task_name_hash + ignore_deprecations do + assert_raises RuntimeError do + t = task(:t, :x, :y => 1, :needs => [:pre]) + end + end + end + + def test_arg_list_is_empty_if_no_args_given + t = task(:t) { |tt, args| assert_equal({}, args.to_hash) } + t.invoke(1, 2, 3) + end + + def test_tasks_can_access_arguments_as_hash + t = task :t, :a, :b, :c do |tt, args| + assert_equal({:a => 1, :b => 2, :c => 3}, args.to_hash) + assert_equal 1, args[:a] + assert_equal 2, args[:b] + assert_equal 3, args[:c] + assert_equal 1, args.a + assert_equal 2, args.b + assert_equal 3, args.c + end + t.invoke(1, 2, 3) + end + + def test_actions_of_various_arity_are_ok_with_args + notes = [] + t = task(:t, :x) do + notes << :a + end + t.enhance do | | + notes << :b + end + t.enhance do |task| + notes << :c + assert_kind_of Task, task + end + t.enhance do |t2, args| + notes << :d + assert_equal t, t2 + assert_equal({:x => 1}, args.to_hash) + end + t.invoke(1) + assert_equal [:a, :b, :c, :d], notes + end + + def test_arguments_are_passed_to_block + t = task(:t, :a, :b) { |tt, args| + assert_equal( { :a => 1, :b => 2 }, args.to_hash ) + } + t.invoke(1, 2) + end + + def test_extra_parameters_are_ignored + t = task(:t, :a) { |tt, args| + assert_equal 1, args.a + assert_nil args.b + } + t.invoke(1, 2) + end + + def test_arguments_are_passed_to_all_blocks + counter = 0 + t = task :t, :a + task :t do |tt, args| + assert_equal 1, args.a + counter += 1 + end + task :t do |tt, args| + assert_equal 1, args.a + counter += 1 + end + t.invoke(1) + assert_equal 2, counter + end + + def test_block_with_no_parameters_is_ok + t = task(:t) { } + t.invoke(1, 2) + end + + def test_name_with_args + desc "T" + t = task(:tt, :a, :b) + assert_equal "tt", t.name + assert_equal "T", t.comment + assert_equal "[a,b]", t.arg_description + assert_equal "tt[a,b]", t.name_with_args + assert_equal [:a, :b],t.arg_names + end + + def test_named_args_are_passed_to_prereqs + value = nil + pre = task(:pre, :rev) { |t, args| value = args.rev } + t = task(:t, [:name, :rev] => [:pre]) + t.invoke("bill", "1.2") + assert_equal "1.2", value + end + + def test_args_not_passed_if_no_prereq_names + pre = task(:pre) { |t, args| + assert_equal({}, args.to_hash) + assert_equal "bill", args.name + } + t = task(:t, [:name, :rev] => [:pre]) + t.invoke("bill", "1.2") + end + + def test_args_not_passed_if_no_arg_names + pre = task(:pre, :rev) { |t, args| + assert_equal({}, args.to_hash) + } + t = task(:t => [:pre]) + t.invoke("bill", "1.2") + end +end + diff --git a/test/rake/test_rake_test_task.rb b/test/rake/test_rake_test_task.rb new file mode 100644 index 0000000000..81b4df3cd5 --- /dev/null +++ b/test/rake/test_rake_test_task.rb @@ -0,0 +1,116 @@ +require File.expand_path('../helper', __FILE__) +require 'rake/testtask' + +class TestRakeTestTask < Rake::TestCase + include Rake + + def setup + super + + Task.clear + ENV.delete('TEST') + end + + def test_no_task + assert ! Task.task_defined?(:test) + end + + def test_defaults + tt = Rake::TestTask.new do |t| end + refute_nil tt + assert_equal :test, tt.name + assert_equal ['lib'], tt.libs + assert_equal 'test/test*.rb', tt.pattern + assert_equal false, tt.verbose + assert Task.task_defined?(:test) + end + + def test_non_defaults + tt = Rake::TestTask.new(:example) do |t| + t.libs = ['src', 'ext'] + t.pattern = 'test/tc_*.rb' + t.verbose = true + end + refute_nil tt + assert_equal :example, tt.name + assert_equal ['src', 'ext'], tt.libs + assert_equal 'test/tc_*.rb', tt.pattern + assert_equal true, tt.verbose + assert Task.task_defined?(:example) + end + + def test_pattern + tt = Rake::TestTask.new do |t| + t.pattern = '*.rb' + end + assert_equal ['*.rb'], tt.file_list.to_a + end + + def test_env_test + ENV['TEST'] = 'testfile.rb' + tt = Rake::TestTask.new do |t| + t.pattern = '*' + end + assert_equal ["testfile.rb"], tt.file_list.to_a + end + + def test_test_files + tt = Rake::TestTask.new do |t| + t.test_files = FileList['a.rb', 'b.rb'] + end + assert_equal ["a.rb", 'b.rb'], tt.file_list.to_a + end + + def test_both_pattern_and_test_files + tt = Rake::TestTask.new do |t| + t.test_files = FileList['a.rb', 'b.rb'] + t.pattern = '*.rb' + end + assert_equal ['a.rb', 'b.rb', '*.rb'], tt.file_list.to_a + end + + def test_direct_run_has_quoted_paths + test_task = Rake::TestTask.new(:tx) do |t| + t.loader = :direct + end + assert_match(/-e ".*"/, test_task.run_code) + end + + def test_testrb_run_has_quoted_paths_on_ruby_182 + test_task = Rake::TestTask.new(:tx) do |t| + t.loader = :testrb + end + flexmock(test_task).should_receive(:ruby_version).and_return('1.8.2') + assert_match(/^-S testrb +".*"$/, test_task.run_code) + end + + def test_testrb_run_has_quoted_paths_on_ruby_186 + test_task = Rake::TestTask.new(:tx) do |t| + t.loader = :testrb + end + flexmock(test_task).should_receive(:ruby_version).and_return('1.8.6') + assert_match(/^-S testrb +$/, test_task.run_code) + end + + def test_rake_run_has_quoted_paths + test_task = Rake::TestTask.new(:tx) do |t| + t.loader = :rake + end + assert_match(/".*"/, test_task.run_code) + end + + def test_nested_libs_will_be_flattened + test_task = Rake::TestTask.new(:tx) do |t| + t.libs << ["A", "B"] + end + sep = File::PATH_SEPARATOR + assert_match(/lib#{sep}A#{sep}B/, test_task.ruby_opts_string) + end + + def test_empty_lib_path_implies_no_dash_I_option + test_task = Rake::TestTask.new(:tx) do |t| + t.libs = [] + end + refute_match(/-I/, test_task.ruby_opts_string) + end +end diff --git a/test/rake/test_rake_top_level_functions.rb b/test/rake/test_rake_top_level_functions.rb new file mode 100644 index 0000000000..69ef8691fb --- /dev/null +++ b/test/rake/test_rake_top_level_functions.rb @@ -0,0 +1,76 @@ +require File.expand_path('../helper', __FILE__) + +class TestRakeTopLevelFunctions < Rake::TestCase + + def setup + super + + @app = Rake.application + Rake.application = flexmock("app") + Rake.application.should_receive(:deprecate). + and_return { |old, new, call| @app.deprecate(old, new, call) } + end + + def teardown + Rake.application = @app + + super + end + + def test_namespace + Rake.application.should_receive(:in_namespace).with("xyz", any).once + namespace "xyz" do end + end + + def test_import + Rake.application.should_receive(:add_import).with("x").once.ordered + Rake.application.should_receive(:add_import).with("y").once.ordered + Rake.application.should_receive(:add_import).with("z").once.ordered + import('x', 'y', 'z') + end + + def test_when_writing + out, = capture_io do + when_writing("NOTWRITING") do + puts "WRITING" + end + end + assert_equal "WRITING\n", out + end + + def test_when_not_writing + Rake::FileUtilsExt.nowrite_flag = true + _, err = capture_io do + when_writing("NOTWRITING") do + puts "WRITING" + end + end + assert_equal "DRYRUN: NOTWRITING\n", err + ensure + Rake::FileUtilsExt.nowrite_flag = false + end + + def test_missing_constants_task + Rake.application.should_receive(:const_warning).with(:Task).once + Object.const_missing(:Task) + end + + def test_missing_constants_file_task + Rake.application.should_receive(:const_warning).with(:FileTask).once + Object.const_missing(:FileTask) + end + + def test_missing_constants_file_creation_task + Rake.application.should_receive(:const_warning).with(:FileCreationTask).once + Object.const_missing(:FileCreationTask) + end + + def test_missing_constants_rake_app + Rake.application.should_receive(:const_warning).with(:RakeApp).once + Object.const_missing(:RakeApp) + end + + def test_missing_other_constant + assert_raises(NameError) do Object.const_missing(:Xyz) end + end +end diff --git a/test/rake/test_rake_win32.rb b/test/rake/test_rake_win32.rb new file mode 100644 index 0000000000..fc2746a0a1 --- /dev/null +++ b/test/rake/test_rake_win32.rb @@ -0,0 +1,72 @@ +require File.expand_path('../helper', __FILE__) + +class TestRakeWin32 < Rake::TestCase + + Win32 = Rake::Win32 + + def test_win32_system_dir_uses_home_if_defined + ENV['HOME'] = 'C:\\HP' + + assert_equal "C:/HP/Rake", Win32.win32_system_dir + end + + def test_win32_system_dir_uses_homedrive_homepath_when_no_home_defined + ENV['HOME'] = nil + ENV['HOMEDRIVE'] = 'C:' + ENV['HOMEPATH'] = '\\HP' + + assert_equal "C:/HP/Rake", Win32.win32_system_dir + end + + def test_win32_system_dir_uses_appdata_when_no_home_or_home_combo + ENV['APPDATA'] = "C:\\Documents and Settings\\HP\\Application Data" + ENV['HOME'] = nil + ENV['HOMEDRIVE'] = nil + ENV['HOMEPATH'] = nil + + assert_equal "C:/Documents and Settings/HP/Application Data/Rake", + Win32.win32_system_dir + end + + def test_win32_system_dir_fallback_to_userprofile_otherwise + ENV['HOME'] = nil + ENV['HOMEDRIVE'] = nil + ENV['HOMEPATH'] = nil + ENV['APPDATA'] = nil + ENV['USERPROFILE'] = "C:\\Documents and Settings\\HP" + + assert_equal "C:/Documents and Settings/HP/Rake", Win32.win32_system_dir + end + + def test_win32_system_dir_nil_of_no_env_vars + ENV['APPDATA'] = nil + ENV['HOME'] = nil + ENV['HOMEDRIVE'] = nil + ENV['HOMEPATH'] = nil + ENV['RAKE_SYSTEM'] = nil + ENV['USERPROFILE'] = nil + + assert_raises(Rake::Win32::Win32HomeError) do + Win32.win32_system_dir + end + end + + def test_win32_backtrace_with_different_case + ex = nil + begin + raise 'test exception' + rescue => ex + end + + ex.set_backtrace ['abc', 'rakefile'] + + rake = Rake::Application.new + rake.options.trace = true + rake.instance_variable_set(:@rakefile, 'Rakefile') + + _, err = capture_io { rake.display_error_message(ex) } + + assert_match(/rakefile/, err) + end + +end diff --git a/test/rake/test_require.rb b/test/rake/test_require.rb deleted file mode 100644 index 8e6e2e9d5b..0000000000 --- a/test/rake/test_require.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'test/unit' -require 'rake' - -# ==================================================================== -class Rake::TestRequire < Test::Unit::TestCase - RakeLibDir = File.dirname(__FILE__) + '/data/rakelib' - - def test_can_load_rake_library - app = Rake::Application.new - assert app.instance_eval { - rake_require("test1", [RakeLibDir], []) - } - end - - def test_wont_reload_rake_library - app = Rake::Application.new - assert ! app.instance_eval { - rake_require("test2", [RakeLibDir], ['test2']) - } - end - - def test_throws_error_if_library_not_found - app = Rake::Application.new - ex = assert_raise(LoadError) { - assert app.instance_eval { - rake_require("testx", [RakeLibDir], []) - } - } - assert_match(/x/, ex.message) - end -end - diff --git a/test/rake/test_sys.rb b/test/rake/test_sys.rb new file mode 100644 index 0000000000..21f7e2c708 --- /dev/null +++ b/test/rake/test_sys.rb @@ -0,0 +1,20 @@ +require File.expand_path('../helper', __FILE__) +begin + old_verbose = $VERBOSE + $VERBOSE = nil + require 'rake/contrib/sys' +ensure + $VERBOSE = old_verbose +end + +class TestSys < Rake::TestCase + + def test_split_all + assert_equal ['a'], Sys.split_all('a') + assert_equal ['..'], Sys.split_all('..') + assert_equal ['/'], Sys.split_all('/') + assert_equal ['a', 'b'], Sys.split_all('a/b') + assert_equal ['/', 'a', 'b'], Sys.split_all('/a/b') + assert_equal ['..', 'a', 'b'], Sys.split_all('../a/b') + end +end diff --git a/test/rake/test_test_task.rb b/test/rake/test_test_task.rb deleted file mode 100644 index 0b7dbc625b..0000000000 --- a/test/rake/test_test_task.rb +++ /dev/null @@ -1,81 +0,0 @@ -require 'tmpdir' -require 'test/unit' -require 'rake/testtask' - -class Rake::TestTestTask < Test::Unit::TestCase - include Rake - - def setup - @oldwd = Dir.pwd - @tmpwd = Dir.mktmpdir - Dir.chdir(@tmpwd) - Task.clear - ENV.delete('TEST') - open('install.rb', 'w') {} - end - - def teardown - FileUtils.rm_rf("testdata") - Dir.chdir(@oldwd) - FileUtils.rm_rf(@tmpwd) - end - - def test_no_task - assert ! Task.task_defined?(:test) - end - - def test_defaults - tt = Rake::TestTask.new do |t| end - assert_not_nil tt - assert_equal :test, tt.name - assert_equal ['lib'], tt.libs - assert_equal 'test/test*.rb', tt.pattern - assert_equal false, tt.verbose - assert Task.task_defined?(:test) - end - - def test_non_defaults - tt = Rake::TestTask.new(:example) do |t| - t.libs = ['src', 'ext'] - t.pattern = 'test/tc_*.rb' - t.verbose = true - end - assert_not_nil tt - assert_equal :example, tt.name - assert_equal ['src', 'ext'], tt.libs - assert_equal 'test/tc_*.rb', tt.pattern - assert_equal true, tt.verbose - assert Task.task_defined?(:example) - end - - def test_pattern - tt = Rake::TestTask.new do |t| - t.pattern = '*.rb' - end - assert_equal ['install.rb'], tt.file_list.to_a - end - - def test_env_test - ENV['TEST'] = 'testfile.rb' - tt = Rake::TestTask.new do |t| - t.pattern = '*' - end - assert_equal ["testfile.rb"], tt.file_list.to_a - end - - def test_test_files - tt = Rake::TestTask.new do |t| - t.test_files = FileList['a.rb', 'b.rb'] - end - assert_equal ["a.rb", 'b.rb'], tt.file_list.to_a - end - - def test_both_pattern_and_test_files - tt = Rake::TestTask.new do |t| - t.test_files = FileList['a.rb', 'b.rb'] - t.pattern = '*.rb' - end - assert_equal ['a.rb', 'b.rb', 'install.rb'], tt.file_list.to_a - end - -end diff --git a/test/rake/test_top_level_functions.rb b/test/rake/test_top_level_functions.rb deleted file mode 100644 index 12a8cd1e36..0000000000 --- a/test/rake/test_top_level_functions.rb +++ /dev/null @@ -1,91 +0,0 @@ -require 'test/unit' -require_relative 'capture_stdout' -require 'rake' - -class Rake::TestTopLevelFunctions < Test::Unit::TestCase - include CaptureStdout - - def setup - super - @app = Rake.application - Rake.application = @mock = Object.new - end - - def teardown - Rake.application = @app - super - end - - def defmock(sym, &block) - class << @mock; self; end.class_eval do - define_method(sym, block) - end - end - - def test_namespace - args = [] - defmock(:in_namespace) {|a, *| args << a} - namespace "xyz" do end - assert_equal(["xyz"], args) - end - - def test_import - args = [] - defmock(:add_import) {|a| args << a} - import('x', 'y', 'z') - assert_equal(['x', 'y', 'z'], args) - end - - def test_when_writing - out = capture_stdout do - when_writing("NOTWRITING") do - puts "WRITING" - end - end - assert_equal "WRITING\n", out - end - - def test_when_not_writing - RakeFileUtils.nowrite_flag = true - out = capture_stdout do - when_writing("NOTWRITING") do - puts "WRITING" - end - end - assert_equal "DRYRUN: NOTWRITING\n", out - ensure - RakeFileUtils.nowrite_flag = false - end - - def test_missing_constants_task - args = [] - defmock(:const_warning) {|a| args << a} - Object.const_missing(:Task) - assert_equal([:Task], args) - end - - def test_missing_constants_file_task - args = [] - defmock(:const_warning) {|a| args << a} - Object.const_missing(:FileTask) - assert_equal([:FileTask], args) - end - - def test_missing_constants_file_creation_task - args = [] - defmock(:const_warning) {|a| args << a} - Object.const_missing(:FileCreationTask) - assert_equal([:FileCreationTask], args) - end - - def test_missing_constants_rake_app - args = [] - defmock(:const_warning) {|a| args << a} - Object.const_missing(:RakeApp) - assert_equal([:RakeApp], args) - end - - def test_missing_other_constant - assert_raise(NameError) do Object.const_missing(:Xyz) end - end -end diff --git a/test/rake/test_win32.rb b/test/rake/test_win32.rb deleted file mode 100644 index dade540629..0000000000 --- a/test/rake/test_win32.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'test/unit' -require_relative 'in_environment' - -require 'rake' - -class Rake::TestWin32 < Test::Unit::TestCase - include InEnvironment - - Win32 = Rake::Win32 - - def test_win32_system_dir_uses_home_if_defined - in_environment('RAKE_SYSTEM' => nil, - 'HOME' => "C:\\HP", - 'APPDATA' => nil - ) do - assert_equal "C:/HP/Rake", Win32.win32_system_dir - end - end - - def test_win32_system_dir_uses_appdata_if_defined - in_environment( - 'RAKE_SYSTEM' => nil, - 'HOME' => "C:\\HP", - 'APPDATA' => "C:\\Documents and Settings\\HP\\Application Data" - ) do - assert_equal "C:/Documents and Settings/HP/Application Data/Rake", Win32.win32_system_dir - end - end - - def test_win32_system_dir_nil_of_no_env_vars - in_environment( - 'RAKE_SYSTEM' => nil, - 'HOME' => nil, - 'HOMEDRIVE' => nil, - "HOMEPATH" => nil, - 'APPDATA' => nil, - 'USERPROFILE' => "C:\\Documents and Settings\\HP" - ) do - assert_raise(ArgumentError) do - Win32.win32_system_dir - end - end - end - -end if Rake::Win32.windows?