Is it possible to resume Gradle incremental build on other machine?

I was trying to make a build pipeline using Jenkins and Gradle. Idea was to run part of war life cycle in one job and the rest in downstream job. Downstream job gets a copy of upstream workspace.

First attempt worked as I was expecting, downstream job resumed executing tasks. However, second execution of downstream job started executing whole life cycle from beginning. It appeared, that first time both upstream and downstream jobs were running on same virtual machine. Second time downstream job got other machine.

If the whole workspace (including ‘build’ directories and top-level ‘.gradle’ directory) gets copied over, the build should “resume” correctly. Did you make sure that the second execution of the downstream job gets the workspace of the first execution of that job?

Depending on your requirements and the tools involved, another solution might be to pin the execution of jobs belonging to the same chain to a particular machine and workspace.

I found one mistake in my setup. Hidden folders were not removed from downstream workspace. Now when it’s fixed, build is not resumed even on same machine. Test on local machine confirmed that. Here are two scenarios:

  1. gradle clean assemble -> gradle test 2) gradle clean assemble -> copy whole workspace to new folder -> switch to copy-> gradle test

In first case, build is resumed. In second case it starts all over. Is it possible that “last modified” or absolute file path affect input file snapshots?

Unfortunately, using same workspace is not an option. I’m using CloudBees and can not guarantee which machine will be allocated for build.

Removing hidden folders means to also remove ‘.gradle’, in which case Gradle will not be able to resume the build. Changed last-modified timestamps shouldn’t prevent resumption but may slow it down considerably, as Gradle is then forced to perform a deep comparison of inputs and outputs.

I remove hidden folders (an all other files) that remain from previous builds, in case if Jenkins job gets some machine that it used before. After that whole workspace is copied from upstream job, including .gradle. Anyway, this does not matter in scenario where I just make a fresh copy of workspace.

It looks like file absolute path makes the difference. Made several tests that confirm it. First I run gradle assemble. Then I archive workspace and wipe it out. If I unpack archived workspace to same folder where original workspace was, build is resumed. If I unpack it to some other place, build build starts all over.

Absolute path to Jenkins workspace contains job name, so downstream workspace has different absolute path than upstream. Is it expected behavior?

Resuming a build on another machine isn’t an explicitly supported feature, so it may well be (actually sounds likely) that absolute paths are compared rather than relative ones. Feel free to submit an enhancement request.

I have the same problem, this is not related to “resuming a build on another machine” thing. The problem is that Gradle takes a snapshot of the input file and maps it to the file’s absolute path, this is the root of the issue (as I understand it, of course I might be wrong, you’re the expert) - when you copy your 1st job workspace to the 2nd job workspace (they are on different paths) assuming everything will continue - it has to start all over again as it doesn’t recognize any of the snapshots saved in the copied .gradle directory.

I’ve investigated a bit and found in

org.gradle.api.internal.changedetection.state.DefaultFileSnapshotter
public FileCollectionSnapshot snapshot(FileCollection sourceFiles) {
   final Map<String, FileSnapshot> snapshots = new HashMap<String, FileSnapshot>();
   final Set<File> theFiles = sourceFiles.getAsFileTree().getFiles();
   cacheAccess.useCache("Create file snapshot", new Runnable() {
      public void run() {
         for (File file : theFiles) {
            if (file.isFile()) {
               snapshots.put(***file.getAbsolutePath()***, new FileHashSnapshot(hasher.hash(file)));
            } else if (file.isDirectory()) {
               snapshots.put(***file.getAbsolutePath()***, new DirSnapshot());
            } else {
               snapshots.put(***file.getAbsolutePath()***, new MissingFileSnapshot());
            }
         }
       }
   });
   return new FileCollectionSnapshotImpl(snapshots);
}

What I thought was that instead of mapping absolutePath->snapshot, one can use a 2 maps:

absolutePath -> snapshot <- relativePathToMasterBuild.gradle

Since the build process hashs every file anyways, if the build can’t locate the absolute path in the map, try and search for the relative path to the master build.gradle and compare the hash. In theory, this should solve the issue.

This is a pretty big issue for us with regard to setting up build pipelines in Jenkins. Having a downstream job that’s either getting a cloned workspace or copying build artifacts from an upstream job doesn’t have the desired affect of NOT rebuilding any artifacts that would otherwise be considered up-to-date in the original workspace.

Additionally, it just seems like a bad idea to have gradle state for a workspace tied to that workspace’s location.

Is anyone else experiencing this issue with trying to use gradle in a build pipeline? Any workarounds or other ideas?

Hi everybody!

Anyone’s got the answer or a workaround for that issue?