On Sep 12, 2008, at 12:28 AM, Adam Murdoch wrote:
Hans Dockter wrote:
The question is how to fit this into a directed acyclic graph.
My rough idea is to create dependsOn relations from the
postRequisite tasks to the source task. If the build is started
with: 'gradle sourceTask' we need to translate this into an
execution of the postRequisite tasks. If a task has
postRequisite its name is rather an alias for the postRequisite
tasks.
I think all of the above stuff boils down to 3 primitive
dependencies between tasks:
1) taskA should execute if taskB successfully executes
2) taskA should execute if taskB executes and fails
3) taskA should execute if taskB executes at all, regardless of
success or failure
Building the DAG would involve converting from TaskDependency
objects to the above primitives (somehow).
May be I'm missing something. But I think the domain is more
complicated then that (leaving aside conditional tasks).
I didn't explain this too well. It feels to me like there are 2
levels of dependency relationships here. The first level is made up
of high-level, domain specific dependencies. Dependencies such as,
taskA depends on all Report tasks. Or taskB must be executed if
taskA fails. Or 'clean' and all its dependencies must be executed
before 'assemble' and all its dependencies. And so on.
These would be represented as TaskDependency implementations
attached to Tasks.
First of all, I'm definitely in favor of a TaskDependency object. And
I also share the notion of high level domain specific dependencies
and low level DAG dependencies.
Domain wise I see another two categories. There are lifecycle
dependencies and execution dependencies. For example the jar task has
a lifecycle dependency on the test task. The jar task does not care
if the test task is skipped. The jar task uses the dependency only to
establish its point of execution in the build. The test task on the
other hand has an execution dependency on testCompile. I know that
this view is a bit fuzzy and has a couple of border cases. Right now
we can only skip a particular task. I think there are use case where
you want to skip a subset of the graph if a certain task is skipped.
Right now we simulate this by adding for example the skip.test
property as a skip property to testResources. But this is not a very
nice solution.
The second level is made up of a small number of types of low
level, general purpose dependencies. Namely the 3 I described earlier.
I'm still not sure if I understand the way you describe the low level
dependencies.
You say: taskA should execute if taskB executes
This means:
gradle taskB, executes first taskB then taskA
gradle taskA, executes only taskA
Which is different to the dependsOn relation we use right now (and is
used this way by almost every other build tool out there).
There would be represented as edges in the DAG.
The process of assembling the DAG would involve evaluating each of
the appropriate TaskDependency instances and adding edges to the
DAG. Build execution would then proceed as it currently does,
traversing the edges, except it would traverse some edges
conditionally.
I don't think the way we currently execute the DAG will be enough for
what we want to achieve. I've tried to explain this is my earlier
email. Before I get into this again, I would like to get on the same
page with you regarding the low level dependencies (see above).
Let's look at test, testCompile and testReport. How would the
relationship between test and testCompile be modeled with the
above? If I execute testCompile, I don't want test to be executed.
gradle testCompile // should execute only testCompile
gradle test // should execute testCompile before and testReport after
gradle testReport // should execute test before.
Should it? Or should it just generate the report for whatever
results are already there?
In this case you are right. It was just an example. You might replace
testReport with some task that really depends on test. It's obviously
a trivial example and my question was how to implement this with low
level dependencies as described above.
Here's another use for a finally task in a build I have: We have a
number of ci builds, each of which builds an expensive artifact and
uploads it. Currently we have a bunch of artificial tasks to make
sure that upload executes after whichever assemble tasks are being
run, but that upload does not depend on any of the assemble tasks.
Finally tasks would simplify this, as the upload task is a finally
task for each of the assemble tasks. However, if I run the upload
task, I don't want any of the assemble tasks to run, I simply want
to upload whatever's already been assembled.
This is an interesting use case.
I guess this discussion demonstrates the usefulness of having
TaskDependency objects. If we choose one behaviour, and I don't
like it, then I can write my own TaskDependency implementation to
do what I like.
Yes. Another improvement for making the impossible possible ;)
- Hans
--
Hans Dockter
Gradle Project lead
http://www.gradle.org
---------------------------------------------------------------------
To unsubscribe from this list, please visit:
http://xircles.codehaus.org/manage_email