Why must gradle be so frustrating?

Some six years ago I first came into contact with gradle, I saw a demo that got me instantly hooked. Clean syntax, intuitive concepts, small and simple to understand build scripts.

Since then I have spent countless hours trying to wrestle with gradle to make it work on several projects, and everytime I have come to utterly loathe it. Yes, Ant sucked big time, but at least you could invest some time into studying the build scripts and slowly figure out why and how something worked. With gradle? Good luck.

All the trivial concepts are documented, and well documented even. But everything you might want to do beyond the trivial to actually harness the full potential of gradle, it is just impossible to figure out. No documentation, and google/stackoverflow are wastelands with tumbleweeds flying by. The gradle developers seem to be so busy implementing the next cool feature that they simply forget about actually explaining anything they have done so far to the users.

Right now I am struggling just getting my standard output from a JavaExec task to show up. Somehow the task wonā€™t do what it should, and I cannot for the life of me get the standard output from the java command to show up, so I have no way of debugging this. I am googling, reading the documentation, begging, pleading, anything to get the output. But no, gradle refuses, and it wonā€™t even tell you why. It just gives you the silent treatment as if you had somehow mistreated it.

I absolutely loathe spending 3+ hours one something so utterly trivial, I want to do some actual work, solve some actual problems, I donā€™t want to wrestle with gradle anymore. Please developers, fix this pile of ****. Make it usable and not frustrating. It has such great potential, but not if you keep piling on feature after feature and forget about actually making a useful product.

6 Likes

Iā€™m kind of understanding the feeling here. Not really looking to go back to Ant, but Iā€™ve had my share of trouble for sure, sometimes with seemingly simple stuff. In fact I came here to post about one of those today.

For me part of the confusion is the amount of rapid change - now thereā€™s so many versions, so many different ways of doing things. Normally Iā€™d be fine with that because that mostly hurts those who exclusively rely on Stack Overflow copy/pastes and such (I prefer to go to documentation first), but in Gradleā€™s case the documentation is a large book and not always the easiest to follow. So I do admit resorting to the ā€œDuckDuckGo searchā€ for an answer more than Iā€™d prefer. You only have so much time available.

I wonā€™t go so far as to call it a pile (except at the moments when I have problems and call it worse :sweat_smile:), but I can definitely relate. I try to focus on the times that it works well.

2 Likes

The standard output should be output to theā€¦ standard output, otherwise you may change it to something else:
https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:standardOutput

You can also check how to control logging:
https://docs.gradle.org/current/userguide/command_line_interface.html#sec:command_line_logging

or you may debug your Java application run with JavaExec task (see debugOptions):
https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html

I know that it should be output to the standard output, but the fact is that it doesnā€™t. I ran into this issue years ago as well, back then I concluded something about the java process being forked (which gradle enforces, this is not my choice) and gradle not dealing with this properly, so that the output gets dropped.

You can even create you own stream and use that in your gradle task, you will see it ends up completely empty.

It has been like this for years and nobody has cared to fix it because the developers are too busy piling on new exciting features.

Did you open or find a bug report about this?

Simplifying things to the most basic of levels, I have created this Gradle project:

plugins {
    id 'java-library'
}

group = 'org.testing'
version = '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

tasks.register("execute", JavaExec) {
    dependsOn assemble
    classpath = sourceSets.main.runtimeClasspath
    main = 'org.testing.StandardOutputter'
    args = [ 'One', 'Two', 'Three' ]
}

wrapper {
    gradleVersion = '6.9'
}

and this Java class:

package org.testing;

public class StandardOutputter {
    public static void main(String[] args) {
        System.out.println("Standard-Output");
        for (String arg : args) {
            System.out.println(">> " + arg);
        }
        System.out.println("DONE");
    }
}

I can execute this project as follows:

$ ./gradlew clean execute 

> Task :execute
Standard-Output
>> One
>> Two
>> Three
DONE

BUILD SUCCESSFUL in 1s
4 actionable tasks: 4 executed

This is doing what I expect. So my question is: What are you doing differently?

Cheers,
Chris

2 Likes

@cornergraf

I know your feeling yes gradle is changing rapidly but however I definitely wont call it what you call ?

I have designed complex build automation CI/CD pipelines starting from build to deploy on big enterprise systems involving kubernetes, openshift on different cloud providers to you name what, using gradle.

