Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fixes #189] [Fixes #190] Don't run task if it depends on already invoked but failed task #252

Merged
merged 2 commits into from Mar 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -83,4 +83,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