0

I am trying to set up a Windows test server to run the unit tests of a Java library project via SSH. The actual building happens on a Linux server.

As a consequence, I would like to be able to copy the pre-built project to the Windows machine and run .\gradlew.bat test on it without recompiling it. Since I’m copying the entire src and build folder, I was expecting Gradle to realise that the build artefacts are more recent than the sources, and thus to avoid recompilation.

However, Gradle does attempt to recompile the sources, and fails (there’s intentionally no JDK installed on the Windows machine, since I don’t want to build on the Windows machine):

> Task :compileJava FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':compileJava'.
> Could not find tools.jar. Please check that C:\Program Files\Java\jre1.8.0_251 contains a valid JDK installation.

(scp does not preserve timestamps when copying from Linux to Windows, but I verified that the timestamps of the files in the build directory are newer than that of the src directory.)

Next I tried removing the compileTestJava dependency from testClasses, but this is no longer supported as of Gradle 6 (and I don’t think this would have worked anyway, and it seems like hack).

So:

  • Why does Gradle think my project needs recompiling after copying it to a Windows machine, even thought the file modification timestamps seem ordered correctly (build is newer than src is newer than build.gradle)?
  • How can I ensure that Gradle does not attempt to rebuild up-to-date dependencies before running tests after copying the project to a different (Windows) machine?

Here’s my build.gradle file (it‘s really quite barebone):

version '1.0-SNAPSHOT'

apply plugin: 'java-library'

sourceCompatibility = 1.8
targetCompatibility = 1.8

compileJava.options.encoding = 'UTF-8'

repositories {
    mavenCentral()
}

sourceSets {
    main.java.srcDirs = ["src/main/java"]
    test.java.srcDirs = ["src/test/java"]
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

test {
    testLogging {
        outputs.upToDateWhen {false}
        showStandardStreams = true
    }
}

Here’s how I kick off the build from the Linux server (copying the individual files/directories manually one after the other to ensure well-ordered timestamps makes no difference):

ssh test@win 'rmdir /Q /S Workspace\test' || :
ssh test@win 'mkdir Workspace\test'
scp -r build.gradle gradle gradlew.bat src build test@win:Workspace\\test
ssh test@win 'cd Workspace\test & .\gradlew.bat --no-daemon test'

Why not just install the JDK and build on the Windows server?
The Windows machine is resource-constrained (it’s also a VM). Furthermore, the build needs to happen anyway on the Linux server. I would really like to avoid a slow and redundant rebuild.
Why not test on the Linux server (using Wine)?
I tried that, but our Linux library uses some Win32 APIs (via cross-compiled JNI) which Wine doesn’t support, and which reproducibly lead to a hang.

Further notes:

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 1
    But... Your tests aren't written in Java ? Also, are you using [build cache](https://docs.gradle.org/current/userguide/build_cache.html) ? Even if so, maybe Gradle needs to launch compile task to make some checks ? – amanin Nov 05 '20 at 16:35
  • @amanin No, my tests are written in Java using JUnit and also compiled on the Linux machine and copied over. I’m not using build caching. — “maybe Gradle needs to launch compile task to make some checks” … I *thought* it doesn’t, but I fear you might be right. I’m installing the JDK now to test this. – Konrad Rudolph Nov 05 '20 at 16:36
  • Yes but, to execute them, you need java anyway. I don't understand how it would work without java installed. Gradle is telling you that no valid *jre* was found. You cannot execute java binaries without jre. – amanin Nov 05 '20 at 16:39
  • @amanin Java *is* correctly installed (otherwise Gradle wouldn’t run at all). It’s the *JDK* that’s missing (and the Gradle error message is telling me that it’s not finding a *JDK* installation in the `JAVA_HOME` path, which is correctly set to `C:\Program Files\Java\jre1.8.0_251`). – Konrad Rudolph Nov 05 '20 at 16:40
  • @amanin Tested that now; but Gradle really does recompile the project once I’ve pointed it to a valid JDK installation. It doesn’t just run `compileJava` to make checks. – Konrad Rudolph Nov 05 '20 at 17:07
  • Ok. Have you tried to [define a custom local build cache](https://stackoverflow.com/a/55381607/2678097) then copying it to the windows machine ? – amanin Nov 05 '20 at 17:35

1 Answers1

0

A regrettably hand-wavy answer here: this seems to be accomplishable through the use of build caches. The caveat is, that from everything that I can see on Gradle's site, this is either:

  • a function of their Gradle Enterprise offering, or
  • something that can be leveraged with their Docker container, which is heavily limited because they probably want you to pay for it.

Once a build cache is configured (and I don't believe it prudent to copy the instructions on how to configure one verbatim here), it seems that it's a matter of leveraging it in your build script.

(from https://docs.gradle.org/current/userguide/build_cache.html#sec:build_cache_configure_remote):

settings.gradle:

buildCache {
    remote(HttpBuildCache) {
        url = 'https://example.com:8123/cache/'
    }
}

According to the relevant documentation, the compileJava task is cacheable so no further configuration in the build is required for it.

Makoto
  • 104,088
  • 27
  • 192
  • 230