Skip to content

Commit 89eeb78

Browse files
committed
Add specific support for JDK 16
JDK 16 is then tested with both Gradle 6 and Gradle 7
1 parent cf5c08c commit 89eeb78

10 files changed

+251
-6
lines changed

.github/workflows/gradle.yaml

+39-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ jobs:
104104
runs-on: ubuntu-latest
105105
strategy:
106106
matrix:
107-
java: [ 11, 15 ]
107+
java: [ 11, 16 ]
108108
steps:
109109
- uses: actions/checkout@v2
110110

@@ -141,3 +141,41 @@ jobs:
141141
with:
142142
name: jdk-${{ matrix.java }}-tests
143143
path: build/reports/tests/test/
144+
145+
test-jdk16-gradle7:
146+
needs: linux
147+
runs-on: ubuntu-latest
148+
steps:
149+
- uses: actions/checkout@v2
150+
151+
- name: Setup JDK 16
152+
id: setup-test-java
153+
uses: actions/setup-java@v1
154+
with:
155+
java-version: 16
156+
157+
- name: Setup JAVA_HOME_16_X64 environment variable
158+
run: echo "JAVA_HOME_16_X64=${{ steps.setup-test-java.outputs.path }}" | tee -a $GITHUB_ENV
159+
160+
- name: Setup environment
161+
run: |
162+
echo "JAVA_HOME=${JAVA_HOME_8_X64}" | tee -a $GITHUB_ENV
163+
echo "org.gradle.java.installations.auto-download=false" | tee -a gradle.properties
164+
echo "org.gradle.java.installations.fromEnv=JAVA_HOME_16_X64" | tee -a gradle.properties
165+
166+
- name: Build with Gradle
167+
id: gradle-build
168+
uses: eskatos/gradle-command-action@v1
169+
with:
170+
arguments: --scan test -Ptest.java-toolchain=16 -Ptest.gradle-version=7.0-milestone-3
171+
wrapper-cache-enabled: true
172+
dependencies-cache-enabled: true
173+
dependencies-cache-key: '**/*gradle.lockfile'
174+
dependencies-cache-exact: true
175+
176+
- name: Store reports
177+
if: always() && (steps.gradle-build.outcome == 'success' || steps.gradle-build.outcome == 'failure')
178+
uses: actions/upload-artifact@v2
179+
with:
180+
name: jdk16-gradle7-tests
181+
path: build/reports/tests/test/

README.md

+17
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,23 @@ someTask.configure {
196196
[BaseForkOptions.getJvmArgs]: https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/compile/BaseForkOptions.html#getJvmArgs--
197197
[ForkOptions.setJavaHome]: https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/compile/ForkOptions.html#setJavaHome-java.io.File-
198198

199+
## JDK 16+ support
200+
201+
Starting with JDK 16, due to [JEP 396: Strongly Encapsulate JDK Internals by Default][jep396],
202+
`--add-opens` and `--add-exports` arguments need to be passed to the compiler's JVM.
203+
204+
The plugin will automatically [use a forking compiler][CompileOptions.fork]
205+
and pass the necessary [JVM arguments][BaseForkOptions.getJvmArgs]
206+
whenever it detects such a JDK is being used and ErrorProne is enabled.
207+
208+
That detection will only take into account the [toolchain][gradle-toolchains] used by the `JavaCompile` task,
209+
or the JDK used to run Gradle in case no toolchain is being used.
210+
This should also include toolchains set using `forkOptions.executable` or `forkOptions.javaHome`,
211+
but only in Gradle 6.7+ where Gradle should expose those as ad-hoc toolchains,
212+
and this hasn't been tested.
213+
214+
[jep396]: https://openjdk.java.net/jeps/396
215+
199216
## Migration from [versions 0.0._x_]
200217

201218
<details>

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

+30-1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,19 @@ Add a dependency to com.google.errorprone:javac with the appropriate version cor
5454
private val HAS_TOOLCHAINS = GradleVersion.current().baseVersion >= GradleVersion.version("6.7")
5555

5656
internal const val TOO_OLD_TOOLCHAIN_ERROR_MESSAGE = "Must not enable ErrorProne when compiling with JDK < 8"
57+
58+
internal val JVM_ARGS_STRONG_ENCAPSULATION = listOf(
59+
"--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
60+
"--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
61+
"--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
62+
"--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
63+
"--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
64+
"--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
65+
"--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
66+
"--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
67+
"--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
68+
"--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED"
69+
)
5770
}
5871

