Skip to content

Commit

Permalink
Merge pull request #24072 Restore an edge case of non-javac toolchain…
Browse files Browse the repository at this point in the history
… tools being used as a Java compiler (Gradle 8.0.x)

Backporting of a fix from #24014

Co-authored-by: Alex Semin <asemin@gradle.com>
  • Loading branch information
bot-gradle and alllex committed Feb 28, 2023
2 parents 707cf62 + 77e3c35 commit a21ec6d
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 23 deletions.
Expand Up @@ -469,6 +469,92 @@ class JavaCompileToolchainIntegrationTest extends AbstractIntegrationSpec implem
JavaVersion.current() | "[deprecation] foo() in Foo has been deprecated"
}

@Issue("https://github.com/gradle/gradle/issues/23990")
def "can compile with a custom compiler executable"() {
def otherJdk = AvailableJavaHomes.getJdk(JavaVersion.current())
def jdk = AvailableJavaHomes.getDifferentVersion {
def v = it.languageVersion.majorVersion.toInteger()
11 <= v && v <= 18 // Java versions supported by ECJ releases used in the test
}

buildFile << """
plugins {
id("java")
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(${otherJdk.javaVersion.majorVersion})
}
}
configurations {
ecj {
canBeConsumed = false
canBeResolved = true
}
}
${mavenCentralRepository()}
dependencies {
def changed = providers.gradleProperty("changed").isPresent()
ecj(!changed ? "org.eclipse.jdt:ecj:3.31.0" : "org.eclipse.jdt:ecj:3.32.0")
}
// Make sure the provider is up-to-date only if the ECJ classpath does not change
class EcjClasspathProvider implements CommandLineArgumentProvider {
@Classpath
final FileCollection ecjClasspath
EcjClasspathProvider(FileCollection ecjClasspath) {
this.ecjClasspath = ecjClasspath
}
@Override
List<String> asArguments() {
return ["-cp", ecjClasspath.asPath, "org.eclipse.jdt.internal.compiler.batch.Main"]
}
}
compileJava {
def customJavaLauncher = javaToolchains.launcherFor {
languageVersion.set(JavaLanguageVersion.of(${jdk.javaVersion.majorVersion}))
}.get()
// ECJ does not support generating JNI headers
options.headerOutputDirectory.set(provider { null })
options.fork = true
options.forkOptions.executable = customJavaLauncher.executablePath.asFile.absolutePath
options.forkOptions.jvmArgumentProviders.add(new EcjClasspathProvider(configurations.ecj))
}
"""

when:
withInstallations(jdk, otherJdk).run(":compileJava", "--info")
then:
executedAndNotSkipped(":compileJava")
outputContains("Compiling with toolchain '${jdk.javaHome.absolutePath}'")
outputContains("Compiling with Java command line compiler '${jdk.javaExecutable.absolutePath}'")
classJavaVersion(javaClassFile("Foo.class")) == jdk.javaVersion

// Test up-to-date checks
when:
withInstallations(jdk, otherJdk).run(":compileJava")
then:
skipped(":compileJava")

when:
withInstallations(jdk, otherJdk).run(":compileJava", "-Pchanged")
then:
executedAndNotSkipped(":compileJava")

when:
withInstallations(jdk, otherJdk).run(":compileJava", "-Pchanged")
then:
skipped(":compileJava")
}

private TestFile configureForkOptionsExecutable(Jvm jdk) {
buildFile << """
compileJava {
Expand Down
Expand Up @@ -41,17 +41,7 @@ public T create() {
}

if (compileOptions.isFork()) {
File customJavaHome = compileOptions.getForkOptions().getJavaHome();
if (customJavaHome != null) {
return getCommandLineSpec(Jvm.forHome(customJavaHome).getJavacExecutable());
}

String customExecutable = compileOptions.getForkOptions().getExecutable();
if (customExecutable != null) {
return getCommandLineSpec(new File(customExecutable));
}

return getForkingSpec(Jvm.current().getJavaHome());
return chooseSpecFromCompileOptions(Jvm.current().getJavaHome());
}

return getDefaultSpec();
Expand All @@ -64,13 +54,7 @@ private T chooseSpecForToolchain() {
}

if (compileOptions.isFork()) {
// Presence of the fork options means that the user has explicitly requested a command-line compiler
if (compileOptions.getForkOptions().getJavaHome() != null || compileOptions.getForkOptions().getExecutable() != null) {
// We use the toolchain path because the fork options must agree with the selected toolchain
return getCommandLineSpec(Jvm.forHome(toolchainJavaHome).getJavacExecutable());
}

return getForkingSpec(toolchainJavaHome);
return chooseSpecFromCompileOptions(toolchainJavaHome);
}

if (!toolchain.isCurrentJvm()) {
Expand All @@ -80,6 +64,20 @@ private T chooseSpecForToolchain() {
return getDefaultSpec();
}

private T chooseSpecFromCompileOptions(File fallbackJavaHome) {
File forkJavaHome = compileOptions.getForkOptions().getJavaHome();
if (forkJavaHome != null) {
return getCommandLineSpec(Jvm.forHome(forkJavaHome).getJavacExecutable());
}

String forkExecutable = compileOptions.getForkOptions().getExecutable();
if (forkExecutable != null) {
return getCommandLineSpec(new File(forkExecutable));
}

return getForkingSpec(fallbackJavaHome);
}

abstract protected T getCommandLineSpec(File executable);

abstract protected T getForkingSpec(File javaHome);
Expand Down
Expand Up @@ -259,7 +259,6 @@ private void validateForkOptionsMatchToolchain() {

JavaCompiler javaCompilerTool = getJavaCompiler().get();
File toolchainJavaHome = javaCompilerTool.getMetadata().getInstallationPath().getAsFile();
File toolchainExecutable = javaCompilerTool.getExecutablePath().getAsFile();

ForkOptions forkOptions = getOptions().getForkOptions();
File customJavaHome = forkOptions.getJavaHome();
Expand All @@ -269,10 +268,16 @@ private void validateForkOptionsMatchToolchain() {
);

String customExecutablePath = forkOptions.getExecutable();
checkState(
customExecutablePath == null || new File(customExecutablePath).equals(toolchainExecutable),
"Toolchain from `executable` property on `ForkOptions` does not match toolchain from `javaCompiler` property"
);
// We do not match the custom executable against the compiler executable from the toolchain (javac),
// because the custom executable can be set to the path of another tool in the toolchain such as a launcher (java).
if (customExecutablePath != null) {
// Relying on the layout of the toolchain distribution: <JAVA HOME>/bin/<executable>
File customExecutableJavaHome = new File(customExecutablePath).getParentFile().getParentFile();
checkState(
customExecutableJavaHome.equals(toolchainJavaHome),
"Toolchain from `executable` property on `ForkOptions` does not match toolchain from `javaCompiler` property"
);
}
}

private boolean isToolchainCompatibleWithJava8() {
Expand Down

0 comments on commit a21ec6d

Please sign in to comment.