On 20/10/2011, at 9:19 PM, Peter Niederwieser wrote:

> 
> Adam Murdoch wrote:
>> 
>>> I've had the need for "wiring variations" on several occasions, but
>>> typically
>>> not limited to a single task.
>> 
>> I'm curious, do you have some example use cases?
>> 
> 
> One use case was a release workflow where the order of tasks needed to be
> different for different types of builds (snapshot, release, etc.).

This sounds like an implementation rather than a use case. What was the problem 
you were trying to solve?

> Another
> one was injecting "clean" task(s) for certain types of builds.

Currently, the plan for the clean problem is to make clean as real dependency 
of the build lifecycle task, and have some way to force it to run first (or a 
convention that does this automatically). So, I don't really see clean as being 
a variation. It's a real dependency.


> There were
> other cases.
> 
> 
> Adam Murdoch wrote:
>> 
>>> But true lifecycle tasks can do more. If we use them for tasks like
>>> 'check'
>>> and 'build', those tasks no longer appear in the command line output
>>> (which
>>> is good).
>> 
>> Interesting. Why do you think that is good?
>> 
> 
> Because the way lifecycle tasks are currently presented in the output is
> meaningless. They only appear once all work is done, don't appear if
> something goes wrong, etc.

This is a good point. To me, the answer is to fix how we present lifecycle 
tasks, rather than hiding them.

For most users of a build (ie the audience for the build output), the lifecycle 
tasks are more important than the implementation tasks, and the implementation 
tasks are noise. Even as someone who is a build author with a good knowledge of 
what all the tasks do, when I run the build I usually only care about the 
lifecyle tasks - check, devBuild, install, sometimes classes or test. I just 
want to see progress in terms of the high-level build lifecycle, I don't really 
care about the details. I want the logging to reflect this.


> 
> 
> Adam Murdoch wrote:
>> 
>> I would have thought we'd want to go in the other direction: ie show less
>> and less of the detail (ie worker tasks) and leave just the high-level
>> stuff (ie lifecycle tasks). For example, I'd rather see:
>> 
>> check UP-TO-DATE
>> 
>> instead of
>> 
>> compileJava UP-TO-DATE
>> compileGroovy UP-TO-DATE
>> processResources UP-TO-DATE
>> compileTestJava UP-TO-DATE
>> compileTestGroovy UP-TO-DATE
>> processTestResources UP-TO-DATE
>> test UP-TO-DATE
>> checkstyleMain UP-TO-DATE
>> codenarcMain UP-TO-DATE
>> checkstyleTest UP-TO-DATE
>> checkstyleMain UP-TO-DATE
>> (lifecycle tasks classes, testClasses and check removed)
>> 
> 
> The up-to-date/skipped output is definitely noisy. My gut feeling is that we
> should try to reduce the output in smart ways without completely abstracting
> over tasks. At the end of the day, the user will still have/want to know
> about tasks, for example when a task fails, runs longer, or provides some
> task-specific output like number of tests completed. For this reason I also
> think that we should try to keep tasks meaningful units of work from the
> perspective of the build user.
> 
> 
> Adam Murdoch wrote:
>> 
>> We certainly want to have some way to conditionally wire up and configure
>> an arbitrary set of tasks when a given task is to be executed. I'm not
>> sold on using the configure block of a particular type of task. Or to
>> bolting it together with 2 other behaviours.
>> 
>> I think we should offer conditional configuration as a cross-cutting
>> capability for all tasks, ideally as the default behaviour. It's a
>> separate question as to whether we offer some convenience LifecycleTask
>> which does some other stuff.
>> 
> 
> We can certainly decouple these concern. In a project I've recently
> implemented the following syntax for conditional configuration:
> 
> task devBuild { ... }
> task snapshotBuild { ... }
> task releaseBuild { ... }
> 
> ...
> 
> onlyFor(snapshotBuild, releaseBuild) { ... }
> 
> I do think conditional configuration needs to be a separate feature. The
> default behavior should be lazy configuration (this will help us with many
> things), which defers evaluation until first use but not necessarily until
> execution time.


We definitely want some way to defer task configuration logic. However, I'm 
liking laziness less and less as a solution to this problem. I think there 
would be some goodness in having concrete points in time where already know 
whatever we need to know in order to configure the tasks.

For example, we might separate the instantiation and configuration of the 
project domain objects from the instantiation and configuration of the tasks, 
so that at configuration time we:

1. Run the build script. All a build script does is register work that needs to 
be done in order to 'build' the model. It doesn't actually execute any 
configuration logic. ie exactly what we do for task actions.
2. Do this for all projects in the build in some fixed order.
3. Create and configure the domain objects of the project by executing the 
registered configuration actions.
4. Do this for all projects, in some declared order (eg evaluationDependsOn() 
or some equivalent).
5. Create and configure the tasks of the project by executing the registered 
configuration actions.
6. Do this for all projects, in some declared order.

At each of these lifecycle steps, there'd be events fired, so you can mess with 
things. For example, it would be easy to do some work once all the dependencies 
for the build have been configured.

Implementation wise, I'd sit this on top of a graph of tasks (or lighter-weight 
actions), so that you can easily rewire things, add new stuff, use incremental 
build, profile things, etc. It would also mean you could shuffle the order of 
things around, so you could do something like: projectA.executeBuildScript 
dependsOn projectB.configurations.runtime

One important use case that this approach solves nicely that laziness does not, 
is that we can defer the decision as to which (worker) tasks are required until 
we actually know. Right now, we just guess.

For example, we can add in a Junit task if there are junit tests, and/or a 
TestNG task if  there are testng tasks. We can skip adding @SkipWhenEmpty tasks 
that have no inputs such as not adding processResources when there are no 
resources. We can add GccCompileExe task instances for each variant of each 
executable when building with gcc under linux, and MsvcCompile and MsLink 
instances when building with msvc under windows.  Or GccCompileExe and StripExe 
to build debug and non-debug versions.


--
Adam Murdoch
Gradle Co-founder
http://www.gradle.org
VP of Engineering, Gradleware Inc. - Gradle Training, Support, Consulting
http://www.gradleware.com

Reply via email to