A dependency is something that HAS to be run before something else. If it does not have to be run, it should allow the task to run anyway.
Task("First")
.WithCriteria(() => shouldDoStuff)
.Does(() =>
{
});
Task("Second")
.RunsAfter("First")
.Does(() =>
{
});
Task("Third")
.IsDependentOn("Second")
.Does(() =>
{
});
In the example before, the task Second will be run regardless if shouldDoStuff was true or not, Third is dependent on Second though, so if Second did not run, an exception will be thrown.
Renaming IsDependentOn to RunsAfter would be nice, but not sure if it would be OK to break backwards compatibility like this. All scripts would still run, but there would be a semantic change to task execution.
Maybe keep IsDependentOn and call the other dependency registration Requires or something like that. Not sure what to do here...
I've been burned a few times by having a dependent task run even though the "parent" task's WithCriteria clause was false.
For example:
``` c#
var runIntegrationTests = Argument
Task("Default")
.IsDependentOn("Build")
.IsDependentOn("Run Unit Tests")
.IsDependentOn("Run Integration Tests")
;
Task("Run Integration Tests")
.WithCriteria(runIntegrationTests)
.IsDependentOn("Provision VM")
.IsDependentOn("Execute Integration Tests")
.IsDependentOn("Tear Down VM")
;
// other tasks ...
RunTarget("Default");
```
Since runIntegrationTests is false, I would not expect any of "Provision VM", "Execute Integration Tests", nor "Tear Down VM" to be executed.
I'm not saying this solves the problem, but what is the reason for evaluating the WithCriteria clause _after_ executing dependent tasks rather than before? Or does evaluating WithCriteria before introduce a similarly-large problem for other use cases?
@dbertram Well, in your example you're only telling Cake that Run Integration Tests should run after Provision VM, Execute Integration Tests and Tear Down VM. The criteria for that task should not influence the other ones since they know nothing about this.
You could however argue that something that relies on Run Integration Tests shouldn't be executed if Run Integration Tests's criteria evaluates to false. We could introduce something like WithCascadingCriteria that would solve that problem.
I think my initial understanding/expectation of how IsDependentOninteracts with WithCriteria is/was just wrong.
The docs say the following about WithCriteria:
You can control and influence the flow of the build script execution by providing criteria. This is a predicate that has to be fulfilled for the task to execute. The criteria does not affect however succeeding task will be executed.
Given that description, it wasn't clear to me that criteria and dependencies are basically two completely separate features.
My personal thoughts would be to do one of the following:
IsDependentOn isn't going to change, update the docs to clarify how criteria and dependencies interact first/separately from the existing example that illustrates definition-time vs. execution-time criteria evaluation.IsDependentOn works, if it's really meant to define the dependency graph, it feels like WithCriteria should cascade by default (imho) (i.e., if Task A has criteria and also depends on Task B, but Task A does not run due to its criteria, then Task B should not run either).If the above causes too many other problems, yes, something like WithCascadingCriteria would help, or as you've suggested in https://github.com/cake-build/cake/issues/29#issuecomment-125054641, possibly rename the existing IsDependentOn to something more like RunsAfter and make IsDependentOn work as outlined above to provide different registration options depending what you're trying to do.
A .RunsAfter() method would make sense even if the semantics of .IsDependentOn() doesn't change. Consider the following:
``` c#
Task("Clean")
.Does(() =>
{
// remove all outputs
});
Task("EnsureOutputDirExists")
.Does(() =>
{
// create a custom artifacts directory
}
Task("Build")
.IsDependentOn("EnsureOutputDirExists")
.Does(() =>
{
// puts some stuff in the custom artifacts directory
}
Task("Clean-Build")
.IsDependentOn("Clean")
.IsDependentOn("Build");
If I now run `Clean-Build`, there's no guarantee that `Clean` will be run before `EnsureOutputDirExists`. If not, `Build` will fail because the output directory doesn't exist (it was created, but then `Clean` removed it again...). If I could define
Task("EnsureOutputDirExists")
.RunsAfter("Clean")
.Does(() =>
{
// create a custom artifacts directory
}
```
I could make sure that _if_ both Clean and EnsureOutputDirExists are to be run, they will be correctly ordered, while still not _requiring_ a Clean before EnsureOutputDirExists.
Most helpful comment
I've been burned a few times by having a dependent task run even though the "parent" task's
WithCriteriaclause was false.For example:
``` c#("runIntegrationTests", false);
var runIntegrationTests = Argument
Task("Default")
.IsDependentOn("Build")
.IsDependentOn("Run Unit Tests")
.IsDependentOn("Run Integration Tests")
;
Task("Run Integration Tests")
.WithCriteria(runIntegrationTests)
.IsDependentOn("Provision VM")
.IsDependentOn("Execute Integration Tests")
.IsDependentOn("Tear Down VM")
;
// other tasks ...
RunTarget("Default");
```
Since
runIntegrationTestsis false, I would not expect any of "Provision VM", "Execute Integration Tests", nor "Tear Down VM" to be executed.I'm not saying this solves the problem, but what is the reason for evaluating the
WithCriteriaclause _after_ executing dependent tasks rather than before? Or does evaluatingWithCriteriabefore introduce a similarly-large problem for other use cases?