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