Gradle Across Machines (without deleting past progress)

We’re using Gradle in a large project with a Jenkins pipeline that allows us to scale our build pipeline (e.g. across multiple machines).

What we have now looks like:

  1. Build everything
  2. Run basic unit tests
  3. Run integration tests with single-machine backend on several machines at once (copies project directory elsewhere)
  4. Run integration tests with distributed backend on several machines at once (copies project directory elsewhere)
  5. Aggregate test results / reports and publish the build if everything was OK

Note that steps 3 and 4 each consist of parallel gradle builds, each on a different machine. We use multiple machines to run our pipeline in parallel because even with Gradle’s built-in parallel support, it takes far too long to do everything on one machine.

For some reason, when we build on the other machines, Gradle cleans up and re-does all of the work performed by past stages. The file structure does not change and we preserve the “.gradle” directory when copying to other machines. We also propagate the cached gradle jars to a temporary GRADLE_USER_HOME directories on the build machines, but do not preserve the local script cache or anything else.

The contents of the .gradle directory isn’t built to be shared in that way, so I’m not surprised this doesn’t work.

This is something you can use the build cache to solve. You basically don’t worry about carefully copying things between machines and just let Gradle pull the shareable things from a remote or local cache.

We use this in the Gradle build pipeline a lot. We split the Gradle build along subproject lines and build/test them on separate machines. This has the added bonus that if Gradle can tell that something hasn’t changed from any previous execution, we can skip that work again.

I can try to do this, but migrating our build to work this way is quite a substantial task. It would be nice to have a temporary fix that can provide this behavior. In older Gradle versions, this worked.

Also, how do I copy back only new/modified files when Gradle deletes/re-creates everything? The overhead of storing everything (from each machine) and network overhead of copying it all back is quite substantial.

With the build cache? You wouldn’t explicitly copy anything between machines (beyond cloning the repository at a particular commit) because Gradle would just download the parts that are needed.

The issue here is that once a machine is done with its testing tasks, it sends and stores all changed files to a subfolder of the master Jenkins instance. This takes a lot longer if Gradle deletes and re-creates the majority of the classes since rsync thinks everything (except the source code) is new.

Also, we build our C++ subproject with CMake since CLion doesn’t support Gradle. I don’t even know how I would use this CMake-built files with the build cache (and this is also the project that takes longest to build by far).

Really, we just need a way to tell Gradle “If you see a build directory, please just trust that it was built with the same version of Gradle and the same tools on a different machine. Don’t delete it.” For Java projects, we are already starting to use the build cache (and this migration is expected to happen soon). However, we are waiting on tool support before doing this for C++.

Other groups here have solved this by setting their wrappers to use older Gradle versions (pre-3.5) but this is a last resort for us.