I'm looking at build tools for a fairly large project (mostly Java with some Jython), and pretty dissatisfied with all of the standard options out there. Gradle looks like it might be able to do the things I want. I am wondering, is anybody out there is using it who could talk about their experiences with it?
Another interesting project, though it looks quite young, is Raven.Here are the cases where, IMO, the usual suspects (Maven, Ant w/ customizations, Ant+Ivy) fail:
- I strongly believe in decomposing software into small libraries that do one thing well. Build systems that
have a concept of inter-project dependencies at all tend to treat other projects exactly the same way as third-party
libraries. This adds enormous overhead to the development process when you are working across multiple libraries.
Building/running/testing/profiling a project which you have the source to locally is fundamentally a different activity than doing the same against a 3rd party library, and you should be able to run against .class files rather than a packaged JAR in that case (you should also, of course, be able to run against packaged resources too).
While IDEs can do some compile-on-save magic, this really needs to be a first-class feature of the build environment, not a situation where IDEs hack around limitations of the build tool.
- XML is great for describing data and horrible for describing behavior. Builds - the parts that need
customization, anyway - are mostly behavior.
- Convention over configuration is great when it works, but when the convention is wrong for the project, there
should be a way to override it in a straightforward, readable way (i.e. I do not want to write Maven plugins, and
if I do, nobody will be able to see the nature of the customization from the pom.xml file, they'll
just see that the build uses some opaque thing they've never heard of before).
Along with this, there needs to be a way for plugged-in code to figure things out in the case that customization is happening - for example, I have an Ant task that cross-links and merges Javadoc. Convention should not be the only solution to that problem.
- 3rd party libraries do need to be handled well, and builds need to be portable without putting JARs in
version control. The one (only) redeeming trait of Maven is that it gets this part right enough to be valuable.
We tried using Apache Ivy as an alternative - I was hoping that Ivy would give us the library handling of Maven without dictating every other aspect of how our software is built. However, a strange thing happened on the way to librating library-downloading from totalitarianism - Ivy seems to be totally focused on defining the One True Way All Repositories Must Be Organized. Of course, to be actually useful, you need Ivy to talk to a messy, disorganized Maven repository. To do that, you (cue drumroll)...write regular expressions to map that Maven repository into The Way God Intended Libraries To Be Organized. I will not waste my or anybody else's time doing that.
- Version labels should be based on DVCS ids and that should be normal and easy. If you are using a versioning
system such as Mercurial, then you have an easy way to, instead of arbitrary
numbers, encode complete information about how to reconstruct the exact bits as the "version" of
the software - just run hg id. That's much more reliable than knowing that 1.0 maps to an SVN tag.
Incrementing version numbers may be needed for other reasons, but it should be possible to embed this information in the software without extreme measures or resorting to something like magic CVS version tags, or rewriting the way JARs get built.
- Automatically downloading the closure of a project's dependencies is nice, but some control is needed. Maven
projects tend to grow giant tails of library dependencies. If you know you can safely give your dependency tree
a haircut, there should be a non-hacky way to do that.
Ironically, this happens because many projects are not factored out into enough small libraries that do one thing well. Why aren't most projects factored this way? Because all the available build tools add too much overhead to the development process if you do that (mainly in the form of rebuilding things you shouldn't need to rebuild, if you are working in multiple projects at the same time).
- Circular test dependencies should be possible. If Project A defines a data access layer, Project B implements it
on a production database, and Project C implements it on an embedded database for ease of development, I should be able
put implementation tests in Project A. The alternatives are, duplicate the tests in A and B and have them get out-of-sync,
or have a Project D that contains the tests for Project A. The former is obviously a bad idea, and the latter is
pointless overhead (not to mention that with Maven, you'll be doing extra cleaning and building to
pick up changes you made in B or C).
Absolutely, circular compile-time dependencies should be forbidden. But running tests is not the same thing as compiling, and a build system should not treat it as if it were.
- Test output should be optimized for development-time, not for report generation. For me, being a
System.out.println() kind of guy, Maven's test output handling is mind-bogglingly awful. All I really
ever want out of development-time logging (which is not the same thing as deployment-time logging) is,
show me the console output, all of it, in the order it was logged. Do nothing clever with separating System.out and
System.err, nothing clever with separating different tests in different files. Just give me the fewest gestures
possible to get to the failure-point when a test fails.
- Parent-child relationships between projects should be a function of their dependency graph. Shared configuration (such as library versions or file encoding) is not a kind of project - and shoehorning it into something like a Maven "parent" project just places pointless limitations on what you can do (try having two parent projects some time - why should that be impossible?). Configuration is tree-like, and may indeed map to a dependency-tree, but not always.