Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 68 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,68 @@ jobs:
annotate_only: true
job_summary: true

# The tests of `build` on the newer JDKs, in parallel with everything else: nothing waits for these
# jobs and nothing they build is kept. The code is still compiled for Java 21 by JDK 21, as in the
# release; -PjavaRuntime moves only the test JVMs, and with them the javac whose internals the
# formatter parses with. The IntelliJ plugin's tests stay on the runtime of the IDE they start.
# JDK 21 itself is `build`, the check the main branch requires.
jdk:
name: build (JDK ${{ matrix.jdk }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- jdk: 25
gradle_args: ''
gradle_args_reason: ''
- jdk: 26
gradle_args: ''
gradle_args_reason: ''
- jdk: 27
gradle_args: -x :gradle-open-java-format:test
gradle_args_reason: >-
Gradle 9.7.1 does not run on Java 27 yet, and the Gradle plugin's tests run their TestKit builds on
the test JVM, where they fail with "Unsupported class file major version 71". Those tests are left
out until the wrapper's Gradle supports Java 27.
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install JDK ${{ matrix.jdk }} for the tests
id: test-jdk
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: temurin
java-version: ${{ matrix.jdk }}

# Installed last, so that it is the JAVA_HOME Gradle runs and compiles on.
- name: Install JDK 21
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: temurin
java-version: '21'

- name: Explain the extra Gradle arguments
if: ${{ matrix.gradle_args != '' }}
env:
GRADLE_ARGS: ${{ matrix.gradle_args }}
REASON: ${{ matrix.gradle_args_reason }}
run: echo "::notice title=JDK ${{ matrix.jdk }} runs with $GRADLE_ARGS::$REASON"

# Gradle does not look into the runner's tool cache, so it is told where the test JDK is.
- name: Build
run: >-
./gradlew test -PjavaRuntime=${{ matrix.jdk }} ${{ matrix.gradle_args }}
-Porg.gradle.java.installations.paths=${{ steps.test-jdk.outputs.path }}

- name: Publish Test Report
uses: mikepenz/action-junit-report@a9170d5795813c01ab4901ffb045b52bab4ab09d # v6.5.0
if: ${{ !cancelled() }}
with:
report_paths: '**/build/test-results/test/*.xml'
annotate_only: true
job_summary: true

# One explicit job per target, so every binary we ship is traceable to a named run.
native:
name: native (${{ matrix.platform }})
Expand Down Expand Up @@ -87,7 +149,8 @@ jobs:
env:
JDK21_HOME: ${{ steps.jdk21.outputs.path }}

# The binary itself on a file that needs formatting, a formatted one and one that does not parse.
# The binary itself on a file that needs formatting, a formatted one, one that does not parse and
# one in Java 25 syntax.
- name: Smoke-test the binary
run: |
binary="$PWD/$(ls open-java-format-native/build/native/nativeCompile/open-java-format-* | grep -v '\.txt$')"
Expand All @@ -100,6 +163,10 @@ jobs:
printf 'class B {\n' > B.java
set +e; "$binary" B.java; status=$?; set -e
test "$status" -eq 2
# Java 25 syntax: a compact source file with a module import and an unnamed pattern.
printf 'import module java.base;\nrecord Box(int a,int b){}\nvoid main(){Object o=new Box(1,2);if(o instanceof Box(_,_)){IO.println(List.of(1));}}\n' > C.java
"$binary" --replace C.java
printf 'import module java.base;\n\nrecord Box(int a, int b) {}\n\nvoid main() {\n Object o = new Box(1, 2);\n if (o instanceof Box(_, _)) {\n IO.println(List.of(1));\n }\n}\n' | diff - C.java

- name: Test the plugins against the image
run: ./gradlew -PnativeImage=true :open-java-format-jdk-bootstrap:test :gradle-open-java-format:test
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ mise trust && mise install
./gradlew test # what the CI build job runs
```

`-PjavaRuntime=25` runs the same tests on JDK 25, as CI's `jdk` jobs do for 25, 26 and 27. The code
is compiled for Java 21 either way. Gradle has to find that JDK: installed with mise, or named with
`-Porg.gradle.java.installations.paths=/path/to/jdk`.

Nothing inside the build downloads a JDK. `gradle.properties` turns toolchain auto-download off and
reads the installations from `JDK21_HOME` and `GRAALVM_HOME`, so a missing JDK is an error you can
read rather than a silent download.
Expand Down
9 changes: 8 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ allprojects {

tasks.withType(Test).configureEach {
jvmArgs(javacInternalExports.collect { "--add-exports=${it}=ALL-UNNAMED".toString() })
// The short form gave CI logs only "FormatterException at FormatterIntegrationTest.java:76", never what
// the formatter complained about, nor the output of a TestKit build that failed.
testLogging.exceptionFormat = 'full'
}

tasks.withType(Javadoc).configureEach {
Expand Down Expand Up @@ -127,7 +130,11 @@ subprojects {
}
}

// The JDK the tests run on, and with it the javac whose internals the formatter parses with. The code is
// compiled for 21 whatever this says. CI's `jdk` jobs pass -PjavaRuntime=25 and so on.
ext.javaRuntime = providers.gradleProperty('javaRuntime').getOrElse('21')

javaVersions {
libraryTarget = 21
runtime = 21
runtime = javaRuntime
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@
}
]
},
{
"type": "com.sun.source.tree.ImportTree",
"methods": [
{
"name": "isModule",
"parameterTypes": []
}
]
},
{
"type": "com.sun.tools.javac.parser.JavaTokenizer",
"fields": [
Expand All @@ -151,6 +160,28 @@
}
]
},
{
"type": "com.sun.tools.javac.tree.EndPosTable"
},
{
"type": "com.sun.tools.javac.tree.JCTree",
"methods": [
{
"name": "getEndPosition",
"parameterTypes": [
"com.sun.tools.javac.tree.EndPosTable"
]
}
]
},
{
"type": "com.sun.tools.javac.tree.JCTree$JCCompilationUnit",
"fields": [
{
"name": "endPositions"
}
]
},
{
"type": "java.io.Serializable"
},
Expand Down
3 changes: 3 additions & 0 deletions open-java-format/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ tasks.named("test") {
// Run all classes and tests in parallel
// https://junit.org/junit5/docs/current/user-guide/#writing-tests-parallel-execution
systemProperty 'junit.jupiter.execution.parallel.mode.default', 'concurrent'
// FormatterVersionTest fails when the tests run on another JDK than the one asked for, so a CI leg
// cannot pass on the wrong JDK and skip the tests that need a newer parser.
systemProperty 'expectedJavaVersion', rootProject.ext.javaRuntime
}

javaVersion {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,8 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOExcept
};
Log.instance(context).useSource(source);
ParserFactory parserFactory = ParserFactory.instance(context);
JavacParser parser = parserFactory.newParser(
sourceText, /*keepDocComments=*/ true, /*keepEndPos=*/ true, /*keepLineMap=*/ true);
JavacParser parser =
Trees.newParser(parserFactory, sourceText, /*keepDocComments=*/ true, /*keepLineMap=*/ true);
unit = parser.parseCompilationUnit();
unit.sourcefile = source;

Expand Down
Loading
Loading