cake
cake copied to clipboard
When running multiple targets, common dependent tasks are executed twice
UPDATED to provide a simple repro
Prerequisites
- [X] I have written a descriptive issue title
- [X] I have searched issues to ensure it has not already been reported
Cake runner
Cake .NET Tool
Cake version
4.0.0
Operating system
Windows
Operating system architecture
64-Bit
What are you seeing?
When running tests for the NUnit console runner with arguments --target=Test --target=Package the Build target is run twice, once before Test and once before Package. Build is, of course, a dependency of both targets.
I would expect the dependent target to run only once
NOTE: This appears to be related to #4066.
Steps to Reproduce
Use the following script:
var targets = Arguments<string>("target", "A");
Task("A")
.Does(() => Information("Running A"));
Task("B")
.IsDependentOn("A")
.Does(() => Information("Running B"));
Task("C")
.IsDependentOn("A")
.Does(() => Information("Running C"));
RunTargets(targets);
Run using arguments --target=B --target=C
This produces the following output:
========================================
A
========================================
Running A
========================================
B
========================================
Running B
========================================
A
========================================
Running A
========================================
C
========================================
Running C
Task Duration
--------------------------------------------------
A 00:00:00.0060125
B 00:00:00.0003280
A 00:00:00.0001377
C 00:00:00.0001879
--------------------------------------------------
Total: 00:00:00.0066661
@devlead I modified a version of the above to use similar code to what you proposed in 2022 as a workaround. That code, as expected, runs A B and C in sequence.
Here's the updated script:
var targets = Arguments<string>("target", "A");
Task("A")
.Does(() => Information("Running A"));
Task("B")
.IsDependentOn("A")
.Does(() => Information("Running B"));
Task("C")
.IsDependentOn("A")
.Does(() => Information("Running C"));
RunTargets(targets); // This fails, running A twice
//MyRunTargets(targets); // This succeeds, running A only once
CakeReport MyRunTargets(ICollection<string> targets)
=> RunTarget(GetOrAddTargetsTask(targets).Name);
Task<CakeReport> RunTargetsAsync(ICollection<string> targets)
=> RunTargetAsync(GetOrAddTargetsTask(targets).Name);
private ICakeTaskInfo GetOrAddTargetsTask(ICollection<string> targets)
{
var targetsTaskName = string.Join(',', targets);
var targetsTask = Tasks.FirstOrDefault(
task => task.Name == targetsTaskName
);
if (targetsTask == null)
{
var task = Task(targetsTaskName);
foreach(var target in targets)
{
task.IsDependentOn(target);
}
targetsTask = task.Task;
}
return targetsTask;
}