The amount of automation that I have done with gradle is incredible, no build tool is capable of getting to what gradle can do or achieve. (This is my experience).

Instead of struggling to find a solution and getting frustrated

Next time please do the following

  1. Instead of posting your frustration please post your problem in this forum and ask for a solution

  2. While posting make sure to provide a sample code (Build script)

  3. what are you trying to achieve with gradle and what problem your facing

  4. What version of gradle your are using?

  5. What is your java version?

  6. What is your OS name , version ectā€¦

There are loads of people in this community who can provide guidance and help

I came across a post that explained the problem of Gradle wasnā€™t Gradle but Groovy. Switching to Kotlin has helped med out quite a lot. The switch was also prone with the ā€˜Gradleā€™ style frustration, as I had trouble finding documentation or books that helped me, but the Gradle Kotlin Primer is much better now, and the people on the forum/stackoverflow are still helpful with specific problems.

With Groovy we donā€™t have any ā€˜tooltipā€™ helping us with what the code is actually doing, no ā€˜javadocā€™, no search for alternative methods, no info on wtf. the object Iā€™m working on is. I have to a print o.getClass() just to figure out what the object is. Kotlins syntax is not as nice, but even if you donā€™t know Kotlin, itā€™ll still help you out as IntelliJ can help you build the code, and learn what is going on. Gradle API documentation also become a bit easier to digest, as suddenly its become feasible to look up classes without spending so much time it isnā€™t worth it.

https://docs.gradle.org/current/userguide/kotlin_dsl.html

2 Likes

Sorry for the lack of response. I was hoping to look into some of the feedback you have given and troubleshoot the problem with the missing JavaExec output, but I have been very busy.

I was actually planning on doing some of that today, but once again gradle has delayed me. A simple thing - extracting a zip file generated by an application distZip task - is taking me hours to figure out. Because while I know it is completely possible, the exact syntax I need to write this in eludes me, and the documentation is once again no help.

The documentation explains each individual concept, but it never puts them together. So for example, I know that it is possible to make a Copy/Sync task use the output of another task. But it doesnā€™t explain how to do that, what the conditions & caveats are, and how to troubleshoot. At this rate I am lucky if I can get this done today, and looking into the missing JavaExec output will have to wait. Again.

ā€¦and I finally made it work:

task installCodegenApplication(type: Sync) {
   def extractDir = "${buildDir}/codegen"

   from {
      project(libraries.codegen).distZip.outputs.collect {zipTree(it.files.getFiles()[0])}
   }
   into extractDir
}

Clear as mud really. Why I cannot just specify the distZip task directly I cannot understand. This is exactly one of the reasons why I find gradle so utterly maddeningly frustrating. I should not have to go to such lengths to unzip the output of the distZip task. Seriously.

1 Like

Doing this all the time. And:

  println it.metaClass.metaMethods*.name.sort().unique()
  println it.metaClass.methods*.name.sort().unique()
  println it.properties.entrySet()*.toString().sort().toString().replaceAll(", ","\n")
  println it.properties.toString()
  println it.dump()

Community Idea allows to step through Gradle build scripts but above is faster to figure out whatā€™s happeningā€¦

1 Like

I donā€™t know what is it you are trying to achieve, but trying to strong-arm Gradle to do something the way youā€™ve done it with ANT or Maven is almost always the wrong way to approach it in Gradle.

Gradle has itā€™s own philosophy and and own way of doing stuff that is different and going against itā€™s grain is mostly where Iā€™ve found that people get in trouble with it. Most of the Gradle horror stories Iā€™ve encountered have been caused by people gravely misunderstanding the utility of using a programming language to describe build process in Gradle and hacking all sorts of actions into the Gradle build file and making an utter mess of things in the process.

Usually trying to extract a build artifact after Gradle has packaged it up is a code smell (or a build smell if you will) and in most times there is probably much better way or a better build stage to achieve the same goals.

For example, you are trying to unzip a generated artifact into a local folder within Gradle, but why do you need to do that? Gradle already has a folder for generated source code in the build that is actually also puts in the classpath during the build. If you need to be able to view the generated content, you should use that location instead.

When one subscribes to the way Gradle likes to work, most of the custom build steps end up being almost effortless and build file itself ends up being much more declarative, clean and readable than with any of the alternatives out there.

1 Like