5972
override fun apply(project: Project) {
@@ -101,7 +114,7 @@ Add a dependency to com.google.errorprone:javac with the appropriate version cor
101114
.compilerArgumentProviders
102115
.add(ErrorProneCompilerArgumentProvider(errorproneOptions))
103116

104-
if (HAS_TOOLCHAINS || JavaVersion.current().isJava8) {
117+
if (HAS_TOOLCHAINS || JavaVersion.current().run { isJava8 || isJava16Compatible }) {
105118
inputs.files(
106119
providers.provider {
107120
when {
@@ -125,10 +138,13 @@ Add a dependency to com.google.errorprone:javac with the appropriate version cor
125138
when {
126139
targetVersion < 8 -> throw UnsupportedOperationException(TOO_OLD_TOOLCHAIN_ERROR_MESSAGE)
127140
targetVersion == 8 -> configureErrorProneJavac()
141+
targetVersion >= 16 -> configureForJava16plus()
128142
}
129143
}
130144
JavaVersion.current().isJava8 && (!options.isFork || (options.forkOptions.javaHome == null && options.forkOptions.executable == null)) ->
131145
configureErrorProneJavac()
146+
JavaVersion.current().isJava16Compatible && (!options.isFork || (options.forkOptions.javaHome == null && options.forkOptions.executable == null)) ->
147+
configureForJava16plus()
132148
}
133149
}
134150
}
@@ -174,6 +190,16 @@ Add a dependency to com.google.errorprone:javac with the appropriate version cor
174190
}
175191
}
176192
}
193+
194+
private fun JavaCompile.configureForJava16plus() {
195+
// https://github.com/google/error-prone/issues/1157#issuecomment-769289564
196+
if (!options.isFork) {
197+
options.isFork = true
198+
// reset forkOptions in case they were configured
199+
options.forkOptions = ForkOptions()
200+
}
201+
options.forkOptions.jvmArgs!!.addAll(JVM_ARGS_STRONG_ENCAPSULATION)
202+
}
177203
}
178204

