Publish a Java 11 main artifact and a jdk21 classifier - #73
Merged
Conversation
Both modules now emit two jars from one source tree: the main artifact at class file 55 and a jdk21 classifier at 65. A Java 11 jar loads on 11, 17, 21 and 25, so the default is the one that works everywhere and the classifier is there for consumers who want a 21-targeted build. Nothing in the source required 21. Both modules compile clean at --release 11, which checks the API surface and not just the language level, and no runtime dependency is above class file 55 -- in CFLint's 36-jar classpath the only Java 21 artifacts were cfparser's own two modules. The baseline was raised in 5d21f6c, whose message notes it broke Java 11 and 17 consumers; this restores them without giving up a 21 build. ## The resource copy has to run at process-classes maven-jar-plugin takes a single classesDirectory, and classes-jdk21 holds only .class files, so the classified jar needs the resources copied in or it ships without cfml.dictionary's XML. That copy must not move later than process-classes. Setting outputDirectory on a compiler execution leaks into what the reactor hands dependent modules, so cfml.parsing's tests resolve cfml.dictionary to classes-jdk21. Copy the resources at prepare-package instead and all 326 tests fail with "Problem loading dictionaryconfig.xml", which reads like a missing resource rather than a phase ordering problem. The pom says so at the point where it matters. skipIfEmpty keeps the pom-packaged parent from emitting and deploying a spurious cfparser-VERSION-jdk21.jar, which it did before that was added. ## CI now runs on both gradle.yml gains a java-version axis of 11 and 21 across all three operating systems. Compiling to 11 on a 21 JDK cannot catch a problem that only appears on an 11 runtime, and this environment has no Java 11 available to test with, so the matrix is the only thing that actually exercises it. Gradle builds the Java 11 line only. Maven is what publishes, so the classifier is produced there. Verified: 326 tests, ./gradlew build, CFLint's 675 against the Java 11 parser, and a deploy to a local file repository producing main, jdk21, sources and javadoc jars for both modules with identical class and resource counts, differing only in bytecode level. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GzpZFd4rnE1Yi2sVHAji35
The first attempt put Java 11 straight on the runner and all three 11 jobs failed before compiling anything: UnsupportedClassVersionError: aQute/bnd/gradle/BndBuilderPlugin has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 55.0 The bnd OSGi plugin needs Java 17 or newer to load, so Gradle itself cannot run on an 11 JVM. That is a build-tooling limit and says nothing about the artifacts, which are Java 11 and were fine. Both JDKs are now installed, with 21 last so it becomes JAVA_HOME and the daemon keeps running there. -PjavaTestVersion selects the toolchain that compiles and runs the tests, so the 11 leg exercises a real Java 11 runtime while bnd still loads. sourceCompatibility and targetCompatibility stay at 11 regardless of the toolchain, so both legs test the same class file 55 output on two different runtimes rather than testing two different builds. Verified locally at -PjavaTestVersion=21 and with no property set, both emitting major version 55. The 11 leg cannot be run here: no Java 11 is installed and the proxy blocks Adoptium and foojay, which is the reason this matrix exists. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GzpZFd4rnE1Yi2sVHAji35
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.
Both modules now emit two jars from one source tree:
cfml.parsing-VERSION.jarcfml.parsing-VERSION-jdk21.jarSame for
cfml.dictionary. The default is the one that works everywhere; the classifier is there for consumers who specifically want a 21-targeted build.Nothing in the source required 21
Both modules compile clean at
--release 11— which checks the API surface, not just the language level, so it would catch a Java 12+ method call as well as a language feature. And no runtime dependency is above class file 55:The only Java 21 artifacts in the whole downstream classpath were cfparser's own. The baseline was raised in
5d21f6c, whose message states it broke Java 11 and 17 consumers. This restores them without giving up a 21 build.The resource copy must run at process-classes
maven-jar-plugintakes a singleclassesDirectory, andclasses-jdk21holds only.classfiles — so the classified jar needs resources copied in or it ships withoutcfml.dictionary's 231 XML files.That copy cannot move later than
process-classes. SettingoutputDirectoryon a compiler execution leaks into what the reactor hands dependent modules, socfml.parsing's tests resolvecfml.dictionarytoclasses-jdk21. Copy the resources atprepare-packageinstead and all 326 tests fail with:which reads like a missing resource rather than a phase-ordering problem. I hit exactly that while building this, so the pom carries the explanation at the line where someone would otherwise move it.
skipIfEmptystops the pom-packaged parent emitting and deploying a spuriouscfparser-VERSION-jdk21.jar, which it did until that was added.CI runs on both runtimes
gradle.ymlgains ajava-versionaxis of[11, 21]across all three operating systems, withfail-fast: falseso one platform failing does not mask the others.This matters more than it might look: compiling to 11 on a 21 JDK cannot catch a problem that only appears on an 11 runtime, and this environment has no Java 11 available and cannot download one — the proxy blocks Adoptium and foojay. The matrix is the only thing that actually exercises Java 11.
Gradle builds the Java 11 line only. Maven is what publishes, so the classifier is produced there.
Verification
./gradlew buildmvn deployto a local file repository produces main,jdk21, sources and javadoc for both modules, and no parent jarcfml.dictionary: 20 classes, 231 XML each), differing only in bytecode levelNot verified here
I could not execute the suite on a real Java 11 JVM — hence the CI matrix. Until that runs, Java 11 support rests on bytecode level and API-surface checking, not on observed behaviour.
🤖 Generated with Claude Code
https://claude.ai/code/session_01GzpZFd4rnE1Yi2sVHAji35
Generated by Claude Code