Say how to run the formatter when the Gradle JVM hides javac - #79
Merged
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #19.
The bug
In a build that applies
dev.openjavaformat.java-formatand nothing else,./gradlew formatDiffprintedFormatting <file>and then failed with the raw error from the formatter's first file: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 forformatDiffand for the Spotless step, checks up front whetherjdk.compilerexports 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:The message lists only the packages that are missing: a Gradle daemon exports
com.sun.tools.javac.apiand.utilon 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 thejdk.compilermodule at all (a JRE) gets its own one-line message.Checked
Formatting <file>; with this change and nogradle.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 noIllegalAccessErrorand noFormattingline appear, and that the file is not written. It lives in its own class becausePalantirJavaFormatPluginTestruns only in thenativeCI jobs, which have the binary../gradlew :gradle-open-java-format:test: 23 tests, all green;:gradle-open-java-format:checkpasses.Follow-up
The Gradle page on the website says that without one of the two settings
formatDifffails with anIllegalAccessError; once this is released it fails with the message above, and that sentence can go.