On 20/10/2011, at 11:18 PM, Adam Murdoch wrote: >> 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
Evaluation vs. execution is already confusing for a lot of people and my understanding of what you propose makes things more complex. However, I see that it solves certain problems. It's not always going to be desirable to build *all* of the model. Consider the case where that's expensive such as fetching configuration from an external store. But, if you can still influence the model once we get into real execution mode you could still defer this kind of thing using the same techniques you can use today I suppose. I'd like to more formalise the concept of Buildable model elements for this purpose, but I think that's a separate discussion. Regarding the complexity, I guess the question is how much of this can we hide behind DSLs for the average user? My instinct is that this might be easier to do with what you propose. > 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. I don't see how this would be simpler than it is now with afterEvaluate hooks and more modelling instead of direct task configuration. Which, now that I think of it is really just a less formal version of what you are talking about. -- Luke Daley Principal Engineer, Gradleware http://gradleware.com
