Emit clear error when running JDK cannot compile source level - #12633
Draft
gnodet wants to merge 12 commits into
Draft
Emit clear error when running JDK cannot compile source level#12633gnodet wants to merge 12 commits into
gnodet wants to merge 12 commits into
Conversation
…'s source level When the running JDK does not support the project's --source/--release level (e.g., JDK 17 cannot compile --source 6), Maven now automatically searches configured toolchains for a compatible JDK and selects it for compilation. The auto-selection hooks into getToolchainFromBuildContext() — when no explicit toolchain has been set via maven-toolchains-plugin, the manager checks the project's targetVersion (Model 4.1.0) or legacy properties (maven.compiler.release, maven.compiler.source) against the running JDK's supported source levels. If the running JDK cannot handle the required level, it picks the newest compatible JDK from configured toolchains, caches the selection, and emits a warning. Key changes: - New JdkSourceLevelSupport utility mapping JDK versions to supported --source levels based on the javac retirement schedule (JEP 182) - DefaultToolchainManager.getToolchainFromBuildContext() now falls back to auto-selection for "jdk" type when no explicit toolchain is stored - Reads source level from Model 4.1.0 <source><targetVersion> elements and legacy maven.compiler.release/source properties - Prefers the newest compatible JDK from configured toolchains Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two ITs verify the auto-selection behavior end-to-end through Maven's compat bridge: - testAutoSelectToolchainWhenSourceLevelUnsupported: project requires source 6 (unsupported by JDK 12+), verifies Maven auto-selects a compatible JDK 11 toolchain and logs a warning - testNoAutoSelectWhenSourceLevelSupported: project requires source 11 (supported by JDK 12+), verifies no auto-selection occurs Also adds debug logging to the auto-selection path for diagnostics. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
elharo
reviewed
Jul 30, 2026
elharo
left a comment
Contributor
There was a problem hiding this comment.
Good idea. does this need an issue to get picked up in the release notes?
Contributor
Author
No, an issue and a PR are similar in all points, but the fact that an issue does not have code. |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When no compatible JDK is found in toolchains.xml, Maven now lazily discovers JDKs from the filesystem by scanning well-known locations: JAVA*_HOME env vars, SDKMan, IntelliJ .jdks/, Gradle, jEnv, JBang, asdf, mise, and OS-specific paths (/usr/lib/jvm, etc.). Discovery only runs on the failure path (running JDK incompatible AND no configured toolchain matches), so normal builds pay zero cost. JDK version is read from the release file (no java process spawned). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace direct System.getProperty/System.getenv calls with the properties map from Session.getSystemProperties(), where env vars are available as "env.VAR_NAME" entries. This follows Maven's convention of accessing environment through the Session and makes the discoverer fully testable without relying on JVM global state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nd JDK discovery Three fixes for automatic JDK toolchain selection: 1. Read source level from compiler plugin configuration: many projects set <source> and <release> directly in the maven-compiler-plugin <configuration> block rather than as properties. Added getSourceLevelFromCompilerPlugin() to check both <release> and <source> elements. 2. Pass JdkToolchainDiscoverer to compat layer: ToolchainManagerFactory was creating DefaultToolchainManager without the discoverer, so old plugins going through the compat layer could detect incompatibility but had no discoverer to find alternative JDKs. 3. Scan JAVA_HOME parent directory for sibling JDKs: in CI and container environments, multiple JDKs are often installed as siblings (e.g. /toolchain/jdk-8, /toolchain/jdk-11, /toolchain/jdk-21). Added parent directory scanning from each JAVA*_HOME env var. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The gh-11055 DI service injection IT calls new DefaultToolchainManager(Map.of()) from outside the package, so the single-arg Map constructor must be public. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nd IT harness The gh-11055 DI service injection IT calls new DefaultToolchainManager(Map.of()) from outside the package, and the compat layer ToolchainManagerFactory calls the 3-arg constructor (factories, discoverer, logger). All constructors must be accessible from outside the package. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the JdkToolchainDiscoverer and automatic JDK toolchain selection. Instead, when the running JDK cannot honour the project's --source/--release level, emit a clear, actionable error message telling the user which JDK version they need and suggesting 'mvnup' to add the maven-toolchains-plugin with automatic discovery. This avoids duplicating the maven-toolchains-plugin's discovery mechanism in core and follows a simpler "detect and error" approach. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When mvnup detects a project's --source/--release level is no longer supported by the running JDK (per JEP 182), automatically add the maven-toolchains-plugin with the select-jdk-toolchain goal. This leverages the plugin's built-in JDK discovery mechanism instead of duplicating it in core. Also makes JdkSourceLevelSupport methods public so they can be reused across modules (maven-impl → maven-cli). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The select-jdk-toolchain goal needs a <version> constraint to know which JDK to select. Without it, it picks the latest LTS (e.g. 21) which defeats the purpose. Now generates e.g. <version>(,8]</version> based on latestJdkForSourceLevel() so the plugin matches a JDK that actually supports the project's source level. Verified end-to-end: mvnup on servicemix-utils (source 1.5) adds the plugin with (,8], and with JAVA8_HOME set the build succeeds with forked compilation via JDK 8. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests the full end-to-end flow: mvnup apply on a POM with maven.compiler.source=1.5 adds the maven-toolchains-plugin with select-jdk-toolchain goal and version constraint (,8], then verifies a second run is idempotent. Also adds mvnup to the chmod list in the IT suite's antrun config so it gets execute permission when extracted from the zip. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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
When the running JDK does not support the project's required
--source/--releaselevel (per JEP 182 retirement schedule), Maven currently lets the build fail with a crypticjavacerror. This PR adds two complementary features:ToolchainPluginStrategythat automatically adds themaven-toolchains-pluginwith theselect-jdk-toolchaingoal when the project's source level requires an older JDKjavacfail crypticallyExample error output (at build time, if mvnup was not run)
Design
mvnup:
ToolchainPluginStrategyA new
UpgradeStrategy(@Priority(15)) that runs by default (same activation asCompatibilityFixStrategy):maven.compiler.release/maven.compiler.sourceproperties, or compiler plugin<configuration>(<release>/<source>)JdkSourceLevelSupport)maven-toolchains-pluginwithselect-jdk-toolchaingoal is already configuredbuild > pluginssection if not presentThis leverages the plugin's existing JDK discovery mechanism (
ToolchainDiscoverer) rather than duplicating filesystem scanning in core.Core:
DefaultToolchainManager.checkJdkSourceLevelCompatibility()Called from
getToolchainFromBuildContext("jdk")when no explicit toolchain is configured:source > targetVersion, compiler properties, or compiler plugin configShared:
JdkSourceLevelSupport(public)Maps JDK versions to supported source levels per the JEP 182 retirement schedule. Provides:
supportsSourceLevel(jdkMajor, sourceLevel)latestJdkForSourceLevel(sourceLevel)normalizeSourceLevel(version)— handles1.8,1.8.0_392,21.0.1, etc.Test plan
JdkSourceLevelSupport— normalization, compatibility,latestJdkForSourceLevel()(19 tests)DefaultToolchainManager— error emitted/suppressed for various source levels and detection paths (16 tests)ToolchainPluginStrategy— applicability, source level detection from properties/plugin config, plugin detection, plugin addition, full apply scenarios (20 tests)maven-implmodule: 569 tests passmaven-compatmodule: 160 tests pass