Hi,
The Gradle interface currently has a few purposes:
* It represents a 'build' - that is a hierarchy of projects. It also has some
related state for this build, such as events for projects being evaluated,
tasks being executed, etc.
* It represents an invocation of Gradle - that is an execution of 'gradle ...'
* It is the domain object that an init script configures.
One problem with this model is that there is not always a 1-1 relationship
between an invocation of Gradle and a hierarchy of project (a build). Here are
a few examples:
* a main build + its buildSrc build
* a main build + a nested build invoked using the GradleBuild task.
* (when we support it) a main build + the local builds that build its
dependencies.
This becomes awkward with init scripts. Sometimes you want to apply some logic
to the invocation of Gradle itself, eg build announcements, logging, profiling.
Sometimes you want to apply some logic to every build/project in the
invocation, eg credentials, organisation-wide configuration, such as setting up
repositories, custom plugins, etc.
And sometimes you want to apply some logic to a particular build.
I think there are a few approaches we could take here:
1. We split up the Gradle interface into Gradle and Build.
* There is a single Gradle instance, and it has a collection of Builds.
* A Build has a collection of Projects.
* Each init script runs once per invocation, against the Gradle instance.
2. We change the meaning of Gradle so that it represents a build, and ignore
the invocation
* There is a Gradle instance for each build in the invocation.
* There is a 'main' Gradle instance, and it has references to all the other
Gradle instances.
* Each init script runs once against each Gradle instance.
3. As 2, but the 'main' Gradle instance represents the invocation.
* Each init script runs once against the main Gradle instance only.
4. Similar to 3, but we introduce an interface 'Build', which 'Gradle' extends.
* The main build implements Gradle, and other builds implement Build.
* Each init script runs once against the main Gradle instance only.
For all of these options, we'd provide ways to navigate between the
Build/Gradle instances, so that you can inject configuration into all builds, a
particular build, sub-builds, etc, just like you can with projects.
I'm pretty keen to have each init script run once per Gradle invocation, so I
don't like option 2.
I think option 1 is my preferred option. Here's a potential migration plan:
1. Stop executing init scripts against buildSrc.gradle. This is a breaking
change. However, we currently only execute ~/.gradle/init.gradle against
buildSrc.gradle, rather than those specified by -I. So, we have a breaking
change here regardless, because we can't continue to execute only some init
scripts and not others.
I don't think this is too nasty a change. It simplifies things quite a bit,
from an implementation point of view, and from a init script author's point of
view.
2. Introduce a Build interface. This contains the build-specific stuff, copied
from Gradle. Add project.getOwnerBuild() (getBuild() is no good, because it
masks the 'build' task).
3. Add Build.getGradle(), that returns the shared Gradle instance.
Project.getGradle() continues to return a build-specific instance.
4. Deprecate Project.getGradle() and the build-specific stuff from Gradle.
Remove at some point.
For now, I just want to do step 1. The rest can wait until we start tackling
build aggregation, I think.
--
Adam Murdoch
Gradle Co-founder
http://www.gradle.org
VP of Engineering, Gradleware Inc. - Gradle Training, Support, Consulting
http://www.gradleware.com