lib/rake.rb in drake-0.8.4.1.1.0 vs lib/rake.rb in drake-0.8.4.1.2.0

- old
+ new

@@ -27,19 +27,18 @@ # # 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.4.1.1.0' +RAKEVERSION = '0.8.4.1.2.0' require 'rbconfig' require 'fileutils' require 'singleton' require 'monitor' require 'optparse' require 'ostruct' -require 'rake/parallel' require 'rake/win32' $trace = false @@ -406,17 +405,17 @@ #################################################################### # InvocationChain tracks the chain of task invocations to detect # circular dependencies. class InvocationChain + attr_reader :value # :nodoc: + def initialize(value, tail) @value = value @tail = tail end - attr_reader :value # :nodoc: - def member?(obj) @value == obj || @tail.member?(obj) end def append(value) @@ -578,95 +577,66 @@ def clear_actions actions.clear self end - def base_invoke(*args) #:nodoc: + def invoke_serial(*args) # :nodoc: task_args = TaskArguments.new(arg_names, args) invoke_with_call_chain(task_args, InvocationChain::EMPTY) end # Invoke the task if it is needed. Prerequites are invoked first. def invoke(*args) - if application.num_threads == 1 - base_invoke(*args) + if application.options.threads == 1 + invoke_serial(*args) else - if application.parallel.lock.locked? - raise "Calling Task#invoke within a task is not allowed." - end - application.parallel.lock.synchronize { - application.parallel.tasks.clear - application.parallel.needed.clear - base_invoke(*args) - application.invoke_parallel(self.name) - } + invoke_parallel(*args) end 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 and application.num_threads == 1 - puts "** Invoke #{name} #{format_trace_flags}" - end - return if @already_invoked - @already_invoked = true - - if application.options.randomize - @prerequisites = @prerequisites.sort_by { rand } - end - - if application.num_threads == 1 + if application.options.threads == 1 + @lock.synchronize do + return unless prepare_invoke invoke_prerequisites(task_args, new_chain) execute(task_args) if needed? - else - # - # Parallel mode -- gather tasks for batch execution. - # - # Either the task knows it's needed or we've marked it as - # needed. - # - # Why do we manually mark tasks as needed? Since this is a - # dry run, files are not created or modified. Therefore the - # 'needed?' result does not propagate through the recursion. - # - prereqs = invoke_prerequisites_parallel(task_args, new_chain) - if needed? or application.parallel.needed[self] - application.parallel.tasks[name] = [task_args, prereqs] - unless invocation_chain == InvocationChain::EMPTY - application.parallel.needed[invocation_chain.value] = true - end - end end + else + return unless prepare_invoke + invoke_with_call_chain_collector(task_args, new_chain, invocation_chain) end end protected :invoke_with_call_chain - def invoke_prerequisite(prereq_name, task_args, invocation_chain) #:nodoc: - prereq = application[prereq_name, @scope] - prereq_args = task_args.new_scope(prereq.arg_names) - prereq.invoke_with_call_chain(prereq_args, invocation_chain) - prereq + def prepare_invoke # :nodoc: + if application.options.randomize + @prerequisites = @prerequisites.sort_by { rand } + end + if application.options.trace + puts "** Invoke #{name} #{format_trace_flags}" + end + return if @already_invoked + @already_invoked = true end # Invoke all the prerequisites of a task. def invoke_prerequisites(task_args, invocation_chain) # :nodoc: @prerequisites.each { |n| invoke_prerequisite(n, task_args, invocation_chain) } end - # Parallel dry-run accumulator. - # This also serves to circumvent MultiTask#invoke_prerequisites. - def invoke_prerequisites_parallel(task_args, invocation_chain) #:nodoc: - @prerequisites.map { |n| - invoke_prerequisite(n, task_args, invocation_chain) - } + def invoke_prerequisite(prereq_name, task_args, invocation_chain) #:nodoc: + prereq = application[prereq_name, @scope] + prereq_args = task_args.new_scope(prereq.arg_names) + prereq.invoke_with_call_chain(prereq_args, invocation_chain) + prereq end - + # Format the trace flags for display. def format_trace_flags flags = [] flags << "first_time" unless @already_invoked flags << "not_needed" unless needed? @@ -1007,25 +977,10 @@ fns.each do |fn| Rake.application.add_import(fn) end end -# -# +seq+ : Force tasks to be executed sequentially. -# -def seq - Rake::SEQ_LAMBDA -end -module Rake - SEQ_LAMBDA = lambda { |*task_names| - (1...task_names.size).each { |n| - task task_names[n] => task_names[n - 1] - } - task_names.last - } -end - # ########################################################################### # This a FileUtils extension that defines several additional commands to be # added to the FileUtils utility functions. # module FileUtils @@ -1752,26 +1707,16 @@ module TaskManager # Track the last comment made in the Rakefile. attr_accessor :last_description alias :last_comment :last_description # Backwards compatibility - attr_accessor :num_threads - attr_reader :parallel - def initialize super @tasks = Hash.new @rules = Array.new @scope = Array.new @last_description = nil - - @num_threads = 1 - - @parallel = Struct.new(:tasks, :needed, :lock).new - @parallel.tasks = Hash.new - @parallel.needed = Hash.new - @parallel.lock = Mutex.new end def create_rule(*args, &block) pattern, arg_names, deps = resolve_args(args) pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern @@ -2022,10 +1967,30 @@ }.flatten end end # TaskManager + # + # Lazily pull in the parallelizing code + # + class Options < OpenStruct # :nodoc: + attr_reader :threads + + def initialize + super + @threads = 1 + end + + def threads=(n) + if n > 1 and require('rake/parallel') + Task.module_eval { include Parallel::TaskMixin } + Application.module_eval { include Parallel::ApplicationMixin } + end + @threads = n + end + end + ###################################################################### # Rake main application object. When invoking +rake+ from the # command line, a Rake::Application object is created and run. # class Application @@ -2116,11 +2081,11 @@ @loaders[ext] = loader end # Application options from the command line def options - @options ||= OpenStruct.new + @options ||= Options.new end # private ---------------------------------------------------------------- def invoke_task(task_string) @@ -2304,22 +2269,13 @@ ], ['--execute-continue', '-E CODE', "Execute some Ruby code, then continue with normal task processing.", lambda { |value| eval(value) } ], - ['--threads', '-j N', "Specifies the number of threads to run simultaneously.", - lambda { |value| self.num_threads = value.to_i } + ['--threads', '-j N', "Run up to N independent tasks simultaneously in separate threads.", + lambda { |value| options.threads = value.to_i } ], - ['--randomize[=SEED]', "Randomize task prerequisite orders", - lambda { |value| - MultiTask.class_eval { remove_method(:invoke_prerequisites) } - options.randomize = true - if value - srand(value.hash) - end - } - ], ['--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 } @@ -2335,9 +2291,16 @@ } ], ['--rakelibdir', '--rakelib', '-R RAKELIBDIR', "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')", lambda { |value| options.rakelib = value.split(':') } + ], + ['--randomize[=SEED]', "Randomize the order of sibling prerequisites.", + lambda { |value| + options.randomize = true + MultiTask.class_eval { remove_method(:invoke_prerequisites) } + srand(value.hash) if value + } ], ['--require', '-r MODULE', "Require MODULE before executing rakefile.", lambda { |value| begin require value