-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Gradle 8.0. silently dropped support for custom compilers in JavaCompile
#23990
Comments
Thank you for providing a valid reproducer. The issue is in the backlog of the relevant team and is prioritized by them. |
Hey! We are looking into fixing this here. In the meantime, here are some comments on the issue.
This is intentional, because
For the majority of cases, if a build configures The reason it is not deprecated is that most builds that do use the |
But shouldn't setting to |
@TheMrMilchmann we merged the fix for this into the Gradle 8.0.x line and published a snapshot. You can use the wrapper with
|
I can confirm that this does seem to work mostly. However, I have one remaining issue/question: In an attempt to prevent potential ordering issues, I have deferred the retrieval of the tool and, thus, the configuration of val javaLauncher = provider {
if (java.toolchain.languageVersion.orNull?.canCompileOrRun(REQUIRED_JAVA_VERSION) == true) {
javaToolchains.launcherFor(java.toolchain).orNull ?: error("Could not get launcher for toolchain: ${java.toolchain}")
} else {
javaToolchains.launcherFor {
languageVersion.set(JavaLanguageVersion.of(PREFERRED_JAVA_VERSION))
}.orNull ?: error("Could not provision launcher for Java $PREFERRED_JAVA_VERSION")
}
}
inputs.property("javaLauncher", javaLauncher.map { it.metadata.languageVersion.asInt() })
/* See https://docs.gradle.org/7.4.2/userguide/validation_problems.html#implementation_unknown */
@Suppress("ObjectLiteralToLambda")
doFirst(object : Action<Task> {
override fun execute(t: Task) {
options.forkOptions.executable = javaLauncher.get().executablePath.asFile.absolutePath
}
}) This, too, works fine on Gradle 7.6.1 but fails on 8 (including I've updated the MCVE in TheMrMilchmann/MCVE@e195bf3 to reproduce this. |
This is incomplete and does not fully work yet. We might have to get rid of the lazy configuration of `forkOptions.executable`. See gradle/gradle#23990 (comment) See #9
How about you set the toolchain you want to have on the The following setup allows doing that. The only downside it has is that it selects the executable by path manipulation. Though I think it is much cleaner. The input tracking for the Java version works out of the box. val currentJavaToolchain = provider {
java.toolchain
}
val selectedCompiler = currentJavaToolchain.map { toolchain ->
val currentCompiler = project.javaToolchains.compilerFor(toolchain).get()
if (currentCompiler.metadata.languageVersion.canCompileOrRun(REQUIRED_JAVA_VERSION)) {
currentCompiler
} else {
project.javaToolchains.compilerFor { languageVersion.set(JavaLanguageVersion.of(PREFERRED_JAVA_VERSION)) }.get()
}
}
withType<JavaCompile>().configureEach {
javaCompiler.set(selectedCompiler)
/* ECJ does not support generating JNI headers. Make sure the property is not used. */
options.headerOutputDirectory.set(provider { null })
options.isFork = true
options.forkOptions.jvmArgumentProviders.add(CommandLineArgumentProvider {
mutableListOf("-cp", ecj.asPath, "org.eclipse.jdt.internal.compiler.batch.Main")
})
@Suppress("ObjectLiteralToLambda")
doFirst(object : Action<Task> {
override fun execute(t: Task) {
val javacExecutable = javaCompiler.get().executablePath.asFile
val javaExecutableName = javacExecutable.name.replace("javac", "java")
val javaExecutablePath = javacExecutable.parentFile.resolve(javaExecutableName).absolutePath
options.forkOptions.executable = javaExecutablePath
}
})
} |
Thanks for the input, @wolfs! I've considered doing something similar before but it didn't cross my mind that manipulating the path like that is possible now. Given that this is possible, I think we can consider this issue to be fixed. |
In some cases, it may be desirable to use a custom Java compiler (e.g., ECJ) instead of javac (ref). Since Gradle does not currently have first-class support for this, I accomplished this by configuring the
JavaCompile
tasks manually. Specifically, in Gradle 7.x (including 7.6), first I set thejavaCompiler
tonull
. Second, I configure theforkOptions.executable
to point at ajava
(notjavac
!) executable. Finally, the ECJ jar and its entrypoint are injected into the commandline arguments.This works fine as can be seen when inspecting the debug output of a project that was configured accordingly. Importantly, there is no warning about this being unsupported or subject for removal.
Expected Behavior
The configuration should work fine on Gradle 8. , just like it did on 7.6.
Current Behavior
On Gradle 8, however, this is broken. First, setting the
javaCompiler
tonull
straight up does not work anymore. An ISE is thrown because Gradle attempts to access the value unconditionally. Additionally, Gradle introduced a check that validates that theforkOptions.executable
and thejavaCompiler
properties ofJavaCompile
tasks point to the same file. Thus, even without setting thejavaCompiler
tonull
, the build fails.As it stands right now, I'm also questioning the purpose of
forkOptions.executable
as the value is forced match the value of thejavaCompiler
property. However, it is not deprecated which leads me to believe that something is incorrect here.Steps to Reproduce
An MCVE is available at https://github.com/TheMrMilchmann/MCVE/tree/gradle/18632-ecj-7.6-repro
Running
gradlew compileJava
on this project (using Gradle 7.6) works. The debug output can be inspected to validate the compiler invocation. When using Gradle 8. , the behavior described above can be observed.The text was updated successfully, but these errors were encountered: