Gradle review

Inversoft has recently switched from Savant to Gradle until Savant 2.0 is finalized and can effectively rolled out (if this ever happens). Gradle is a decent temporary solution, but as a build tool junkie, it rubs me the wrong way. Here are some things that we encountered during the switch that Savant addresses.

Maven Central

We started by attempting to use Maven Central for our dependencies, but quickly realized that Maven Central is a complete mess. We also realized that it suffers from the “most surprise” pattern as well as “GPL” infestation. During our transition, we started to realize that using Maven Central actually caused our number of JARs to dramatically increase and for a couple of GPL JARs to sneak into our classpath.

In the end, we had to rever to managing our own repository and manage all of the ivy.xml dependency files by hand to ensure that everything was correct.

Transitive Mess

Gradle appears to suffer from the “let’s make it simple” methodology of Maven. In order to actually make life simple and avoid actual work, Maven pulls in the entire universe of your project dependencies for compiling and runtime. This means that even if you never import a class from a transitive dependency, Maven puts it in the classpath. This allows you to use classes from transitive dependencies. If you do this though, you’ll never actually know what JAR file a class comes from since it could come from some deeply nested dependency. It also tightly couples your project to your dependencies and those dependencies transitives. We prefer loose coupling rather than tight, so this is generally bad. We also like fewer dependencies at compile time rather than more. Again, bad news all around.

Flat Structure

Gradle plugins and domain are entirely flat. This makes it difficult to separate concerns and distinguish between Java compiling, testing and runtime. Maven actually got this right by cleaning separating concerns and separating plugins.

Configuration vs. tasks

Gradle build files can quickly become large and complex. It is often hard to distinguish between what is configuration and what are tasks.

Order matters

Order matters in Gradle build files unless you always use the String syntax for all configurations and tasks. If you have one task that depends on another task, the order of declaration matters. This is quite painful to debug and I thought we had finally grown out of single pass compiling. Apparently not.

No Versions

There are many pieces of Gradle that don’t like versions, e.g. plugins. These don’t have versions and just use the latest available. This is usually a bad idea because historical builds can break and you’ll never know what version you’ll be using when you execute a build.

MD5 and SHA1?

Why both?

Hashes not always used

The MD5 and SHA1 hashes aren’t always used. For example, the ivy.xml files don’t appear to use the hashes associated with that file. It really should use the hashes for everything.

UploadArchives doesn’t run the tests

WHAT?!?!?

Summary

Overall, I’m not a big fan of Gradle. It seems to be overly complex and sorta punts on dependency management. Having used Maven, Savant, Ant and Ivy, I would say that Savant 2.0 is still the best build system out there, even though it still isn’t finished.

3 thoughts on “Gradle review

  1. It has been almost two years since the article was written, some things have changed.

    Maven Central – I don’t get what this has to do with gradle

    Transitive Mess – transitivity for dependencies can be disabled, though this should have been the default, it can be done globally

    Flat Structure – not entirely sure what you mean by this, but plugins have their own scope as part of the plugin API

    Configuration vs. tasks – still true, I don’t see any solution as long as this separation exists

    Order matters – still true, but I didn’t have many issues with this. The problem is there’s no such thing as “declaring” a task, the DSL is just groovy code that gets executed, so tasks are created/added at run-time.

    No Versions – plugins can have versions. these are specified as part of buildScript { dependencies {} }

    MD5 and SHA1? – this is the default in Ivy, however for some time gradle overrides this default and only creates SHA1

    Hashes not always used – still true, they’re only used for caching, but generate no errors on mismatch

    UploadArchives doesn’t run the tests – this is a longer story. uploadX is generated by gradle for each configuration X (it’s not necessarily “archives”). “test” is a task added by a plugin (ex. java). They can’t know about each other. At most the java plugin could add its own task for example an “uploadJar”.

    Like

    1. Patras,

      Thanks for the great comment. I still have a number of the problems with Gradle that are in the post. You comment about what Maven Central has to do with Gradle, well it has a lot to do with it actually. Gradle uses Ivy under the hoods because it doesn’t provide its own DM system. Ivy relies heavily on Maven Central because there isn’t any Ivy Central. Furthermore, neither Ivy or Maven do a good job of management dependencies and versioning. I believe that SemVer is the best solution to DM and that Ivy and Maven should both move quickly to that standard.

      Plus, I believe that Gradle not having its own DM system is a huge issue. Gradle bolts on Ivy and this makes things like loading custom plugins messy and non-intuitive. It also makes loading core plugins abstract and not controllable (hey – I want version 1.3.2 of the Java plugin not version 1.8 that comes with the latest version of Gradle). Ivy also has issues with transitive pruning and scoping. If you have ever run into the IDE project file bug where one of your dependencies moves from the scope you define in your build.gradle file to a scope it exists in as a transitive dependency, you know what I’m talking about. Ivy also doesn’t support dependency downgrades (similar to Maven). It always upgrades to the latest version in the dependency graph (bad!).

      When I say flat structure, plugins are defined like this:


      apply plugin: "java"

      What if there are two plugins with the name Java?

      I have also uncovered new issues include the “check” target bug where some targets aren’t run even though they fail the up-to-date check.

      Overall, Gradle suffers from the same problems as Maven. Most of these problems arise from the concept that plugins should be able to define targets. This inherently makes plugins dependencies not only possible but required and plugin dependencies are hard when they are globally scoped. They are also hard when you consider that many projects don’t need the Java plugin but are forced to include it or define a bunch of empty targets just so they can use other plugins.

      Rather than just complaining, I decided to resurrect the Savant build system and try to improve on things. You should check it out and contribute if you are interested.

      Like

Leave a Reply to Sandeep Heggi Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s