I’ll try to answer more direct questions in this section. Sorry if I missed yours, I got quite a few comments/answers…
I would sacrifice caches, dependency locks and better plugins to version to have a declarative build process instead of an imperative one. Give me a declarative Gradle and I will love it.
Again, I think this is the wrong tradeoff. Given that you run the build way more often that you change it, declarativeness (that you can have with Gradle) shouldn’t be the goal. Your goal should be to reduce your build times, make your build reproducible, improve your developer productivity. Declarativeness is not a goal, it’s at best a mean, but not sufficient by itself. A declarative Gradle, whatever that means, would help you reduce the cognitive overhead, but wouldn’t help you better model what your application needs.
make a one liner the ability to publish on different repos the snapshot and release artifacts. The way it was done on Gradle 4.x was broken on 5.x and the only way we found to do it is a horrible hack
Here’s a webinar about publishing. Publishing is not complicated with Gradle. It used to be poorly documented, and the old publishing plugins didn’t help. But publishing to a snapshot repository should be trivial already.
Some people will prefer to do their own way, some people will prefer to have a less expressive tool that will produce similar build processes on their projects. Gradle give you the former, Maven the latter. As I say, a matter of taste.
A less expressive tool reduces the risks of writing bad builds. It doesn’t help, however, in developing correct, reproducible, fast builds. An, again, I disagree that Gradle leads to "custom builds" everywhere. Most people stick to the defaults and are very happy with them. The more complex builds you find in the wild are those which have indeed very specific needs, or need to be tweaked for performance, producing more artifacts, combinations of artifacts or testing. Things that you can’t easily do with Maven profiles, for example, because profiles are adhoc solutions which do not combine well.
Gradle tries to create a fake sense of declarativeness, but it is just an illusion.
It’s not an illusion. Gradle has a clear separation between its configuration model and execution model. All tasks have declared inputs. The plugins create either new tasks or conventions. This is not an illusion, this is the reality. Now, because you can write if
or loops doesn’t mean it’s not declarative, it’s imperative-declarativeness. And yes, you can end up with giant build scripts with "code" inside. If you have, do yourself a favor, refactor your build like you would with your code, because no one should tolerate this. Use buildSrc
, this is your friend.
I think library dependencies is not correctly supported by IDEs and Java modules are better.
That’s not correct. We’ve been using the native Gradle IntelliJ support for years at Gradle, with api and implementation separation, and it works exactly like it should. Implementation dependencies are hidden from consumers, like they should. If you don’t see this, either you didn’t declare the dependencies or you have a bug in the IDE, in which case it needs to be reported.
Even worst, Gradle don’t have an official plugin to deal with module-info. there was an old post that says it is not necessay with Gradle because lib dependencies were better (they don’t)
I don’t think anyone said you don’t need module-info
. There are different things in play:
-
separation of API and implementation: Gradle supports this, and it maps to requires
vs requires transitive
-
declaration of public API packages: Gradle used to have this with the deprecated "software model". It still has to be backported to the current configuration model. For this, module-info
works fine but it forces you into using the "modular world", which a lot of libraries, frameworks and IDEs are not ready for.
-
declaration of services: Gradle doesn’t support this.
Can you use modules with Gradle? Yes, there’s a quite good plugin to do it. We are planning to support modules and modularity in general better in Gradle, but not short term, because we have bigger pain to solve for our users first. It doesn’t mean we don’t consider this important.
I don’t get why Gradle allow you to explain what your app is better than Maven. In fact I think it is more difficult to explain it on a script that descriptivelly.
I think the question is what Gradle models better than Maven. A good example here is api
vs implementation
dependencies. Because Maven uses the same descriptor (pom.xml) for the producer and the consumer, a dependency declared in the <compile>
scope ends up on the compile classpath of the consumers. This is leaking internal implementation details to consumers, which is very bad because it makes it very hard to evolve libraries, because changing an internal dependency would break consumers which accidentally started depending on your own transitives. This is just an example of course, there are many other differences (like, why we consider that exclude
is a bad workaround in general, more on this topic in Gradle 6, if you want to read our docs).
The builds I’ve seen have been very spaghettish and clearly copy-and-pasted together un-understood recipes from SO.
Yes, there are bad builds out there. With Gradle it’s frequent for quite old builds from early adopters. More recent builds tend to be much cleaner, because we made a significant effort in guides, getting started samples, documentation. You’ll always find bad things, and it should be encouraged to fix. On this topic, tools like build scans really help. And copy/pasting from SO is indeed a bad thing. If you copy and paste without understanding what it does, well, bad things can happen… That said I’ve seen very scary Maven builds too, and believe me or not, some of our customers wouldn’t be proud to show you their Maven builds. It’s the "personal experience fallacy". I’ve experienced very clean Gradle builds, you’ve experienced very bad Gradle builds. I’ve also written bad Gradle builds, which I dramatically improved, making them more correct, faster, … Gradle is like any other technology: learn it and you can understand what it brings.
Gradle performance/caching are very attractive but the scripting possibility is a deal breaker. A « declarative-only » Gradle would be perfect for people like me.
Again I think "declarative" is the wrong term here. Locked down to reduce the risks of doing bad things is what you want. It doesn’t matter if it’s Kotlin, Groovy, XML or whatever else. It doesn’t matter if you can use if
or for
loops. What matters is what you can express, and what should be limited. It’s all good engineering that we must share within the industry, find the best patterns, discourage the bad ones. There are quite a few things in the Android world (which uses Gradle) in this direction. We, at Gradle, should do more, but it’s always a matter of priorities: fixing the most important user pain first. By the way, we provide a Maven build cache with Gradle Enterprise. That is to say, the ability to cache Maven builds using Gradle Enterprise. However, this is limited to "clean builds" (which Maven users are used to do in any case), because of the limitations of the Maven execution model (no knowledge of what each plugin or mojo does, where it writes files, …).
IMHO the biggest feature of Gradle that Maven doesn’t have is the ability to change the version of the project
Well, this is just an accidental example of the interest of having access to the API in a build script. It offers a number of options for the release process, but that’s not the only one.
My only complain about @gradle is how it is unnecessarily complex to deploy a multi-module project to central. Too much copy & paste, or you need to make an init script, which I still haven’t managed to do.
Technically the problem is not "how to deploy a multi-module project to Central", but rather, how do I avoid duplicating configuration between scripts. This is what buildSrc
is for. As soon as you have repetition, then, it means a plugin makes sense. buildSrc
can be seen as "local plugins", and this is where you should write your common code. Then each project applies a plugin to publish. This is a composition model, as opposed to the inheritance model of Maven.
I like all those (caching, incrementality, …) , in theory, but for my needs they are more complexity than feature
I don’t think those are complexity. A task declares its inputs. If you do, you benefit from up-to-date checking, and with a bit more configuration, caching. You don’t have to. If you don’t declare the inputs/outputs, you’re back to the "Maven" approach where the build tools knows nothing about what a task does, at the difference that Gradle knows that it knows nothing, so can be a bit smarter. As soon as you start declaring your inputs, you benefit from more. It’s more work, for sure, but it’s not that complex and the benefit is huge.