Launch builds from another gradle installation using tooling api

We are creating a custom distribution where we package our own set of jar files inside the gradle distribution and then include an init script within that distribution which wires our jar file into the distribution.

At any point the users (& us on our CI servers) can have many different “versions” of the build system installed on the machines. Each “version” could be based on the same core gradle distribution but might have different versions of the plugins that we are providing.

I’m trying to provide a way from one instance to launch a build to another instance thats installed on the machine. I am using the Tooling API to do this. I have this code, which is executed through a BuildListener registered within the init script:

ProjectConnection connection = GradleConnector.newConnector()
    .forProjectDirectory(gradle.startParameter.currentDir)
    .useInstallation(gradleHome)
    .connect()
    try {
   connection.newBuild()
     .forTasks(gradle.startParameter.taskNames.toArray(new String[gradle.startParameter.taskNames.size()]))
     .setJvmArguments("-D$IGNORE_UPDATES_PROPERTY_NAME=true")
     .run()
  }
  finally {
   connection.close()
  }

The gradleHome variable is a File object which is passed in - basically it points to a location on the filesystem where the gradle installation is that I want to run from. The problem I’m having is that while this works the 1st time, if I then re-run and point to a different location on the filesystem it doesn’t work - it now runs with the same installation directory that it ran from the first time.

I suspect this has something to do with the gradle daemon, since both of the installations I’m trying to run with are based on the same core gradle distribution version (1.1). Is there a way I can get around this? Basically whatever I decide gradleHome is I want to execute the build from that spot. I tried using a ProcessBuilder and physically launching a new process, and that seems to work, but what I lose is the feedback from the process as to what it is doing.

Any ideas?

I’m trying to provide a way from one instance to launch a build to another instance thats installed on the machine.

Can you elaborate a bit on the use case?

Sure. The use case is this:

When gradle starts up our requirement is that the latest “released” version of our custom distribution is always used. So in the init script I go out to our server, determine what the latest version is & compare it to the currently-executing version (based on a version scheme we derive based on the core gradle distro version + the version of our plugins - for example version 1.1-1.0.0.100 would be the version of the custom distro based on core gradle distro 1.1 and our custom plugins version 1.0.0.100).

If I see that the currently executing version is not the most recent, I fetch it & unpack it. Then I want to basically fire off the same build using the newly-unpacked distro (which in this case is based on the same core gradle distro version 1.1).

I have code in my init script that also prints out all of the version info (core gradle distro, customizations version, etc) at startup. When I fire up the build through the tooling api it seems that the same version of the customizations is used that was executing the build in the first place.

Sounds like you have written your own Gradle wrapper, just as an init script. This feels like the wrong approach to me. It might slow down the build, require more resources (two daemons, etc.), you’ll have to trick the Gradle instance that executes the init script not to run the build again, etc. What’s the advantage over writing a wrapper from scratch, or forking Gradle’s wrapper?

Your explanation about what’s causing the problem sounds reasonable to me. My first guess is that you might have to inject a custom version number into the distribution. But I’ll have to ask the team.

I’ve actually changed my direction over the course of the day today to look at the wrapper - basically I want to catch the version & have everything downloaded before Gradle itself gets fired up, that way I don’t have to worry about the daemon. I’ve been looking at the wrapper code and I’ve created my own wrapper class which looks up the urls and creates a gradle-wrapper.properties file on the fly to then hand off to the rest of the wrapper code.

I’m still playing around with it so I’ll keep you posted.

If there was a way for me to inject a custom gradle version (i.e. <core_gradle_distro>-<company_specific_version>) into the daemon that would be helpful too.

I actually found my implementation for this. I created my own entrypoint with a main method that generates a gradle-wrapper.properties file in a unique location on the filesystem for this particular invocation. Once that has been generated & contains the url of the version i want to use, I call into the wrapper API and have it execute using that properties file.

This seems to be a very viable solution for us.

Good to hear. By the way, you were right in that selecting a daemon doesn’t work correctly if you have multiple custom distros based on the same Gradle version. (Maybe there is a way to inject a custom version number into the distro, but I haven’t found one yet.) This is GRADLE-2408.