Skip to content

Say how to run the formatter when the Gradle JVM hides javac - #79

Merged
abashev merged 2 commits into
mainfrom
format-diff-javac-exports-message
Sep 25, 2026
Merged

abashev merged 2 commits into
mainfrom
format-diff-javac-exports-message

Conversation

@abashev

@abashev abashev commented Sep 25, 2026

Copy link
Copy Markdown
Collaborator

Closes #19.

The bug

In a build that applies dev.openjavaformat.java-format and nothing else, ./gradlew formatDiff printed Formatting <file> and then failed with the raw error from the formatter's first file:

> class com.palantir.javaformat.java.JavaInput (in unnamed module @0x…) cannot access class
  com.sun.tools.javac.parser.Tokens$TokenKind (in module jdk.compiler) because module jdk.compiler
  does not export com.sun.tools.javac.parser to unnamed module @0x…

The formatter reads javac's internals, and a plain Gradle daemon exports none of them. Nothing in the error says what to change.

The fix

JavaFormatExtension, which loads the formatter into the daemon for formatDiff and for the Spotless step, checks up front whether jdk.compiler exports the five packages the formatter needs to the formatter's class loader (Module.isExported). If not, it fails before the first file with the two settings that make the formatter run and the page that explains them:

> open-java-format cannot run inside this Gradle JVM: module jdk.compiler does not export com.sun.tools.javac.file, com.sun.tools.javac.parser, com.sun.tools.javac.tree to it. Set one of these in gradle.properties:

    openjavaformat.native.formatter=true
        runs the formatter as a native binary, outside the Gradle JVM (Linux with glibc, macOS, Windows on x86-64)

    org.gradle.jvmargs=--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
        opens javac's packages to the Gradle JVM; if the file already sets org.gradle.jvmargs, add the flags to that line

  See https://openjavaformat.dev/get-started/gradle/#choose-how-the-formatter-runs

The message lists only the packages that are missing: a Gradle daemon exports com.sun.tools.javac.api and .util on its own, the other three it does not. The Spotless step goes through the same loader, so a Spotless build without the flags gets the same message instead of the raw error. The native path does not load the formatter into the daemon and is unchanged. A JVM without the jdk.compiler module at all (a JRE) gets its own one-line message.

Checked

  • In a consumer project against the plugin published to a scratch Maven repository, as Explain the fix when formatDiff hits the jdk.compiler IllegalAccessError #19 asks, with Gradle 9.7.1 on Temurin 21: on main, the raw error above after Formatting <file>; with this change and no gradle.properties, the message above, exit code 1, and the file untouched; with the five flags, the changed lines are formatted. The native variant was not run here: it needs the native image built and published, and its code path is not touched.
  • FormatDiffWithoutJavacExportsTest, a TestKit project without the flags, pins the message, that no IllegalAccessError and no Formatting line appear, and that the file is not written. It lives in its own class because PalantirJavaFormatPluginTest runs only in the native CI jobs, which have the binary.
  • ./gradlew :gradle-open-java-format:test: 23 tests, all green; :gradle-open-java-format:check passes.

Follow-up

The Gradle page on the website says that without one of the two settings formatDiff fails with an IllegalAccessError; once this is released it fails with the message above, and that sentence can go.

In a build that applies the plugin and nothing else, formatDiff failed
with the raw error from the formatter's first file (#19):

    class com.palantir.javaformat.java.JavaInput (in unnamed module @0x…)
    cannot access class com.sun.tools.javac.parser.Tokens$TokenKind (in
    module jdk.compiler) because module jdk.compiler does not export
    com.sun.tools.javac.parser to unnamed module @0x…

The formatter reads javac's internals, and a plain Gradle daemon exports
none of them. Nothing in the error says what to change, and "Formatting
<file>" was printed first for a run that could not succeed.

JavaFormatExtension, which loads the formatter into the daemon for
formatDiff and for the Spotless step, now checks up front whether
jdk.compiler exports the five packages the formatter needs to the
formatter's class loader. If not, it fails before the first file with
the two settings that make the formatter run, the native binary or the
--add-exports flags in org.gradle.jvmargs, and the page that explains
them. The message lists only the packages that are missing: a Gradle
daemon exports com.sun.tools.javac.api and .util on its own, the other
three it does not. The native path does not load the formatter into the
daemon and is unchanged.

Checked in a consumer project against the plugin published to a scratch
repository, as #19 asks: without gradle.properties the build fails with
the message and touches no file, with the five flags it formats the
changed lines. FormatDiffWithoutJavacExportsTest pins the message in
the default test task; PalantirJavaFormatPluginTest could not hold it,
because that class runs only with the native binary at hand.
abashev added a commit to openjavaformat/docs that referenced this pull request Sep 25, 2026
openjavaformat/open-java-format#79 makes formatDiff stop before the
first file when the Gradle JVM does not export javac's packages, with a
message that names the two settings, so the sentence that described the
raw IllegalAccessError and the module it mentioned no longer matches.
The page now says to pick one of the two settings and what happens
without either.
The same message, as a text block with String.formatted instead of a
String.format over concatenated pieces with %n, so that it reads in the
source the way it prints. The line-continuation backslashes keep the
source within the column limit without adding line breaks to the text.
@abashev
abashev merged commit b48ba48 into main Sep 25, 2026
15 checks passed
@abashev
abashev deleted the format-diff-javac-exports-message branch September 25, 2026 15:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Explain the fix when formatDiff hits the jdk.compiler IllegalAccessError

1 participant