Hans Dockter wrote:
On Aug 13, 2009, at 11:38 PM, Adam Murdoch wrote:
Hi,
I'd like to figure out a bit of a design for the so-called 'source
directories' feature. Basically, this feature is about making the
Java plugin, and all the plugins based on it, more flexible in how
java projects are laid out, and what happens to the various source
files that make up a java project.
I'd like to start with use cases we would need to solve.
Right now a java project:
- Has some production source files. These are compiled and assembled
into a jar or war. Optionally some unit tests or quality checks are
run against them.
- Has some test source files. These are compiled and run as unit
tests against the production classes.
This is all nice and simple. It works fine for, say, an open source
library project. However, I've not seen a decent sized java project
where this is sufficient.
I'd like to find out about those use cases you might have where this
simple model does not work well. Here's some example use cases:
- Multiple projects sharing the same source directory. For example,
this might happen when a legacy monolithic Ant build is chopped up
into a bunch of Gradle projects.
- Production and test source in the same source directory. Not
necessarily a great idea, but some people do this.
- Production source with an api and implementation. For example, you
might want to use different checkstyle rules for each, or javadoc
just the api, or you might want to end up with an api jar and an api
+ impl jar.
- Test source contains test fixture classes which are used by other
projects. For example, you want to jar up your test classes and
publish the test fixture jar alongside the production jar.
- Multiple test suites, each in a separate source directory and with
separate classpaths. For example, you might have functional tests or
performance tests which you don't want the 'test' task to execute.
- Compile different parts of the source against different java versions.
- Generated source, or generated bytecode.
Anyone have any other use cases?
I have forgotten to mention one other use case. It is discussed in an
old posting on this list: http://markmail.org/thread/6ncjp7dxtbq5et5f
It is more related to project dependencies for the build script
classpath. But I think it belongs to this discussion.
I hadn't forgotten about this one. It feels more like a build
composition problem to me, so I was going to leave it for when the
settings file is removed.
Some possible approaches:
1. Use a buildSrc build.
This is similar to what we do now with the buildSrc project. We would
generalise the buildSrc concept, so that every build may also have an
associated buildSrc build, which you can add projects to when you define
the structure of the main build. Then, prior to evaluating the main
build, Gradle builds all the projects in the buildSrc build, and adds
their artifacts to the main build's classpath.
2. Aggregate builds.
This is similar to what we've talked about for Gradle's website build.
Basically, you can declare dependencies on projects in another local
build, and when the dependency is resolved, Gradle takes care of
building the dependency in the other build. For example:
buildscript {
classpath name: 'some-other-project' {
buildfile: 'some-other-project/build.gradle'
}
}
This is just a regular module dependency, except that we also tell
Gradle that when the classpath configuration is resolved (ie just before
the build script is evaluated), it should build configuration 'default'
in project ':some-other-project' in the build defined by
'some-other-project/build.gradle'.
The buildSrc project would be changed to work the same way, and an
implict dependency similar to the above would be added to every build.
The benefit of this option over option 1, is that you can use these
types of dependencies for any configuration, not just for the build
script classpath. For example, the buildSrc artifacts could be used for
both the build script classpath, and the java compile classpath for a
project. You can also easily customise the location of the buildSrc project.
3. Allow project dependencies in the classpath declaration of a project.
Here, you would be able add a classpath dependency on another project in
the same build. Gradle would take care of building the artifacts in the
other project before evaluating the build script. For example:
classpath {
dependencies project(':project1')
}
Gradle would evaluate project1 and build its default configuration
before evaluating this build script.
This is a really interesting option. We could change project evaluation
into a task, and the project evaluation task would simply dependsOn
configurations.classpath.buildDependencies. We get all the dependency
ordering and cycle detection for free. You can doFirst() or doLast() or
enable = false or onlyIf() or gradle -x subproject or whatever. And any
new stuff we do for tasks (eg change detection, parallel execution, and
so on), would also work for project evaluation.
4. Combine 2. and 3.
This is where we really get some interesting combinations. We end up
with Gradle building the DAG across multiple builds, wired together
based on the artifacts that projects publish and use in build scripts or
compilation or runtime or all 3. The projects in different builds remain
loosely-coupled, and yet Gradle wires them up into a single aggregate build.
Adam
---------------------------------------------------------------------
To unsubscribe from this list, please visit:
http://xircles.codehaus.org/manage_email