179205
internal class ErrorProneCompilerArgumentProvider(
@@ -199,3 +225,6 @@ internal class ErrorProneCompilerArgumentProvider(
199225

200226
internal val TEST_SOURCE_SET_NAME_REGEX =
201227
"""^(t|.*T)est(\p{javaUpperCase}.*)?$""".toRegex()
228+
229+
internal val JavaVersion.isJava16Compatible: Boolean
230+
get() = this < JavaVersion.VERSION_HIGHER && this >= JavaVersion.toVersion(16)

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

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.google.common.truth.Truth.assertThat
44
import com.google.common.truth.TruthJUnit.assume
55
import java.io.File
66
import java.util.Properties
7+
import org.gradle.api.JavaVersion
78
import org.gradle.testkit.runner.TaskOutcome
89
import org.gradle.util.GradleVersion
910
import org.junit.Before
@@ -15,6 +16,7 @@ class AndroidIntegrationTest : AbstractPluginIntegrationTest() {
1516
@Before
1617
fun setupAndroid() {
1718
assume().that(GradleVersion.version(testGradleVersion)).isAtLeast(GradleVersion.version("6.5"))
19+
assume().withMessage("isJava16Compatible").that(JavaVersion.current()).isLessThan(JavaVersion.VERSION_16)
1820
assertThat(androidSdkHome).isNotEmpty()
1921

2022
Properties().apply {

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

+5
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package net.ltgt.gradle.errorprone
22

33
import com.google.common.truth.StringSubject
44
import com.google.common.truth.Truth.assertThat
5+
import com.google.common.truth.TruthJUnit.assume
56
import com.google.errorprone.ErrorProneOptions.Severity
67
import com.google.errorprone.InvalidCommandLineOptionException
78
import org.gradle.api.InvalidUserDataException
9+
import org.gradle.api.JavaVersion
810
import org.gradle.api.model.ObjectFactory
911
import org.gradle.api.provider.ProviderFactory
1012
import org.gradle.api.tasks.SourceSet
@@ -28,6 +30,9 @@ class ErrorProneOptionsTest {
2830

2931
@JvmStatic @BeforeClass
3032
fun setup() {
33+
// FIXME: remove when updating to Gradle 7
34+
assume().that(JavaVersion.current()).isLessThan(JavaVersion.VERSION_16)
35+
3136
ProjectBuilder.builder().withProjectDir(projectDir.root).build().let { project ->
3237
objects = project.objects
3338
providers = project.providers

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

+18
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package net.ltgt.gradle.errorprone
33
import com.google.common.truth.Truth.assertThat
44
import com.google.common.truth.TruthJUnit.assume
55
import java.io.File
6+
import org.gradle.api.JavaVersion
67
import org.gradle.testkit.runner.TaskOutcome
78
import org.gradle.util.GradleVersion
89
import org.junit.Before
@@ -27,6 +28,19 @@ class ErrorPronePluginIntegrationTest : AbstractPluginIntegrationTest() {
2728
}
2829
""".trimIndent()
2930
)
31+
if (JavaVersion.current().isJava16Compatible && GradleVersion.version(testGradleVersion) < GradleVersion.version("7.0-milestone-3")) {
32+
// https://melix.github.io/blog/2021/03/gradle-java16.html
33+
buildFile.appendText(
34+
"""
35+
36+
allprojects {
37+
tasks.withType<JavaCompile>().configureEach {
38+
options.isIncremental = false
39+
}
40+
}
41+
""".trimIndent()
42+
)
43+
}
3044
}
3145

3246
@Test
@@ -155,6 +169,10 @@ class ErrorPronePluginIntegrationTest : AbstractPluginIntegrationTest() {
155169
@Test
156170
fun `is configuration-cache friendly`() {
157171
assume().that(GradleVersion.version(testGradleVersion)).isAtLeast(GradleVersion.version("6.6"))
172+
assume().that(
173+
JavaVersion.current().isJava16Compatible &&
174+
GradleVersion.version(testGradleVersion) < GradleVersion.version("7.0-milestone-3")
175+
).isFalse()
158176

159177
// given
160178
writeSuccessSource()

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

+8
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
package net.ltgt.gradle.errorprone
22

33
import com.google.common.truth.Truth.assertThat
4+
import com.google.common.truth.TruthJUnit.assume
5+
import org.gradle.api.JavaVersion
46
import org.gradle.testkit.runner.TaskOutcome
7+
import org.gradle.util.GradleVersion
58
import org.junit.Test
69

710
class GroovyDslIntegrationTest : AbstractPluginIntegrationTest() {
811
override fun setupProject() {
12+
assume().that(
13+
JavaVersion.current().isJava16Compatible &&
14+
GradleVersion.version(testGradleVersion) < GradleVersion.version("7.0-milestone-3")
15+
).isFalse()
16+
917
settingsFile = testProjectDir.newFile("settings.gradle")
1018
buildFile = testProjectDir.newFile("build.gradle").apply {
1119
appendText(

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

+36-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class Java8IntegrationTest : AbstractPluginIntegrationTest() {
2121
"""$JVM_ARG_BOOTCLASSPATH.*${fileSeparator}com\.google\.errorprone${fileSeparator}javac$fileSeparator$escapedErrorproneJavacVersion$fileSeparator.*${fileSeparator}javac-$escapedErrorproneJavacVersion.jar[${File.pathSeparator}${System.lineSeparator()}]"""
2222
.toPattern()
2323
}
24+
private val JVM_ARGS_STRONG_ENCAPSULATION = ErrorPronePlugin.JVM_ARGS_STRONG_ENCAPSULATION.joinToString(prefix = JVM_ARG, separator = JVM_ARG)
2425

2526
private fun jvmArg(argPrefix: String) = "$JVM_ARG$argPrefix"
2627
}
@@ -53,12 +54,24 @@ class Java8IntegrationTest : AbstractPluginIntegrationTest() {
5354
compileJava.finalizedBy(displayCompileJavaOptions)
5455
""".trimIndent()
5556
)
57+
if (JavaVersion.current().isJava16Compatible && GradleVersion.version(testGradleVersion) < GradleVersion.version("7.0-milestone-3")) {
58+
// https://melix.github.io/blog/2021/03/gradle-java16.html
59+
buildFile.appendText(
60+
"""
61+
62+
tasks.withType<JavaCompile>().configureEach {
63+
options.isIncremental = false
64+
}
65+
""".trimIndent()
66+
)
67+
}
5668
writeSuccessSource()
5769
}
5870

5971
@Test
60-
fun `does not configure forking in non-Java 8 VM`() {
72+
fun `does not configure forking in non-Java 8 or 16+ VM`() {
6173
assume().withMessage("isJava8").that(JavaVersion.current().isJava8).isFalse()
74+
assume().withMessage("isJava16Compatible").that(JavaVersion.current()).isLessThan(JavaVersion.VERSION_16)
6275

6376
// when
6477
buildWithArgs("compileJava").also { result ->
@@ -108,9 +121,30 @@ class Java8IntegrationTest : AbstractPluginIntegrationTest() {
108121
}
109122
}
110123

124+
@Test
125+
fun `configure forking in Java 16+ VM`() {
126+
assume().withMessage("isJava16Compatible").that(JavaVersion.current()).isAtLeast(JavaVersion.VERSION_16)
127+
128+
// when
129+
buildWithArgs("compileJava").also { result ->
130+
// then
131+
assertThat(result.task(":compileJava")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
132+
assertThat(result.output).contains(FORKED)
133+
assertThat(result.output).contains(JVM_ARGS_STRONG_ENCAPSULATION)
134+
}
135+
136+
// check that it doesn't mess with task avoidance
137+
138+
// when
139+
buildWithArgs("compileJava").also { result ->
140+
// then
141+
assertThat(result.task(":compileJava")?.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
142+
}
143+
}
144+
111145
@Test
112146
fun `does not configure forking if Error Prone is disabled`() {
113-
assume().withMessage("isJava8").that(JavaVersion.current().isJava8).isTrue()
147+
assume().withMessage("isJava8Or16plus").that(JavaVersion.current().run { isJava8 || isJava16Compatible }).isTrue()
114148

115149
// given
116150
buildFile.appendText(

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

+23
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.TaskOutcome
67
import org.gradle.util.GradleVersion
78
import org.junit.Test
@@ -45,6 +46,17 @@ class ManualConfigurationIntegrationTest : AbstractPluginIntegrationTest() {
4546
}
4647
""".trimIndent()
4748
)
49+
if (JavaVersion.current().isJava16Compatible && GradleVersion.version(testGradleVersion) < GradleVersion.version("7.0-milestone-3")) {
50+
// https://melix.github.io/blog/2021/03/gradle-java16.html
51+
buildFile.appendText(
52+
"""
53+
54+
tasks.withType<JavaCompile>().configureEach {
55+
options.isIncremental = false
56+
}
57+
""".trimIndent()
58+
)
59+
}
4860
writeFailureSource()
4961

5062
// when
@@ -88,6 +100,17 @@ class ManualConfigurationIntegrationTest : AbstractPluginIntegrationTest() {
88100
}
89101
""".trimIndent()
90102
)
103+
if (JavaVersion.current().isJava16Compatible && GradleVersion.version(testGradleVersion) < GradleVersion.version("7.0-milestone-3")) {
104+
// https://melix.github.io/blog/2021/03/gradle-java16.html
105+
buildFile.appendText(
106+
"""
107+
108+
tasks.withType<JavaCompile>().configureEach {
109+
options.isIncremental = false
110+
}
111+
""".trimIndent()
112+
)
113+
}
91114
writeFailureSource()
92115

93116
// Error Prone is disabled by default, so compilation should succeed.

0 commit comments

Comments
 (0)