Skip to content

Commit 5877180

Browse files
Apply errorprone and nullaway
The main java on which Gradle is running is 25 (via Toolchains), therefore compilation is done with options.release = 17 flag for compatiblity. It no longer means that project is compiled with Java 17 - it's compiled with Java 25, with bytecode compatibility with Java 17.
1 parent 7b63486 commit 5877180

17 files changed

Lines changed: 177 additions & 65 deletions

.github/copilot-instructions.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121

2222
- **Bootstrap:** No explicit bootstrap step; dependencies managed via Gradle.
2323
- **Build:**
24-
- Run `./gradlew build` from the repository root (or `gradlew.bat build` on Windows).
24+
- Run `./gradlew` from the repository root (or `./gradlew.bat` on Windows), default tasks are `spotlessApply build`
25+
so this is a convenience method.
26+
- Run `./gradlew build` only if you explicitly want to exclude code formatting for some reason.
2527
- Java 17+ required.
2628
- **Test:**
2729
- Tests run automatically with `./gradlew build` or separately via `./gradlew test`.

.github/workflows/gradle-build.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,22 @@ on:
2424

2525
jobs:
2626
build:
27-
name: Build
27+
name: Build (Java ${{ matrix.java }})
2828
runs-on: ubuntu-latest
29+
strategy:
30+
fail-fast: false
31+
matrix:
32+
java: [17, 21, 25]
2933
permissions:
3034
contents: read
3135
steps:
3236
- name: Checkout
3337
uses: actions/checkout@v6
3438

35-
- name: Set up JDK 17
39+
- name: Set up JDK ${{ matrix.java }}
3640
uses: actions/setup-java@v5
3741
with:
38-
java-version: "17"
42+
java-version: "${{ matrix.java }}"
3943
distribution: "temurin"
4044

4145
# Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies.

.github/workflows/gradle-dependency-submission.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: Dependency Submission
22

3-
# workflow limited to only files related to gradle dependencies
3+
# workflow limited to only files related to Gradle dependencies
44
on:
55
push:
66
branches:

README.md

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -216,21 +216,31 @@ dependency.
216216
<details>
217217
<summary><b>Expand...</b></summary>
218218

219-
Gradle **9.x+** requires **Java 17+** to run, but higher Java versions can also be used. All modules of this project are
220-
compiled using a **Java 17 toolchain**, so the produced artifacts are compatible with **Java 17**, regardless of the
221-
Java version Gradle runs on.
219+
Gradle **9.x+** requires **Java 17** or higher to run. For building the project, Gradle automatically picks up **Java
220+
25** via **toolchains** and the `foojay-resolver-convention` plugin. This Java version is needed because the project
221+
uses **ErrorProne** and **NullAway** for static nullness analysis.
222+
223+
The produced artifacts are compatible with **Java 17** thanks to `options.release = 17` in Gradle `JavaCompile` tasks.
224+
This means that regardless of the Java version used to run Gradle, the resulting bytecode remains compatible.
225+
226+
The **default Gradle tasks** include `spotlessApply` (for code formatting) and `build` (for compilation and tests).The
227+
simplest way to build the project is to run:
222228

223229
```bash
224-
./gradlew build
230+
./gradlew
225231
```
226232

227-
To execute tests use `test` task.
233+
---
234+
235+
To **execute tests** use `test` task. Tests do not change `options.release` so newer Java API can be used.
228236

229237
```bash
230238
./gradlew test
231239
```
232240

233-
To format the code according to the style defined in [`build.gradle.kts`](./build.gradle.kts) rules use `spotlessApply`
241+
---
242+
243+
To **format the code** according to the style defined in [`build.gradle.kts`](./build.gradle.kts) rules use `spotlessApply`
234244
task. **Note** that **building will fail** if code is not properly formatted.
235245

236246
```bash
@@ -245,7 +255,9 @@ for local development and builds you can choose to skip it and update the year l
245255
./gradlew spotlessApply -Pspotless.license-year-enabled
246256
```
247257

248-
To publish the built artifacts to local Maven repository, use `publishToMavenLocal` task.
258+
---
259+
260+
To **publish** the built artifacts to **local Maven repository**, use `publishToMavenLocal` task.
249261

