Skip to content

Commit f981cfc

Browse files
committed
Avoid forking if Gradle already has appropriate add-exports/add-opens
…passed through org.gradle.jvmargs property.
1 parent 3adff47 commit f981cfc

File tree

3 files changed

+92
-6
lines changed

3 files changed

+92
-6
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,17 +157,20 @@ Starting with JDK 16, due to [JEP 396: Strongly Encapsulate JDK Internals by Def
157157

158158
The plugin will automatically [use a forking compiler][CompileOptions.fork]
159159
and pass the necessary [JVM arguments][BaseForkOptions.getJvmArgs]
160-
whenever it detects such a JDK is being used and ErrorProne is enabled.
160+
whenever it detects such a JDK is being used and ErrorProne is enabled
161+
(unless the Gradle daemon's JVM already was given the appropriate options [through `org.gradle.jvmargs`][org.gradle.jvmargs]).
161162

162163
That detection will only take into account the [toolchain][gradle-toolchains] used by the `JavaCompile` task,
163164
or the JDK used to run Gradle in case no toolchain is being used.
164165
The plugin will ignore any task that forks and defines either [a `javaHome`][ForkOptions.setJavaHome] or [an `executable`][ForkOptions.setExecutable],
165166
and thus won't configure the JVM arguments if you're e.g. running Gradle with an older JDK and forking the compilation tasks to use JDK 17.
166167

167-
Note that the plugin also configures the JVM arguments for any JDK between 9 and 15 to silence related warnings,
168-
but they will then only be used if the task is explicitly configured for forking.
168+
Note that the plugin also configures the JVM arguments for any JDK above version 9 to silence related warnings,
169+
but they will then only be used if the task is explicitly configured for forking
170+
(or if the configured toolchain is incompatible with the JDK used to run Gradle, which will then implicitly fork a compiler daemon).
169171

170172
[jep396]: https://openjdk.java.net/jeps/396
173+
[org.gradle.jvmargs]: https://docs.gradle.org/current/userguide/build_environment.html#sec:configuring_jvm_memory
171174

172175
## Custom Error Prone checks
173176

src/main/kotlin/net/ltgt/gradle/errorprone/ErrorPronePlugin.kt

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,39 @@ class ErrorPronePlugin @Inject constructor(
5252
"--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
5353
"--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED"
5454
)
55+
56+
private val CURRENT_JVM_NEEDS_FORKING by lazy {
57+
// Needs bootclasspath
58+
JavaVersion.current() == JavaVersion.VERSION_1_8 || (
59+
// Needs --add-exports and --add-opens
60+
JavaVersion.current() >= JavaVersion.VERSION_16 &&
61+
try {
62+
sequenceOf(
63+
"com.sun.tools.javac.api.BasicJavacTask",
64+
"com.sun.tools.javac.api.JavacTrees",
65+
"com.sun.tools.javac.file.JavacFileManager",
66+
"com.sun.tools.javac.main.JavaCompiler",
67+
"com.sun.tools.javac.model.JavacElements",
68+
"com.sun.tools.javac.parser.JavacParser",
69+
"com.sun.tools.javac.processing.JavacProcessingEnvironment",
70+
"com.sun.tools.javac.tree.JCTree",
71+
"com.sun.tools.javac.util.JCDiagnostic"
72+
).any {
73+
val klass = Class.forName(it)
74+
return@any !klass.module.isExported(klass.packageName, this::class.java.classLoader.unnamedModule)
75+
} &&
76+
sequenceOf(
77+
"com.sun.tools.javac.code.Symbol",
78+
"com.sun.tools.javac.comp.Enter"
79+
).any {
80+
val klass = Class.forName(it)
81+
return@any !klass.module.isOpen(klass.packageName, this::class.java.classLoader.unnamedModule)
82+
}
83+
} catch (e: ClassNotFoundException) {
84+
true
85+
}
86+
)
87+
}
5588
}
5689

5790
override fun apply(project: Project) {
@@ -103,7 +136,7 @@ class ErrorPronePlugin @Inject constructor(
103136
if (!errorproneOptions.isEnabled.getOrElse(false)) return@doFirst
104137
jvmArgumentProvider.compilerVersion?.let {
105138
if (it < JavaVersion.VERSION_1_8) throw UnsupportedOperationException(TOO_OLD_TOOLCHAIN_ERROR_MESSAGE)
106-
if (it == JavaVersion.VERSION_1_8 || it >= JavaVersion.VERSION_16) options.isFork = true
139+
if (it == JavaVersion.VERSION_1_8 || (it == JavaVersion.current() && CURRENT_JVM_NEEDS_FORKING)) options.isFork = true
107140
}
108141
}
109142
}

src/test/kotlin/net/ltgt/gradle/errorprone/ToolchainsIntegrationTest.kt

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package net.ltgt.gradle.errorprone
22

33
import com.google.common.truth.Truth.assertThat
44
import com.google.common.truth.TruthJUnit.assume
5+
import org.gradle.api.JavaVersion
56
import org.gradle.testkit.runner.BuildResult
67
import org.gradle.testkit.runner.TaskOutcome
78
import org.gradle.util.GradleVersion
@@ -230,7 +231,10 @@ class ToolchainsIntegrationTest : AbstractPluginIntegrationTest() {
230231
}
231232

232233
@Test
233-
fun `configure forking in Java 16+ VM`() {
234+
fun `configure forking in Java 16+ VM (unless implicitly forked by incompatible toolchain)`() {
235+
// https://github.com/gradle/gradle/issues/16857#issuecomment-931610187
236+
assume().that(GradleVersion.version(testGradleVersion)).isAtLeast(GradleVersion.version("7.0"))
237+
234238
// given
235239
buildFile.appendText(
236240
"""
@@ -248,7 +252,53 @@ class ToolchainsIntegrationTest : AbstractPluginIntegrationTest() {
248252
// then
249253
result.assumeToolchainAvailable()
250254
assertThat(result.task(":compileJava")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
251-
assertThat(result.output).contains(FORKED)
255+
assertThat(result.output).contains(if (JavaVersion.current() == JavaVersion.VERSION_17) FORKED else NOT_FORKED)
256+
assertThat(result.output).contains(JVM_ARGS_STRONG_ENCAPSULATION)
257+
// Check that the configured jvm arg is preserved
258+
assertThat(result.output).contains(jvmArg("-XshowSettings"))
259+
}
260+
261+
// check that it doesn't mess with task avoidance
262+
263+
// when
264+
testProjectDir.buildWithArgsAndFail("compileJava").also { result ->
265+
// then
266+
result.assumeToolchainAvailable()
267+
assertThat(result.task(":compileJava")?.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
268+
}
269+
}
270+
271+
@Test
272+
fun `does not configure forking in Java 16+ VM if current JVM has appropriate JVM args`() {
273+
// https://github.com/gradle/gradle/issues/16857#issuecomment-931610187
274+
assume().that(GradleVersion.version(testGradleVersion)).isAtLeast(GradleVersion.version("7.0"))
275+
assume().withMessage("isJava16Compatible").that(JavaVersion.current()).isAtLeast(JavaVersion.VERSION_16)
276+
277+
testProjectDir.resolve("gradle.properties").appendText(
278+
"""
279+
280+
org.gradle.jvmargs=-Xmx512m "-XX:MaxMetaspaceSize=384m" ${ErrorPronePlugin.JVM_ARGS_STRONG_ENCAPSULATION.joinToString(" ")}
281+
""".trimIndent()
282+
)
283+
284+
// given
285+
buildFile.appendText(
286+
"""
287+
288+
java {
289+
toolchain {
290+
languageVersion.set(JavaLanguageVersion.of(${JavaVersion.current().majorVersion}))
291+
}
292+
}
293+
""".trimIndent()
294+
)
295+
296+
// when
297+
testProjectDir.buildWithArgsAndFail("compileJava").also { result ->
298+
// then
299+
result.assumeToolchainAvailable()
300+
assertThat(result.task(":compileJava")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
301+
assertThat(result.output).contains(NOT_FORKED)
252302
assertThat(result.output).contains(JVM_ARGS_STRONG_ENCAPSULATION)
253303
// Check that the configured jvm arg is preserved
254304
assertThat(result.output).contains(jvmArg("-XshowSettings"))

0 commit comments

Comments
 (0)