Why I Moved Back from Gradle to Maven
blog.philipphauer.de
blog.philipphauer.de
That being said, "flexibility" is not special to Gradle. As long as there have been build systems there have been people who are too lazy to learn them and get "bright ideas". For instance I have no idea how many shellscript-infested build setups I've seen for C programs that could easily have been built using entirely standard Makefiles. If the author had bothered learning to use Makefiles.
We're on opposite sides here. I can control my team wrt rope and what not. But it's very hard to wrangle a build system to perform some advanced task you need without flexibility. I'd way rather have problems with the build system users than the build system authors. One is more easily fixable.
Make doesn't have a C obsession (although the docs do), make works anywhere you have a dependency graph that can be expressed as a file timestamp. Here's an example compiling a c# project: http://flukus.github.io/rediscovering-make.html . It's much simpler than any .net build tool (and I've tried them all).
From a quick look t looks like it's task based instead of output based as well, so an incremental build doesn't look simple. Correct me me if I'm wrong, but it looks like it's trying to be a better nant, not a better make.
The older I get, the more I realize that the lisp DSL approach is probably best: provide a simple, declarative walled garden. If I want to change something for my project using the full language, let me, and may the PR reviewers determine my fate. Ruby also seems to adhere to this philosophy, although I don't have a lot of experience with it.
Confused here. I can't really stop progress waiting on build system features. Extensibility is required like a programming language. Otherwise, you must stop using the build system or build wrappers around it or outside of it making your build even more bespoke than if you used a more flexible system.
If I'm writing code anyways in MyLanguage to compensate for MyBuildScript, why aren't my build scripts written in MyLanguage?
I think this dichotomy, flexibility vs maintainability, reflects build complexity.
Not only do I think the same-language-DSL (a la LISP, as you pointed out), is the 'least bad' choice, I think if we raise our gaze a little the answer here is painfully obvious: how do we handle those issues everywhere else?
In other cases if you need to move files, call some sys commands, structure some data, and express some logic? What if you have to call a webservice with some throttling logic, where is your "build language"? Plop something in a DB? Not to mention that complex builds often have specialized command line apps called by their specialized build langauge.... why?
Complex logic we use programming languages for. Why sacrifice power, components, libraries, compiler-time-checking, and all the rest to use a lesser tool? Full language power, it's The Right Thing.
You can do whatever you want, but you're still operating within the confines of the build system so you've not lost the ability to see what's going on.
It's not all that common that I see an "advanced task" that actually belongs in the build, though -- more often I see people who have decided to fight against their tooling rather than working with it.
And if you find yourself consistently not being able to build a piece of software with a relatively simple build process, perhaps you are trying to do too many weird things?
In my experience, it's not dumb but clever which you should worry about — those are the ones who'll spend two weeks making something completely unmaintainable after reading someone's post about how some esoteric mathematically pure style delivered a 7% performance increase on an edge case.
Then what it means, is that 100-xx percent of projects are going to be screwed up.
Not everybody lives in a nice world when the project is a single web application with 4 microservices and a shared library maintanied by a team of 5.
It is sometimes worth it to spend a little extra time as you set up the build process, and as you make decisions on how to organise things so they can conform to a build process other people will understand. Quite often "clever" builds on long-lived projects, or projects with many developers, have a higher cost in terms of effort than what you save on "clever" shortcuts.
This isn't so much about review or not as it is about not establishing a culture where you always take the easy way out. It is tempting to think that "this will be solved through the magic of code reviews", but if you are in charge of several teams and you have to face the reality of projects living on for years with people joining and leaving the team. It is important to have empathy with developers and your future self.
Much build complexity and annoyance is due to people taking the easy way out, for instance, by scripting things that should either be adapted to make use of an existing build process, or develop a proper plugin that is documented and helps newcomers to the project understand what is going on.
If you make it easy to make ad-hoc solutions, people will build ad-hoc solutions.
* The Gradle Kotlin DSL, which is nearing production-readiness, substantially improves the assistance/docs one gets through the IDE.
* However, the Kotlin DSL does not help with the large API surface and understanding of Gradle concepts, so I have been working on improved docs and best practices for the past few months.
* However, most folks don't or won't read docs, so additional tools will help users opt-in to strictness and quality checks that enforce best practices through the tool.
Even with all that, one can think of build tools like languages: one tool will never be able to suit all needs or projects. However, I intend the broaden the audience for Gradle by making it more accessible to a wider audience.
I'd love your help. My Twitter DMs are open @eriwen. DM me the 1 specific thing you would change about Gradle if you could.
[EDIT]: Fixed the formatting of the bullet points.
Focus on Android Studio because that's the only place people really use Gradle because they are forced to do so.
You need to create a LOT of Android Studio sample projects, and they need to be kept up to date for the various versions of Android Studio. If you did nothing but create "Hello World" in a zillion various flavors (Java, Kotlin, Scala, NDK, NDK with static library on different architectures, etc.) for people, that would be huge.
My specific beef is with NDK projects. Pulling in static libraries and compiling them with the NDK is a mix of magical keywords that sometimes materialize from some kind stranger via chat--generally because Gradle has to shove those keywords to something like CMake--which ALSO has an enormous learning curve.
We refer to that in the corporate world as "death by committee".
You can solve the problem with a GitHub repository and an intern trawling StackOverflow and IRC logs. Choosing not do that sends a signal.
Waiting for Google to "accomplish it together" (aka sometime before the heat death of the universe) is a good way to get replaced.
Otherwise we would keep using only Maven for Java projects.
Slower builds and higher hardware requirements for daemon and build cache are not worth it.
False
If it wasn't forced upon us for Android dev no one at my employer would even bother to learn what is Gradle.
The opinion is that it is the last spot keeping Groovy alive.
(They may have good reasons, but anything that prolongs the lifespan of Groovy is a bad thing.)
My Android projects are mostly private stuff but I don't remember any project that would require any kind of custom build. There is whole plethora of tools. My own calculators for various stuff, app to communicate with my family, apps to manage my electronics (bluetooth, wifi, cloud), apps to manage stuff in my Drive. My changes are mostly adding dependencies.
That basic boilerplate is no longer enough and tricks change with each release.
Edit: Also, I personally do the opposite route wrt the paradigm of the wrapper and the daemon. I don't commit a wrapper script and have people use a downloaded Gradle themselves. I also run --no-daemon on every manual execution for smaller projects. But I may not be normal here.
Yup, and while we're at it, realize that the declarative approach doesn't work after the first basic uses and even though your hello world build file is simple, most people need customization. Scripting the build system should be its main feature and everything else as just shortcuts (to be fair, Gradle kinda does this w/ tasks, but doesn't make it simple to just drop into code).
Checking versions can be accomplished by using awk or another tool to compare output from $compiler --version.
What am I missing?
If the dependency doesn't conflict, then my package manager takes care of it.
And a lot of Java/Groovy/Scala/Kotlin libs are on Maven Central, but not packaged for let's say CentOS/Ubuntu/Debian/etc. So the package manager for JVM is Ivy (ivy2), or full Maven (which is basically ivy + a task runner).
For example, have you ever wanted to break a large project up into subcomponents with build-time enforced dependency relationships? This is trivial with gradle but very hard/custom to do using make. gradle is aware at a domain modeling level about components of a project, their dependencies, and the implementation and interface artifacts they generate. I think this is really cool.
Their native support is still evolving, so it probably can't drop-in replace more complex native builds, but a few years ago I had it building a shared c++ codebase for macOS, android-linux, and iOS. I could test the core on macOS locally as its own project. I had google-test built locally and specified as a dependency (a sibling project) rather than prebuilt and plopped in a vendor directory. And the code was mostly free of ifdef android or macos etc. because I could not only enforce separation at a project level, but also easily provide a place for platform specific glue to live (in their own sibling projects). It was really cool and would have been a lot more work without gradle.
* Convention over configuration: implicit defaults for everything so the hello world example is very simple. If you try to create a new Java project in Gradle, not by copying and pasting or having the IDE autogenerate something for you, you're going to have a bad time. * Whenever you do want to add something, there is a clear place within the structure of the root ratpack{} closure to add it. The port goes under serverConfig, routes go under handlers, etc. You don't set the port under the routing handlers, that would make no sense. Very different than build.gradle which feels like a flat list of unrelated tasks. * Support for both anonymous code and for code which has been factored out. Of course Gradle supports this (factoring out buildscript dependencies instead of a flat build.gradle) but the experience makes you feel like you're playing around with Frankenstein's monster so hardly anybody ever does so. * Documentation provides examples through tests, which clearly show both the code and the expected outcome, and which Ratpack prioritized being able to express in a concise manner precisely so that it could be used for documentation purposes.
Could Ratpack have gotten rid of the root ratpack{} closure, the serverConfig and handlers and registry closures? Technically yes, they're not needed to namespace their children. But it would be so much less understandable.
I understand the arguments made in article and I am the Gradle ninja in my project so I may have subjective view on the whole topic.
My points:
- Maven requires a lot of boilerplate for everything. Copy-paste. I need to instruct everybody on what to copy, where to paste, and how to edit and what they should not touch under any circumstances.
- Gradle... It's an environment for programmer! I just write a bunch of code in Kotlin so that all 100 projects have a bit of my custom DSL that suits the needs of our application. The code takes the DSL and does all the job behind the scenes and most developers don't need to understand it. They can, but they don't need to. This is how it is supposed to work -- in a large project people specialize and the application should be created in a way that will allow people to not have to understand everything.
- Maven is strictly declarative. If you can't find a plugin you either dig up Ant or you have to write your own plugin. Not nice.
- In Gradle I can have almost everything declarative but I have the freedom to drop this bit of logic where it is really needed.
- Don't try to make it too complex just because you start using Gradle. Great power comes with great responsibility.
If I wanted to change one thing in Gradle it would be for the Gradle project to focus on debugging. I really find it difficult to figure out why things fail even though I have almost 20 years of experience with various languages and a decade of very intimate experience with Java. Make it easier so that people are not put off.
> Maven requires a lot of boilerplate for everything. Copy-paste
I think you may be doing it wrong. Maven supports declaring project-wide or module-wide entities and their attributes at every level. Duplication or copy-paste is a sign of not understanding Maven
> Gradle... It's an environment for programmer
To me this is a counter-argument. When I need to understand project configuration I expect structure, not behaviour.
> If you can't find a plugin you either dig up Ant
Been doing Java software engineering for many different projects in various industries on all scales and I only had that problem once or twice.
For example in my current project the applications are packaged into completely custom and proprietary package that is then being used by the completely custom automation to be deployed to the infrastructure.
I have created a DSL which allows people to specify rules concerning how their projects are supposed to be treated when they are being included in one of these large packages. I have also placed some extension points that allow people to attach their automation. The extension points are there so that automation tasks that are specific to projects don't have to all live in a single gigantic automation script (out of my control) but can be nicely separated for each project.
Mind that we are not putting automation in the build script. The build script is there to organize the process not to be the place where you put your commands.
Now, I don't even want to start thinking how I would do this with Maven... probably would dump this work on somebody else...
If you really need something insanely custom, you can just write a maven plugin (it's actually really easy to do so).
I'd be hard-pressed to see how you can do that with gradle.
Here is a small example I just googled: http://mrhaki.blogspot.com/2013/05/gradle-goodness-extending...
The main idea is to use the language to create another language layer, better suited to the task at hand.
Appreciate the feedback.
Its a bit unfortunate that a lot of Gradle tutorials start with the syntactic sugar.
Until then I had pretty much a hate relationship with Maven. Mostly tests failed due to improper excluded transitive dependencies. Once writing your own plugin you understand depenendency management and class-loading much better.
That, and it sounds like the parent doesn’t understand POM inheritance and archetypes.
> To me this is a counter-argument. When I need to understand project configuration I expect structure, not behaviour.
Agreed. Every time I see a build system that lets you write arbitrary code, I shy away from it. Builds should be standardized, not a mess of custom code. Maven lets you write plugins for the case when you need to do something truly custom, but in my experience that's vanishingly rare.
My takeaway from using both maven and gradle is that I would really love to have a build system with emphasis on allowing custom arbitrary code, but only within guiderails that force them into a shape that is easily factored into a plugin if you layer decide to do so. Maybe even with a defined reverse path for unboxing existing plugins back into their inline form (for when you absolutely need a fix/unexpected customization, but can't commit to fully maintaining the plugin)
Completely disagree. Off-the-shelf plugins behave in defined and documented ways, and understanding a build that uses a particular plugin just means reading that plugin's documentation. When you allow arbitrary code in the build file, it means you have to puzzle out what the author was trying to do, for each and every case where you see unusual arbitrary code. You also get into situations where there's more than one way to accomplish many tasks, so you have to learn all of them instead of relying on a standard built-in method.
Sure, any random maven user in your organization can write a plugin to add undocumented behavior, but I rarely see that happen in practice. Even though writing a maven plugin actually is pretty easy, there's an implied barrier both in setting up a plugin project and writing it, as well as publishing it in a way that that a consuming build can use it. So you end up with it being rare to write a custom plugin. There are very few things you can't do with off-the-shelf maven plugins, and arguably I'd rather be restricted and be unable to do some things than have to deal with the mess that arbitrary code creates. I've seen it time and time again with people who try to introduce sbt into my org, and it makes me want to scream.
Maven is declarative. Yes, it has some boilerplate, it may not look nice (I'm not a big fan of XML), but the main advantage for me is that I can read it easily and understand what it does. Because it just declares tasks. Similar to any other XML file or JSON or .properties. You just read it.
Reading Gradle, on the other hand, is more like reading a program. You can't just read it, you have to interpret it, you need to put effort in order to understand what this program does.
Yes, Gradle may be more flexible and less boilerplate, but personally I don't want to have another program (in another language) to just build my project.
>Maven...you have to write your own plugin. Not nice.
>If I wanted to change one thing in Gradle it would be for the Gradle project to focus on debugging
You've just explained why I have no interest in Gradle. You're writing one off, undocumented, anonymous plugins, in a weird domain specific language, with debugging tools that aren't very good.
Worse, when the Gradle guru decides to leave the company for greener pastures, everyone else is left with a mess trying to figure out WTH that person was doing in all the build files.
artifactory { publish { repository { repoKey = version.endsWith('-SNAPSHOT') ? 'libs-snapshot-local' : 'libs-release-local' } } }
In Gradle, this is immediately understandable to everybody. You can't do this in Maven without plugin support or some very protracted constructs.
Sprinkling those small bits makes the Gradle useful. We are not talking about making a beast of a build system, just provide you with an ability to solve your problems.
You may not want or like to learn another tool and that's fine. But don't think that just because you don't see the reason it means its not useful for other people.
>I really find it difficult to figure out why things fail even though I have almost 20 years of experience
>In Gradle, this is immediately understandable to everybody
You pulled an easy example to defend Gradle for the exact problem you just complained about.
>You may not want or like to learn another tool and that's fine.
It's not about like or want, it's about having the wisdom to recognize a poor solution before investing time in adopting it. OP fell for the hype. Maven works fine for me, thanks.
To the creator of these tools: Try to remember that what you are creating is a support tool. A second act to the main development language. It is better if it is not general purpose, it is better if it is not turing complete, it is better if it is less powerful.
Regarding the execution speed, IDEs not able to autocomplete, no guarantees about the format that IDEs can easily parse, and it often breaks between releases.
Then to compensate for lack of speed, you need to have a running daemon and an in-memory build cache.
If you wrote a custom gradle build script (or indeed, any other custom scripts), then you cannot be sure when that generation of sources is going to happen.
Maven forcing you to design your build around maven's fairly strict set of phases (and build lifecycle) means you confirm, and any tooling can compose as long as they also conform.
A programming tool has a certain affordance. It talks to the programmer and says "hey - here is how you use me".
Maven says: Hello. I am a just a configuration file. Just put your project's name and the dependencies in here and the defaults will take of the rest. You remember Ant? Please don't turn this into a big mess.
Gradle says: Oi buddy. I am a full-fledged scripting language. Anything Maven can do? I can do the same times 10. Plus my syntax is tight - just look at this &#€#"&/+=+++ << '${.&%&}' ... Not even a semi-colon.
You see the cool kids over there? They all have advanced build scripts with code generation, variable substitution, dsls, database migration, performance daemons. You need this too (unless you are one of those corporate XML-dorks ... yuck XML, it sucks, no one uses XML anymore).
And because we are so cool, we change all the time so you better keep these random files we generate for you under version control: gradle/*, gradlew, gradlew.bat besides your actual build script.
After a long time fighting between what I liked best between Maven or Gradle I realized that the only thing I really needed was the parts to build the Java code. Things like Docker, Java code generation and a variety of other tasks you often do for releases, I finally figured out what I needed to do:
Make sure everyone on the team can run Bash. From there I could stop worrying about Windows users - Git Bash or Babun or some flavor of Cygwin. And things are even better with Linux Subsystem for Windows.
Now we simply have bash scripts that do all our one-offs that often were nightmares to accomplish in Gradle or Maven. Whether it is JAXB or Docker integration every maven/gradle plugin I ever used was 10 times harder than just bash.
When you start taking out all the other stuff it started to become clear that Gradle was winning out. With gradle I can build my code and get a variable that represents every JAR I need for a deployment into a Dockerfile containing folder. So that's where I personally ended up liking Gradle more - because I stopped depending on Maven and Gradle to do the other kitchen sink tasks.
If you use Maven for your project, then I expect that if I run `mvn clean install` then it will build the project.
I think that is the whole point of standard build tools: that you can switch projects easily, run the same command and get the same result, and not waste time learning custom scripts for each project.
Project to another, just run "build.fsx" with the desired target -- it will self-download necessary tools and packages -- and you'll get a straightforward standard build. If you want to see the logic, just open it up: build scripts are standard F# scripts
Because you're using first-class scripting you get full language power, full component and package management power, full abstraction power, aided by a set of utility methods and tools. At any point things get hairy you can wrap them into a lib, or a full program, with no domain impedance. It's standard code, as learnable as the rest of the F# proj, no voodoo, no black boxes.
Heck, you can also load your project and use its libraries during the build. I find myself often using FAKE components in my main solutions to take advantage of its interop and publishing...
Lower level recipes are either one liners that invoke a project specific builder (ex: mvn package) or they invoke a separate bash script.
For complicated polyglot projects it works great as each component can use its own native tooling yet the top level is consistent.
This is the opposite of my experience. Trying to get consistent behaviour out of bash scripts is a nightmare (every nontrivial bash script has an undocumented dependency on a particular version of some system tool, every time you try to build on a new machine you're rolling the dice). Maven plugins are versioned and (usually) tested, have sane defaults, and will Just Work (provided you're willing to roll with their defaults rather than fighting it).
why not on copies of known-good machines? vm boxes with the same definitions would alleviate almost all of that 'roll the dice' aspect, no?
Your build works in a vm of RHEL 5 with packages x and y installed, ok, now you need to add a new build step that depends on something that's only available for RHEL 7. So you run the VM generate script (if you can even find it a year or two later) and the build doesn't work. Now you get the same fun debugging experience. Standardising which OS and package installs every developer uses (whether with a VM or just with something like Puppet) means you get to roll the dice less often, but it's still a dice roll whenever you want to change something.
And running all your builds in VMs has a cost in terms of performance but more importantly in terms of comprehensibility. If my build is just Maven I can run it in my IDE, then I can push one button differently and run it in my IDE with a debugger attached. If the build is running slowly and I'm not sure why, I can push one other button and now I'm running it with a profiler. Some of that is just an argument for having your build tooling written in the same language you're using for development (if I was working in e.g. Python, I'd be more likely to have a Python IDE and a Python profiler to hand, so would want to use a Python build tool), but even if you're used to working in Bash and C I'm not sure the VM advocates have good answers to how you debug something running in a VM as yet.
Hm - I don't know about that. The biggest issue I found with Gradle was that it offered unlimited flexibility, and inevitably, somebody would mickey mouse sorcerer's apprentice the thing and stick something into the build script that didn't belong there. Although with enough effort you can do that in Maven, too, by the time you get to the point where you understand how to do that, you know better. I can't even imagine what sort of nightmares somebody with the even-more unlimited flexibility of a bash script could conjure up.
Even as a 25 year vi/vim user, I've succumbed to depending on IDEs more and more and I believe that any proposed build system has to easily support deep/rich integration with IDEs. This is theoretically impossible if the build system is effectively a general Turing-complete programming language. At best you will be able to provide brittle integration or else the IDE will have to dumbly treat the entire build process as a black box.
Gradle enables this as a first-class citizen by providing a "Tooling API": https://docs.gradle.org/current/userguide/embedding.html
I know that Buildship, the Eclipse plugin that integrates Gradle, is actually worked on pretty heavily by Gradle contributors. I don't know about IDEA but that's the IDE that I use for Java development and the Gradle integration is very, very good.
Of course, the IDE can do it if you limit yourself to a subset that is not fully Turing complete, which is what IDEA does today.
The issue a lot of people have with Gradle is that this subset is not well defined, and it's very easy to do something that works but will get the tools to have problems, and debugging such issues is not particularly entertaining.
IDE cannot get the graph by analyzing scripts, but it does not need to: instead it can ask Gradle for the model. The model itself has nothing to do with Turing completeness.
Something I just thought of but have yet to try, could we have a build script generate the project structure that an IDE can use and get the best of both worlds? Looking through my VS project files, 50% of it is stuff the make file would be doing, the other 50% is just a list of files and dependencies.
So let's say we generated these project files from a makefile, the IDE could still treat the build process as a black box but as part of executing that black box it gets all the information it needs for intellisense and debugging.
But also, both Maven and Gradle work by first building up a model of what the build will look like, then executing it. Gradle is complicated by the fact that it's much easier to run custom code that modifies the build during the build (which is why I don't like it so much), but IDEs can still inspect the build model to work out what's going on.
So does this work as I suggested? Can you do whatever you want in you're build script and output something usable for eclipse?
> But also, both Maven and Gradle work by first building up a model of what the build will look like, then executing it
Isn't that pretty much what make has always done? You define outputs and what dependencies are needed for those outputs and make will work out the path to get there.
Maven and Gradle plugins also have the advantage that the IDE can be taught to understand what they do -- a Makefile can run a compiler, but the IDE can decide to use incremental compilation and update a debug target on-the-fly, as it can see what the project build is doing and replicate the results.
On the subject of generating project files, pretty much everyone I know prefers to use the IDE's tooling for importing build files rather than the build tool's tooling for generating IDE files. And that's probably a self-reinforcing behaviour as far as tooling quality goes.
It's gotten a lot better because there are many examples now accessible with Google. Overall I'm pretty happy with maven now. It works.
That is the main reason I am sticking with Maven (where practical). Examples, documentation, and answered questions are plentiful.
"Configuring plugins to fire at the right point in the build cycle (e.g., package vs. install) comes lightly to mind."
The official documentation has that covered well: https://maven.apache.org/guides/introduction/introduction-to...
An example they give is:
...
<plugin>
<groupId>com.mycompany.example</groupId>
<artifactId>display-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<phase>process-test-resources</phase>
<goals>
<goal>time</goal>
</goals>
</execution>
</executions>
</plugin>
...
i.e. plugin "display-maven-plugin" should execute its "time" goal during the "process-test-resources" phase.IIUC, there are no fixed phases in Gradle, so this sort of thing is done with task dependencies.
Usually this is a red flag for me. People's apps really aren't that different, if you're doing some weird customization its going to be a nightmare for the next guy to maintain.
Fwiw, the need to learn groovy is a bit of a red herring because it doesn't help that much: I know groovy very well but I still find gradle confusing and have to google everything. My favourite build system of all time was gant, also based on groovy but a simple wrapper for ant. It had zero magic, was just a systematic wrapping of the ant API which itself was just a bunch of well documented useful build utilities. I found it very easy to use. The contrast between two build systems both based on the same dynamic language was very stark to me.
Did I missed the sarcasm tone? I've never experienced slower build systems then those used in Java world, like Gradle for example.
It is not time for coffee, rather lunch.
At Addepar a few years ago, I transitioned a several-100-k LOC Java codebase from Gradle to buck. It was a fairly complex build including Jooq code gen.
Build times became much faster and flaky CI issues went away.
Today, I’d try Bazel— Buck only exists because ex-Google FB engineers had to recreate Googles amazing Blaze build system. Google has since released much of the original as open source, renaming to Bazel.
Maven and Gradle are both flaky, slow, and annoying to configure, in my experience.
I'd like to use Bazel (or even Buck) these days, but no bandwidth given other projects. I always felt modules were a mistake compared to a single large tree, which Bazel does elegantly. Sadly my familiarity with Gradle is much more than Blaze these days, and I'm too exhausted to do it just for fun. I think there is also a bit of Stockholm syndrome at play, since I convinced myself it was not too bad back at Addepar.
Not hating on the Gradle setup at all — it made sense at the time.
I also like the tree approach, it feels clean. Plus the hash cache to avoid rebuilding things when nothing’s changed—faster and more robust than tools that use mod time.
I’m going to try Bazel soon at Dynasty. (We’re on ant now lol... but with very simple build, so it isn’t too bad.) I’ll let you know how it goes.
LMK if you’re ever in socal!
I should add that I am no Java guru, so for me this is an important consideration. Still, the real Java gurus at my company are split on the issue. We have projects on both build systems.
[1] https://stackoverflow.com/questions/11677572/dealing-with-xe...
I know that this isn't entirely Java's fault, as that second jar shouldn't have been there to begin with, but it's pretty ridiculous that two versions of the same dependency can be loaded and in use at the same time.
The introduction of modules might actually make version hell even worse: https://blog.codefx.org/java/dev/will-there-be-module-hell/
Which makes this backlash entirely unsurprising because the mainstream foundational tools will eventually end up reflecting the culture of the programming language, given enough development time and an unconstrained mandate seeking to keep the wider community happy.
It wouldn't be so bad, if there was consistent, thorough documentation available, including for the various plugins that people put together and shared.
But where things stand it's entirely too easy to have builds that only a few people really understand as they read through the build definitions.
At their heart, builds are simple. Pull in dependencies. Process templated files. Copy files. Run a test suite. Compile/link/etc.
Yet, with large projects there always seems to be something unique that requires a custom plugin or a day and a half trying to figure out how to move a particular step, or heaven forbid running the same kind of step twice at different stages.
The way I see it - if you have to spend a day figuring out how to do anything with your build system, you have eliminated the benefit of said build system. You might as well build a shell script in half an hour that does what you want it to do. If someone can come along later and say "that's easy with ant/maven/gradle/buildr/make/etc." and can throw it together quickly along with a quick doc explaining the steps, then fantastic.
I think time is better spent writing software than struggling through learning a build environment.
With complex Makefiles, at least you have the sources and can try to understand the logic. With Gradle, it's all tons of compiled java code.
We have Maven builds that can take an hour and Gradle drops that down to minutes (or less than a minute).
Xcode/iOS: we published CocoaPods. It was pain to get to work reliably on laptops/Jenkins b/c of upgrades to Ruby, but if you lock yourself to one Ruby version, and lock the Gemfile, it's mostly OK. At least it's pretty obvious when/why it doesn't work.
Gradle: it's such a mess. I don't even know where to start. I don't know Maven, but Gradle error warnings are the worst. 4 lines, tells you nothing, with turned on debugging the output tells you nothing, while e.g.: in reality you don't have the child-repo checked out because you forgot to `git submodule update --init`.
Getting Gradle to work on a laptops across the team of 4 was a pain. We had cases where two people with the same AS version and the same gradle version couldn't import the project into AS. Our instructions are - after 'git pull' make sure you do 'git clean -dfx' and re-import the project.
Personally 50% of time when I start Android Studio I have some critical exception of some sort, each time from Gradle window. There's this, plus gradle wrapper. I pretty much never worked for me. This is meant to solve problems with always getting you the right Gradle version, but this is sci-fi. I believed it's just me who uses SDKMan to be able to fetch the right gradle which our project depends on, but others around do too. Otherwise it's a pain in the ass -- and `brew` doesn't have the gradle's we need, because they migrated to the very latest one, when in production you'd rather have older version for an extended period of support time.
If you, just like us, have a project which has 3+ years of scar tissue and it has to integrate with javanet, picasso, okhttp, volley etc. and you have several people who fetch and integrate your code in their apps--I feel really, really sorry for you.
DSL is all right, and maybe Groovy has some unique features, but I never discovered them. This is Java language that isn't Java, or some dialect of Java-like language aimed to make Java 3% better, but to be honest I don't hear people writing Groovy for fun and pleasure. So to me it was a cost of learning basic stuff just to be able to automate simple stuff in Android build system (when we run ./jenkins.sh --local, we want our Android functional tests to hit local dev environment). Of course in XCode it works 100% better and took 20% of time to do, since Apple owns XCode and made it look better: go to project settings, drop in the path to post/pre built scripts to customize your flow, and in a language you already know. Google, who subleases Android Studio from JetBrains, had no choice and made Android ecosystem look like a Frankenstein.
It was common at local JUGs almost 10 years ago, when there was lot of hype around Groovy and Grails.
Even at JSF Days 2007 there were some talks about adding Groovy support to JEE as a means to write session and entity beans.
Nowadays Gradle is the only thing keeping Groovy from completely fading away.
That and Grails maintenance projects.
Everything was done in scripts (predictable when you offer nothing else) and it was so misconfigured and esoteric that nobody happened to notice that it was reaching out to the wrong server for artifacts....WHOOPS!
Dependency management was awful and g'luck even getting something as simple as a dependency tree out of the system. Oh and it appears to lack any mindshare so nobody appears to be improving it and g'luck if you happen to need some help with it.
The problem with buildr is that it's a ruby based build system for Java. Why? Who wants to write their Java build code in Ruby, and why can't they just go work in a RoR company already?
Builds shouldn't be interesting, artisanal, or occupy a lot of your time. They should just produce the artifacts and move on.
Since the switch to Maven I find myself worrying about the builds a lot less and actually working on code instead.
Sure, it’s an unforgiving ambition and many other systems fell trying, but it was their choice.
With Gradle the blame falls squarely at the feet of plugins, and you’re on your own (which has always been the case anyway)
I find all the cases can be coerced, and it's not even hard. Some users seem to insist on customization for its own sake, "oh I absolutely must call my source folder source rather than src", but Maven does the right thing by ignoring those users, with the result that you can pick up any project that uses Maven and be immediately productive.
PhantomJS - 1 min
PhantomJS + Coverage - 5 min
HTMLUnit - 10 min
HTMLUnit + Coverage - 25min
Coverage required one set of additional flags, PhantomJS required another. But we could only replace the test runner string, not add to it. We couldn't drop HTMLUnit altogether, as our company mandated CI system would not make it easy to install PhantomJS. And tests were still new to many team members so if we defaulted coverage on, nobody would run it with those times. So ideally we'd have mvn test -Pphantomjs -Pcoverage if we wanted phantom with coverage. But actually we had to do mvn test -P{phantomCoverage,htmlUnitCoverage,phantomNoCoverage,htmlUJnitNoCoverage} which meant copy pasting a substantial chunk of our pom file 4 times to change two lines of it, rather than a base case with two overrides.
There are maven and gradle plugins for just about everything and it is tempting to use them but your build files will quickly become very complex and unmaneagable. Worse, a lot of these plugins are poorly documented, if at all, and tend to not add a lot of value. And that's before you start hitting the corner cases, bugs, and left as an exercise to the reader type stuff that just requires copy pasting bits of half working stuff from all over stackoverflow. Once you go down that path, maven and gradle are equally frustrating.
Solution in both cases is to simply not even try to do everyting in a build file and fall back to simple alternatives: use a small run.sh instead of trying to get some unholy maven xml blob or gradle script to fork off a jvm just right; really a script is a lot easier to write, maintain and it does exactly what it says. And even better, you get to reuse it in the last line of your Dockerfile where you start your software.
Speaking of Docker, packaging things up as a docker image (or rpm, or whatever), is also definitely out of scope for build files. I'm sure it is possible to do with some unholy combination of craptastic plugins in both; but why even bother? Just use a Dockerfile and docker build for that. They are nice and simple and any decent devops person knows how to work with them.
Actually, in some of my projects the whole build is a docker build. It's great! I let it take care of downloading the jdk, gradle/mvn, and then let it download the world as a separate docker layer before letting it build the software. Most of the builds it jumps straight to building software unless I touch the build file. Also makes for really simple CI: all your build server needs to know is docker. Also people can build and run your software without installing anything else than docker (and git, and maybe a few other bits and pieces). Add some docker compose and you can do integration tests (another thing that you should not attempt from a build file).
Also, with both maven and gradle: avoid multi module setups. They slow down your builds, multiply your complexity, and all for the illusion of not quite having reusable components. Once you have two modules, you will need three, and four, etc. There's no end to it. Just don't. Usually you are just looking at Conway's law in action: it rarely buys you anything but misery and slow builds. KISS & YAGNI.
Including your source code inside your Docker image unnecessarily inflates the size of your Docker image, including (seemingly from your description) the entire version control history. For big projects (especially big projects where junior engineers didn't get the memo that you shouldn't check binary files into Git...), this can easily add hundreds of megabytes if not gigabytes to the size of the image.
Docker isn't a build system (which is responsible for the creation of both inflated-but-easily-debuggable as well as optimized-for-production), it's a combined packaging system and runtime.
I think the only good thing from Maven was the directory structure layout (src/{main,test}/{java,scala,groovy,resources}) which has carried on to other build system. I didn't like that at first too, but I've since come around.
SBT does a lot of things really nicely if you're in the Scala world. `sbt console` is wonderful. That being said, never take up the sbt source code and try to hack on it. That's a disgusting mess under there. Also the whole task dependency/macro structure can get pretty awful when you want to run tasks with multiple configurations in one go. I don't think it's possible. The sbt-native-packager works really well, but once again, that source code will make you want to throw up. At least back in like 2014 it did. Who knows. Maybe it's gotten better.
Honestly all the build systems are kinda terrible, but I'd take groovy or sbt any day over maven. I don't see how anyone likes it. Trying to figure anything out in Maven was just miserable.
Ant and Maven just work on IDEs and on Netbean's case, they are even the official project format.
Maven it's only in your comfort zone. Bye.
(And really, why would you know Groovy these days? Is it even used for anything outside of Gradle anymore?)
There is a whole world out there.
Nowadays all of that is a synonym to Grails maintenance projects.
I don't want to get too deeply into language wars, but Java, Python and Scala all have "killer" features, things they offer that no other language does - you may not agree with them but there are reasons someone might pick that language over every other language in the world. Whereas Gradle just felt like "it's a Python/Ruby/Perl-like language that runs on the JVM" - if there's something Gradle's actually the best at, their communication failed to convey it.
I assume you meant groovy there. But yes, I remember actually posting exactly that view on here several years ago! I felt that if I was going to learn a JVM language, why not invest that effort in something that was more generally applicable or has broader benefits, and kill two birds with one stone? I deep dived into Scala, and looked into Jython, JRuby and other JVM languages. What I came up against every time was that each of those languages seemed to have significant impedance mismatch with the JVM. I found I really wanted something as close to Java as I could get but with a scripting programming style so that I could do quick experimental coding. For me that is where groovy beat everything else. As I used it more I started building larger projects with it because it scales up pretty well. So I often build about 70% of a project in groovy and then do 30% in Java where I want the real static typing (interfaces, domain model, etc.).
This seamless interoperabiity with Java/JVM is a fairly subtle selling point especially since every JVM language will say they do it. It's only when you try to use them in complex projects and see them fall down that you realise that groovy is the only language that is philosophically trying to be close to Java (I'm fairly keen to try Kotlin to see how that holds up on this count).
Interesting - can you say anything more concrete/specific? E.g. with Scala I've gradually drifted away from a Java-like style over the years, but I found it was very possible to write in a close-to-Java style (e.g. using the Java collections rather than Scala ones, using mutable vars and null and subclasses) when that was what I wanted.
I don't see a reason to NOT use groovy in any java project. It makes java so much nicer.