Hans Dockter wrote:
Hi,

one thing I find inconvenient with Gradle multi-project builds at the moment, is that dependent projects are rebuild although it would not be necessary.

Let's look at an example:

root
- commons
- webservice (has a compile dependency on commons)

Let's look at some use cases

1.)
webservice> gradle compile

current:
does compile, test and uploadLibsInternal on commons
does compile on webservice

should do: if commons hasn't been touched an existing jar of commons should be used. If commons has been touched or no jar does exists, the commons-jar should be build, but without being tested.


Part of the problem here is that uploadLibsInternal does not really depend on test, but we add this as an artificial dependency to overlay a lifecycle over the tasks. Perhaps separating real dependencies (artifact dependencies?) from lifecycle would help here. Then, Gradle could consider real dependencies in other projects and ignore lifecycle dependencies.

2.)
webservice> gradle test

current: like above except that webservice is tested
should do: If commons hasn't been touched an existing jar of commons should be used. If commons has been touched or no jar does exists, the commons-jar should be build with being tested.


Should commons be tested?

I think this is the same question as to whether commons should be cleaned or not by 'gradle clean'. Sometimes yes and sometime no. Sometimes I want the task to be run for the current project and all the projects it depends on. And sometimes I want the task to be run only for the current project. Running 'gradle test' isn't really expressive enough - it doesn't declare my intentions.

One solution is to select one behaviour as the default, and provide some way to override this from the command-line. A few options:
- Use command-line options to express this, such as: gradle --recursive test
- Use synthetic task names to express this, such as: gradle recursive-test, where Gradle generates the 'recursive-test' task from the 'test' task. This could be implemented by a plugin.
- Use task name pattern matching, such as gradle *:test

------------------------------------

I'm wondering if we can solve this by working on our 'skipping' design.

I think so. This feels like just another way of deciding whether a task should be skipped or not.

A good step in this direction would be to extract the skipping/enabling stuff out of AbstractTask and into some kind of ExecutionStrategy object. The responsibility of an ExecutionStategy for a Task would be to decide whether the task should be executed or not. Or possibly to actually execute the task. Another possible responsibility for ExecutionStrategy could be to decide whether the task should be included in the DAG or not.

We could define monitors for our projects. The Java plugin could add a HaveBinariesChanged monitor.

I think this belongs on the domain objects that represent artifacts, not the project. That is, a task - or Gradle on its behalf - should be able to ask: have the input artifacts for this task changed since this task was last run? It is the responsibility of Configuration (or better, a sub-interface of FileCollection) to answer this question.

How it answers the question, well, it may be up to a plugin to provide an implementation that does anything other than return a hardcoded 'yes they have changed'

Tasks could associate themselves with those monitors. For example the HaveBinariesChanged monitor would be used by the test and archive tasks (of course the archive tasks would use it in conjunction with checking if archives exists).

I think a better model is one where tasks associate themselves with groups of artifacts, ie FileCollection (or Configuration). A FileCollection would have associated with it some meta-info about the artifacts, things like: - which tasks produce these artifacts, so that we can add them as dependencies of the consuming task - which artifacts are included and how to fetch them, so that we can use them in the task - have the artifacts changed since the consuming task was last executed, so we can skip the task if its input artifacts have not changed.
- etc

That shouldn't be that hard to implement and would be a big step forwards for Gradle multi-project builds. It does not solve the clean issue of use case 3.). There is another thread from quite a while ago were we have discussed this issue (Thread Name: dependency layer refactoring).

What do you think?

Something like this would be excellent.


Adam


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

   http://xircles.codehaus.org/manage_email


Reply via email to