A lot of people have asked me to document the reasons I want to migrate Hibernate from Maven to Gradle as its build tool so I enumerate those reasons here. If you are completely new to Gradle, I suggest having a look at their overview page. Up front I want to point out that this is not intended as a "Maven bash session" nor as a means to directly compare Maven and Gradle. It is just a means to describe the issues and frustrations I have seen in my 2.5+ years of using Maven for Hibernate builds; in many cases the cause is simply an assumption or concept in Maven itself which did not line up cleanly with how I wanted to do build stuff in Hibernate. Some of the list aggregated by Paul comes directly from Hibernate use-cases; I'd suggest reading through those as well. It is also a means to describe why I decided on Gradle as opposed to other related build tools out there now (Buildr, SBT, etc). Note that there is a comparison wiki between Gradle and Maven, but that it is quite old and out of date in many respects especially in regards to Gradle.
The issues I had with Maven (note that these are largely chronological, not in order of "importance") are as follows:
- Maven does not support mutli module builds. They claim it does, but that is not accurate. What it actually supports is building an aggregation of independent projects. Whats the difference? By way of illustration, take Hibernate. We have a collection of modules that we build. The modules exist solely to isolate dependencies based on which features you are using. Not using ehcache for caching? No problem, do not reference the hibernate-ehcache module and ehcache is not included in your deps. The intention is not to ever release these modules separately. So why do we need to define the project version over and over and over (in each and every module)? Its a case of Maven making simple things hard. But it has real ramifications when, for example, I want to do a release; I have to manually make sure the versions are in synch in many places (and don't get me started on the release plugin).
- Well ok, you got me started. The release plugin is completely worthless. I have been able to get it working reasonably well on smaller projects, but it failed almost anytime I tried it on Hibernate. And even worse you'd have to sit there most times and wait 30, 40, 60 minutes to find out it was not going to work, then go fix whatever it did not like (if you were able to figure that out) and try it all again. We eventually just gave up. (btw a friend just pointed out that Jason van Zyl has banished the release plugin to to the 7th ring of hell and that it will be replaced by "something new")
- hibernate-core is the main artifact. hibernate-annotations, for example, depends on it. Maven knows this. So why on earth can I not simply cd into the hibernate-annotations directory and do `mvn compile` and have it know it needs to build hibernate-core? (hint, see #1) In any case its utterly ridiculous that I need to `cd hibernate-core; mvn install; cd ../hibernate-annotations; mvn compile`. The build tool should be able to figure it out after I went through all the trouble to tell it the nature of these relationships.
- one artifact per project. I know this is a good general rule. But there is a problem with making GoodGuidelines into AboluteTruths... they seldom are. So for example this Maven mandate forced us to move the testsuite for hibernate-core outside of hibernate-core because of the need for hibernate-testing, which depends on hibernate-core and defines common test environment bootstrapping and is used in most other modules (and we wanted to "expose" for users as well). So now we routinely have checkins that break the testsuite runs because developers (myself included) forget to run the testsuite because it lives in some other completely separate module. Yes that is largely just an attention to detail problem, but are these the types of situations the build tool should help you account for as opposed to creating them? Single source directory and single class diredctory per project. See http//in.relation.to/Bloggers/SimultaneouslySupportingJDBC3AndJDBC4WithMaven
- I personally hate the notion of combining "inherited pom data" and "module aggregation" into a single file. I think it is an awful idea. Many of the Maven developers agreed with me when I started migrating Hibernate to Maven and helped come up with the scheme where Hibernate splits "inherited pom data" into ./parent/pom.xml and "module aggregation" into ./pom.xml. And Maven support that, so great... well almost great; it almost supports it. The issue is that many plugins simply do not and it leads to insidious little issues. Why the split in the first place? Well as I said, I do not think it is right. So why not just swallow "rightness" and do it the other way because maven really wants it done that way? The problem is in terms of setting up after initial checkouts. You are not able to build modules unless its parent is there. But if the parent is the aggregator, the only way to make that happen is to install the entire project (or know your maven command switches and disable recursion).
- Right off the bat 2.5+ years ago I was faced with how to deal with handling our DocBook builds. Christian set the standard for much of Java OSS with his work on the ant-based DocBook builds for Hibernate. I saw a means to improve that a little using the notion of dependency management to download the needed stuff on demand. The problem is that in order to achieve anything like this in Maven you *have to* write a plugin. There is no "well lets just script it, and if its generally useful I can bundle it up for others to use" (I realize it is somewhat possible nowadays with gmaven etc, but back then not so much). So I ended up writing the jDocBook plugin, and going on to write quite a few more. And were they ever largely painful. This is the one time I will directly compare Maven and Gradle here, because Gradle "gets it". Writing plugins in Gradle (I have written 2 pretty large ones already) is very refreshing. Yes it is difficult in ways because there is a lack of documentation on how to properly, but the APIs and the power you are afforded are simply amazing (and its not like there is a plethora of documentation on writing "real plugins" with Maven out there anyway).
- Many Hibernate users would like for us to reference the JBoss Maven repos in the build scripts. Seems logicaly, you'd like the build to work "out of the box". However thats a no-no in Maven because the pom that gets used to build the project is the same one that gets installed and deployed into the respositories. In fact most deployment repos (both JBoss and Maven Central do this) check to make sure you do not specify repository info at all. So instead we need to document it on wikis and docbook and direct users to these resources.
- Running the Hibernate testsuite against the multiple databases is darn near impossible in the IDE, in IntelliJ at least using the integration. Ther profiles and how the integration handles them (and the necessary resource filtering) is the issue.
So it was time to start looking for a better way to do Hibernate builds. Yes Maven 3 was on the horizon. Yes it does add some "scripting support", but that is largely "eye candy". As far as I know it just provides alternate ways to configure the same stuff. So I started looking at Gradle, amongst other "DSL, build-by-convention" tools. (btw this feature is called Polygot Maven and according to a friend, Jason van Zyl has stated that it will go away and replaced by "something else").
- First and foremost was the fact that I could script parts of the build that were too difficult to describe through "build by convention" . Its the blend that I liked; you have the best of both worlds.
- Having had difficulty (assembly plugin, etc) trying to access information from other modules in the build, I found the fact that I could simply script an iteration (using closures no less!) over other modules in the build was a God-send in some cases. Like the way you commonly configure standard stuff throughout all subprojects in the main gradle build is a thing of beauty and goes way beyond the notion of inheritence in Maven.
- The general flexibility to define the build and directories the way that seemed to make sense to me and not being forced to do things just for the sake of the build tools was awesome.
- The entire conceptualization of the build process is much cleaner in Gradle imho. In Gradle, you not only have dependencies between modules but you can also define dependencies on tasks, modules, directores, etc in extremely flexible ways. In Gradle, for example, it is already possible to say that one module depends on the compiled classes of another module as opposed to the output (jar) of the module. Take some time to think about that. Very very flexible, And useful!
- Each project/module can have multiple "source sets". A "source set" defines a named group of source directories, output directories, resource diretories, etc. This is what allows, for example, the version of varied JDBC3/JDBC4 support and testing that I liked as outlined at http://in.relation.to/Bloggers/SimultaneouslySupportingJDBC3AndJDBC4WithMaven
- Not sure how to quantify this, but it is really just so refreshing to think "how am I going to solve this?" as opposed to "what are the viable options my build tool is going to leave me to achieve this?"
- Its "incremental build" features are awesome. That's that? It understands when things have changed and when they have not and when certain portions of the build are really needed. This is actually built into the plugins themselves; tasks define their inputs and outputs and gradle uses that info to track whether a task needs to be run because its inputs changed and whether a task changed its outputs to determine if later tasks need to be run. Bottom line, subsequent builds tend to be *very* fast
- The fact that Gradle can publish artifacts to Maven repositories and even generate accurate POMs is not only unique but was a critical component to the decision. I may have issues with the way Maven works with Hibernate builds, but the idea of having a unified means of producing and consuming artifacts is critical. Generally Gradle is able to generate the POM from just the information you already gave it. However in those cases when its not you are able to simply script the extra things you want or override the things you want in a closure to the POM configuration code. Notice the flexibility theme?
- IDE project generation. This was another critical component in the decision. Most Hibernate developers use Eclipse or IntelliJ. Personally I spend most of my development time inside my IDE. I code there, I run tests there, I write documentation there, etc. I was not about to decide on a route that made working in the IDE any more difficult. Gradle has had the ability to create Eclipse projects for quite some time. Recent the ability to generate IntelliJ projects was just completed as well. For IntelliJ, at least, I have been told that Maven-style integration with the IDE itself is coming.
Comments