fix: keep the Gradle classpath order in packaged launchers - #729
Merged
Merged
Conversation
jpackage has no classpath option: it lists every file of --input, sorted by name, after the main jar. When two JARs define the same classes, the packaged app therefore loaded whichever sorted first, while ./gradlew run follows the runtime-classpath order. Jewel apps hit it: the IntelliJ icon libraries pull kotlinx-coroutines-core-jvm-1.10.2-intellij-2, which sorted before 1.11.0 and failed at run time with NoSuchMethodError (Job.cancel$default). The launcher .cfg classpath is now rewritten in classpath order right after jpackage, before macOS signing and the Linux pathing-jar collapse. The sandboxed strip task, whose output directory loses the order, records it in .classpath-order for the package task.
jewel-demo, scheduler-demo and system-info-demo compile to class file 69 but were packaged with the Gradle JVM's runtime, so the distributable died with UnsupportedClassVersionError. Resolve a 25 toolchain as jewel-tabs-demo does.
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.
Summary
app.classpathfrom the--inputdirectory, sorted by name. When two JARs define the same classes, a packaged app loaded whichever sorted first, while./gradlew runfollows Gradle's runtime-classpath order. Every Jewel app was affected: the IntelliJ icon libraries pullkotlinx-coroutines-core-jvm-1.10.2-intellij-2, which sorted before1.11.0and made the packaged app fail withNoSuchMethodError: Job.cancel$default.LauncherClasspathOrderrewrites the launcher.cfgclasspath in classpath order (main JAR first; unknown entries keep their relative place;jvm.cfgand line endings untouched). It runs right after jpackage, before macOS signing and before the Linux pathing-jar collapse, both of which preserve order..classpath-order;AbstractJPackageTask.classpathOrderFileconsumes it.jewel-demo,scheduler-demoandsystem-info-democompile to class file 69 but were packaged with the Gradle JVM's runtime (21 here), so the distributable died withUnsupportedClassVersionError. They now resolve a JDK 25 toolchain, asjewel-tabs-demoalready does.Test plan
LauncherClasspathOrderTest(5 cases); full:plugin:test— 438 tests, 0 failures;:plugin:ktlintCheckjewel-tabs-democreateDistributable, before → after: the.cfglists the fork before1.11.0→1.11.0before the fork-Xlog:class+load:Job,JobSupport,BuildersKtload from the fork before the fix, from1.11.0after it (zero classes loaded from the fork)Job.cancel()with the fork on its classpath hitNoSuchMethodErrorbefore the fix and runs clean after itjewel-demo,scheduler-demo,system-info-demodistributables ship a 25.0.3 runtime and stay up when launched (scheduler-demodid not start before)Known, not addressed here
system-info-demologsIllegalAccessErrorin lets-plot (SkiaContext2dcalling the privateMatrix33(float[])constructor) — the same under./gradlew run, so it's a lets-plot/skiko version mismatch, not packaging.createRuntimeImagefailure (jlinkAbstractMethodError), seen once with three jlink runs in parallel in the same build; did not reproduce when rerun.