Skip to content

Commit

Permalink
Merge pull request #252 from grzuy/fix_multitask_failures
Browse files Browse the repository at this point in the history
[Fixes #189] [Fixes #190] Don't run task if it depends on already invoked but failed task
  • Loading branch information
hsbt committed Mar 19, 2018
2 parents 55bee9a + edb7743 commit a8ebea2
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 48 deletions.
38 changes: 1 addition & 37 deletions lib/rake/multi_task.rb
Expand Up @@ -5,46 +5,10 @@ module Rake
# parallel using Ruby threads.
#
class MultiTask < Task

# Same as invoke, but explicitly pass a call chain to detect
# circular dependencies. This is largely copied from Rake::Task
# but has been updated such that if multiple tasks depend on this
# one in parallel, they will all fail if the first execution of
# this task fails.
def invoke_with_call_chain(task_args, invocation_chain)
new_chain = Rake::InvocationChain.append(self, invocation_chain)
@lock.synchronize do
begin
if @already_invoked
if @invocation_exception
if application.options.trace
application.trace "** Previous invocation of #{name} failed #{format_trace_flags}"
end
raise @invocation_exception
else
return
end
end

if application.options.trace
application.trace "** Invoke #{name} #{format_trace_flags}"
end
@already_invoked = true

invoke_prerequisites(task_args, new_chain)
execute(task_args) if needed?
rescue Exception => ex
add_chain_to(ex, new_chain)
@invocation_exception = ex
raise
end
end
end

private

def invoke_prerequisites(task_args, invocation_chain) # :nodoc:
invoke_prerequisites_concurrently(task_args, invocation_chain)
end
end

end
42 changes: 31 additions & 11 deletions lib/rake/task.rb
Expand Up @@ -103,6 +103,7 @@ def initialize(task_name, app)
@scope = app.current_scope
@arg_names = nil
@locations = []
@invocation_exception = nil
end

# Enhance a task with prerequisites or actions. Returns self.
Expand Down Expand Up @@ -183,20 +184,39 @@ def invoke(*args)

# 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)
#
# If multiple tasks depend on this
# one in parallel, they will all fail if the first execution of
# this task fails.
def invoke_with_call_chain(task_args, invocation_chain)
new_chain = Rake::InvocationChain.append(self, invocation_chain)
@lock.synchronize do
if application.options.trace
application.trace "** Invoke #{name} #{format_trace_flags}"
begin
if application.options.trace
application.trace "** Invoke #{name} #{format_trace_flags}"
end

if @already_invoked
if @invocation_exception
if application.options.trace
application.trace "** Previous invocation of #{name} failed #{format_trace_flags}"
end
raise @invocation_exception
else
return
end
end

@already_invoked = true

invoke_prerequisites(task_args, new_chain)
execute(task_args) if needed?
rescue Exception => ex
add_chain_to(ex, new_chain)
@invocation_exception = ex
raise ex
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

Expand Down
21 changes: 21 additions & 0 deletions test/test_rake_multi_task.rb
Expand Up @@ -82,4 +82,25 @@ def test_cross_thread_prerequisite_failures
Rake::Task[:b].invoke
end
end

def test_task_not_executed_if_dependant_task_failed_concurrently
multitask :default => [:one, :two]

task :one do
raise
end

task_two_was_executed = false
task :two => :one do
task_two_was_executed = true
end

begin
Rake::Task[:default].invoke
rescue RuntimeError
ensure
sleep 0.5
refute task_two_was_executed
end
end
end

0 comments on commit a8ebea2

Please sign in to comment.