Debugging build.gradle with Eclipse

It would be nice if we could debug our Gradle scripts in Eclipse, using all the goodies that development environment provides: syntax highlighting, code auto-completion, debugging support, etc.

And it would be even better if I could create .groovy files with all the goodies Eclipse can offer and be able to use them in our build.gradle script with little or no effort. I try hard to avoid spagueti code, and being able to distribute part of my scrips in isolated Groovy classes could help.

The good news is that we can do all of this. The bad news? There are some ugly limitations with which we’ll have to live -at least as of Eclipse Luna 4.4.2 with plugins Groovy-Eclipse 2.9.2 and Gradle IDE 3.6.4.

Before we begin

Before starting, make sure you have the following plugins installed:

  • Gradle Plugin for Eclipse.
  • Groovy Plugin for Eclipse.

Properly configured, they’ll provide us with syntax highlighting, code completion and debugging support.

Debugging a simple Gradle Script

Let’s create a debug-gradle-in-eclipse directory for the build.gradle file we want to debug and the Eclipse project we will use to debug it –which can be at the same time the Eclipse project corresponding to the gradle project.

The following build.gradle file will help us understand how and what can be debugged with Eclipse and Gradle:

apply plugin: 'eclipse'

class ClassInBuildGradle {
   ClassInBuildGradle() {
      println '*BUT* you *CAN* place a breakpoint in a class method ;)'
   }
}

void scriptFunction() {
   println 'You can\'t place a breakpoint in a script function :('
}

task sayHello << {
   def configMessage = 'You can\'t place a breakpoint in a task :('
   println configMessage
   scriptFunction()
   new ClassInBuildGradle()
}

Here, we have defined a class (ClassInBuildGradle) and a function, scriptFunction, to show where you can place a breakpoint –or not.

To make things easier, we have added the eclipse plugin to generate an Eclipse project for this gradle build file. To create it, execute

   gradle eclipse

Now open Eclipse, and import the project with File|Import… and then General|Existing Projects into Workspace, choosing the project directory.

Open build.gradle in Eclipse, and you’ll notice you’ve got syntax highlighting, thanks to the Groovy/Gradle plugins.

Figura_1

Now, before adding breakpoints you need to tell Eclipse this is a Groovy project: just choose the Configure|Convert to Groovy Project from the project contextual menu (I did this in the Navigator view, selecting the project itself and right-clicking the mouse).

Once this is done, you can add breakpoints: add them at line 5 (inside a class method), line 10 (inside a script function) and line 14 (inside a task), as seen in the figure above.

Now, create a remote Eclipse debug configuration, going to Run|Debug Configurations…, and then choosing Remote Java Application. Call it debug-gradle-in-eclipse, build.gradle, and set the Port to 5005 and check the Allow termination of remote VM option. Save the configuration with Apply and then Close -do not launch it by clicking Debug.

Figura_2

Note that port 5005 is the port gradle expects a debugger to use by default.

I recommend you to store this debug configuration in your project directory, by going to the Common tab and then selecting the directory in Shared file: I stored it in the eclipse-launchers subdirectory, which is where I place this and other Eclipse launch configurations.

Next, run the sayHello gradle task from the command line as follows:

gradle sayHello –Dorg.gradle.debug=true

This will start the task for debugging, and Gradle will wait for a debugger to connect to port 5005. Do it by opening our debug configuratio and clicking Debug. Eclipse will start debugging, showing you something like this:

Figura_3

You’ll notice several things going on here.

First, Eclipse will not open the build.gradle file. This is a limitation, but you can circumvent it here by taking a look at the stack trace, where you can see that source line is number 5. So, you can open build.gradle and go to that line.

Or you can click in the Breakpoints view to jump to the source line. This is my preferred way, but you need to have build.gradle open in the editor. The Variables view is fully operative, and it will allow you to check and modify variable values.

Next, you’ll notice that the other breakpoints…they are ignored. If you take a look at the Breakpoints view you’ll see they are reported as being placed in build, which is a way to say they are located in code belonging to the script object.

No, you can’t place breakpoints in the code belonging to the script code Gradle generates under the hood, you can only place them in code for other classes defined in the script or groovy classes used by the script –more on that later. Therefore, moving as much code as possible to classes can be a good idea.

Besides, by debugging step by step you’ll be able to see the values of variables in the stack, with the same names you gave them in the source code, and you’ll quickly find the task objects, the project object, etc. Surprisingly, this can be very useful.

Yeah, ugly, but might be workable and better than debugging without a debugger. I hope next version of the Eclipse Groovy plugin solves this issue. I think we are not that far from there, given what we already have.

Important
You must execute the gradle task first, and only then launch the Eclipse debugger. If you launch the debugger first, you’ll get an error, as shown below.

Figura_4

Moving part of the Gradle Script to separate Groovy classes

If your script starts to grow, you will probably want to create your own Groovy classes to avoid spaguetti code. Gradle comes with support for this out of the box, by allowing you to write Groovy code in buildSrc. The default source directory will be in buildSrc/main/groovy.

Let’s create a Greeter.groovy class in buildSrc/main/groovy, as follows:

class Greeter {
   private String name
   Greeter(String name) {
      this.name = name
   }

   void greet() {
      def message='Greeter Groovy class greeting you, '+ this.name+'!'
      println message
   }
}

Now, we’ll use it in build.gradle by adding a line that uses it, such as

new Greeter().greet()

You’ll need to configure Gradle so that it can process these Groovy files, adding the following lines at the beginning of build.gradle:

repositories {
   mavenCentral()
}

apply plugin: 'groovy'

dependencies {
   compile gradleApi()
   compile localGroovy()
}

The last step is to help Eclipse show the source code for these groovy files when a breakpoint is hit. Just edit the debug configuration, going to the Source tab in it, and adding an entry to the Source Lookup Path: click Add…, choose the Workspace Folder option and then choose the debug-gradle-in-eclipse project and its buildSrc/main/groovy directory.

Figura_5

Now, run the gradle sayHello task from the command line as explained before, execute the Eclipse debugging configuration, and when a breakpoint in Greeter.groovy is hit the source file will open automatically.

Now, that’s much better than what we’ve got for the build.gradle script, and very helpful to create complex scripts or even Gradle plugins.

Now that we are at it…

I recommend you enable several options by using the project contextual menu and clicking Gradle|Enable DSL Support and Groovy|Enable DSL Support, which can help with working with .gradle source code.

Last, but not least, you can run the gradle tasks with the gradle daemon, as follows:

gradle sayHello --daemon –Dorg.gradle.debug=true

In this scenario, the Eclipse debugger will attach itself to the daemon, and it will be active until the daemon is stopped. This can make debugging faster, though some instability might result –both things inherent to using the gradle daemon.

Leave a comment