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.
This is the way the meta-jb (my project) builds are setup. It used to
be one big monolithic ant build with lots of different targets and now
I've superimposed gradle projects onto it. (Interesting side-note: the
reason I've abandoned maven here is because the pom.xml's produced were
uglier than gradle's nice clean ones.)
In some cases, I could have moved things around but some of these
projects are so small and for some things it's a lot easier to navigate
around one tree than a dozen different full-depth trees.
For our project, in the root we have:
/src/main... etc.
/std/build.gradle, settings.gradle
/std/core, /std/util, etc.
/ext/
/ext/extract, /ext/util, etc.
"std" contains all of the main sub-projects that are managed and
versioned together. The dependencies in those sub-projects are of the
project( ":foo" ) variety.
"ext" contains separately released projects that are independent. They
depend on other project artifacts just like any other dependency using
the maven-style gradle-ized coordinate groupId:app:version.
In both cases, all of the build.gradle files have sections like:
compile {
include( "org/progeeks/foo/**" )
}
The only odd thing is configuring resources like that seems to use an
entirely different syntax.
For the most part, this works great... until it doesn't. And when it
doesn't one gets very strange behavior.
Since javac will happily compile anything that it finds the source for
if it doesn't also find a class on the classpath, and since we're
building everything out of the same source tree, any mistakes in
dependency specification (or "excludes" in some cases) will cause those
.class files to be duplicated in the local jar.
This can also have a cascade effect. I think I've seen cases where if a
project's dependencies include an old version of a .class file for a
class they shouldn't have and the compiling project is dependent on a
newer version then it will get recompiled.
At any rate, it should be easy to see that one little mistake can cause
some very strange classpath behavior in trying to figure out why
updating "util" isn't causing a certain class to update.
I'm not sure this is a problem gradle can solve but it's something to be
aware of for any going down this road.
Actually, is there a way to limit the classes included in the build jar
file? If I could give it a similar set of include()/exclude() lines as
in the compile {} configuration then at least the errors wouldn't
propagate silently.
- 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?
This is probably not related but my only other project pain has to do
with the compile test cycle.
It used to be that we had some junit helper classes that depended on our
util module. The util tests used these helper classes to test the util
classes. In Ant this was ok because all project source was compiled and
then all test code was compiled and run.
In gradle (and maven, and etc., etc. any other project-based build
system), each project is an island and must be "fully built" before
another project gets to use it. This adds a sort of artificial circular
dependency since to compile the junit helpers one must full build util
which includes testing the jar which requires the junit helpers, etc..
It's sort of too bad that project -> project dependencies can't have
their tasks interleaved such that I'd get util:compile, junit:compile,
util:test, junit:test, etc..
I realize there are several reasons that this setup was fragile but it
was really convenient. Probably completely unrelated to "java project
source layout" though.
-Paul
---------------------------------------------------------------------
To unsubscribe from this list, please visit:
http://xircles.codehaus.org/manage_email