250262
```bash
251263
./gradlew publishToMavenLocal

build.gradle.kts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,15 @@ spotless {
4343
target("**/src/**/package-info.java", "**/src/**/module-info.java")
4444

4545
// License headers in these files are not formatted with standard java group, so we need to use custom settings.
46-
// The regex is designed find out where the code starts in these files, so the license header can be placed
47-
// before it. The code starts with either "package", "import", "module" or "/**" in case of a global JavaDoc.
48-
val delimiter = "^(package|import|module|/\\*\\*)"
46+
// The regex is designed to find out where the code starts in these files, so the license header can be placed
47+
// before it.
48+
//
49+
// The code starts with either:
50+
//
51+
// - any annotation (ex. @NullMarked before package declaration),
52+
// - package, module or import declaration,
53+
// - "/**" in case of a pre-package (or pre-module) JavaDoc.
54+
val delimiter = "^(\\s*@|package|import|module|/\\*\\*)"
4955

5056
licenseHeaderFile(licenseHeader, delimiter).updateYearWithLatest(updateLicenseYear)
5157
}
@@ -84,3 +90,5 @@ spotless {
8490
lineEndings = LineEnding.UNIX
8591
}
8692
}
93+
94+
defaultTasks("spotlessApply", "build")

buildSrc/build.gradle.kts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,14 @@ plugins {
88
version = "current"
99

1010
repositories {
11+
gradlePluginPortal()
1112
mavenCentral()
1213
}
14+
15+
dependencies {
16+
implementation(plugin(libs.plugins.errorprone))
17+
}
18+
19+
fun plugin(plugin: Provider<PluginDependency>): Provider<String> = plugin.map {
20+
"${it.pluginId}:${it.pluginId}.gradle.plugin:${it.version}"
21+
}

buildSrc/settings.gradle.kts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Note that usage of version catalogs in buildSrc is not as straightforward as in regular modules.
2+
// For more information, see:
3+
// https://docs.gradle.org/current/userguide/version_catalogs.html#sec:buildsrc-version-catalog
4+
pluginManagement {
5+
repositories {
6+
gradlePluginPortal()
7+
}
8+
}
9+
10+
dependencyResolutionManagement {
11+
versionCatalogs {
12+
create("libs") {
13+
from(files("../gradle/libs.versions.toml"))
14+
}
15+
}
16+
}
17+
18+
rootProject.name = "buildSrc"
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import net.ltgt.gradle.errorprone.errorprone
2+
3+
plugins {
4+
id("internal.java-convention")
5+
id("net.ltgt.errorprone")
6+
}
7+
8+
tasks.withType<JavaCompile>().configureEach {
9+
options.errorprone {
10+
disableAllChecks.set(true)
11+
error("NullAway")
12+
option("NullAway:OnlyNullMarked", "true")
13+
option("NullAway:JSpecifyMode", "true")
14+
}
15+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
2+
import org.gradle.api.tasks.testing.logging.TestLogEvent
3+
4+
plugins {
5+
id("internal.common-convention")
6+
id("java-library")
7+
}
8+
9+
// The project is built using a JDK 25 toolchain, but the main sources are compiled with --release 17.
10+
//
11+
// This means:
12+
// - Gradle can run on any JDK 17+,
13+
// - javac from JDK 25 is used,
14+
// - the produced bytecode and available Java API for main sources are restricted to Java 17.
15+
//
16+
// This setup lets us use a modern JDK for tooling (ErrorProne, NullAway) while keeping Java 17 binary compatibility for
17+
// library consumers.
18+
//
19+
// Tests are NOT compiled with --release XYZ, so they may use newer Java APIs.
20+
21+
java {
22+
toolchain.languageVersion = JavaLanguageVersion.of(25)
23+
}
24+
25+
tasks.withType<JavaCompile>().configureEach {
26+
options.compilerArgs.add("-parameters")
27+
options.encoding = "UTF-8"
28+
}
29+
30+
tasks.named<JavaCompile>("compileJava") {
31+
options.release = 17
32+
}
33+
34+
tasks.withType<Test>().configureEach {
35+
useJUnitPlatform()
36+
37+
testLogging {
38+
events(TestLogEvent.FAILED, TestLogEvent.PASSED, TestLogEvent.SKIPPED)
39+
exceptionFormat = TestExceptionFormat.SHORT
40+
showStandardStreams = true
41+
}
42+
43+
// For resolving warnings from mockito.
44+
jvmArgs("-XX:+EnableDynamicAgentLoading")
45+
46+
systemProperty("user.language", "en")
47+
systemProperty("user.country", "US")
48+
}
49+
50+
tasks.withType<Javadoc>().configureEach {
51+
javadocTool = javaToolchains.javadocToolFor { languageVersion.set(JavaLanguageVersion.of(17)) }
52+
}
53+
54+
tasks.withType<Jar>().configureEach {
55+
manifest {
56+
attributes["Implementation-Title"] = project.name
57+
attributes["Implementation-Version"] = project.version
58+
attributes["Created-By"] = "Gradle ${gradle.gradleVersion}"
59+
}
60+
from("${rootProject.rootDir}/LICENSE") {
61+
into("META-INF/")
62+
rename { "LICENSE.txt" }
63+
}
64+
}

buildSrc/src/main/kotlin/internal.java-library-convention.gradle.kts

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,13 @@
1-
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
2-
31
plugins {
4-
id("internal.common-convention")
2+
id("internal.java-convention")
53
id("java-library")
64
}
75

86
java {
9-
toolchain.languageVersion = JavaLanguageVersion.of(17)
107
withSourcesJar()
118
withJavadocJar()
129
}
1310

14-
tasks.withType<JavaCompile>().configureEach {
15-
options.compilerArgs.add("-parameters")
16-
options.encoding = "UTF-8"
17-
}
18-
19-
tasks.withType<Jar>().configureEach {
20-
manifest {
21-
attributes["Implementation-Title"] = project.name
22-
attributes["Implementation-Version"] = project.version
23-
attributes["Build-Jdk-Spec"] = java.toolchain.languageVersion.get().toString()
24-
attributes["Created-By"] = "Gradle ${gradle.gradleVersion}"
25-
}
26-
from("${rootProject.rootDir}/LICENSE") {
27-
into("META-INF/")
28-
rename { "LICENSE.txt" }
29-
}
30-
}
31-
32-
tasks.withType<Test>().configureEach {
33-
useJUnitPlatform()
34-
35-
testLogging {
36-
events("passed", "skipped", "failed", "standardOut", "standardError")
37-
exceptionFormat = TestExceptionFormat.SHORT
38-
showStandardStreams = true
39-
}
40-
41-
// For resolving warnings from mockito.
42-
jvmArgs("-XX:+EnableDynamicAgentLoading")
43-
44-
systemProperty("user.language", "en")
45-
systemProperty("user.country", "US")
46-
}
47-
4811
// buildSrc/src/main/kotlin/internal.common-convention.gradle.kts - "printVersion" task definition
4912
tasks.withType<PublishToMavenLocal>().configureEach {
5013
finalizedBy("printVersion")

0 commit comments

Comments
 (0)