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


Reply via email to