[ https://issues.apache.org/jira/browse/MTOOLCHAINS-49?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17822251#comment-17822251 ]
ASF GitHub Bot commented on MTOOLCHAINS-49: ------------------------------------------- elharo commented on code in PR #14: URL: https://github.com/apache/maven-toolchains-plugin/pull/14#discussion_r1507960684 ########## src/site/apt/toolchains/discovery.apt.vm: ########## @@ -0,0 +1,148 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + + ------ + Discovery mechanism + ------ + Guillaume Nodet + ------ + 2024-02-28 + ------ + +JDK Toolchain discovery mechanism + + Since version 3.2.0, the plugin provides a heuristic to discover installed JDK toolchains, by looking + at known installation directories and at environment variables. + + The list of discovered toolchains can be easily displayed using the command + <<<mvn org.apache.maven.plugins:maven-toolchains-plugin:${project.version}:display-discovered-jdk-toolchains>>>. + This will print something like: + ++---+ +[INFO] Discovered 10 JDK toolchains: +[INFO] - /Users/gnodet/.sdkman/candidates/java/21.0.2-graalce +[INFO] provides: +[INFO] version: 21.0.2 +[INFO] runtime.name: OpenJDK Runtime Environment +[INFO] runtime.version: 21.0.2+13-jvmci-23.1-b30 +[INFO] vendor: GraalVM Community +[INFO] vendor.version: GraalVM CE 21.0.2+13.1 +[INFO] current: true +[INFO] lts: true +[INFO] env: JAVA_HOME,JAVA21_HOME +... ++---+ + + If you have installed JDK using standard tools and they are not listed here, feel free + to {{{../issue-management.html}raise an issue}}. + + The discovery mechanism provides a few information for each discovered JDK: Review Comment: delete "a few" ########## src/main/java/org/apache/maven/plugins/toolchain/jdk/SelectJdkToolchainMojo.java: ########## @@ -0,0 +1,265 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.plugins.toolchain.jdk; + +import javax.inject.Inject; +import javax.inject.Named; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.toolchain.MisconfiguredToolchainException; +import org.apache.maven.toolchain.RequirementMatcherFactory; +import org.apache.maven.toolchain.ToolchainFactory; +import org.apache.maven.toolchain.ToolchainManagerPrivate; +import org.apache.maven.toolchain.ToolchainPrivate; +import org.apache.maven.toolchain.model.PersistedToolchains; +import org.apache.maven.toolchain.model.ToolchainModel; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.ENV; +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.RUNTIME_NAME; +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.RUNTIME_VERSION; +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.VENDOR; +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.VERSION; + +/** + * Discover JDK toolchains and select a matching one. + */ +@Mojo(name = "select-jdk-toolchain", defaultPhase = LifecyclePhase.VALIDATE) +public class SelectJdkToolchainMojo extends AbstractMojo { + + public static final String TOOLCHAIN_TYPE_JDK = "jdk"; + + /** Jdk usage mode */ + public enum JdkMode { + /** always ignore the current JDK */ + Never, + /** to not use a toolchain if the toolchains that would be selected is the current JDK */ + IfSame, + /** favor the current JDK if it matches the requirements */ + IfMatch + } + + /** + * The version constraint for the JDK toolchain to select. + */ + @Parameter(property = "toolchain.jdk.version") + private String version; + + /** + * The runtime name constraint for the JDK toolchain to select. + */ + @Parameter(property = "toolchain.jdk.runtime.name") + private String runtimeName; + + /** + * The runtime version constraint for the JDK toolchain to select. + */ + @Parameter(property = "toolchain.jdk.runtime.version") + private String runtimeVersion; + + /** + * The vendor constraint for the JDK toolchain to select. + */ + @Parameter(property = "toolchain.jdk.vendor") + private String vendor; + + /** + * The env constraint for the JDK toolchain to select. + * To match the constraint, an environment variable with the given name must point to the JDK. + * For example, if you define {@code JAVA11_HOME=~/jdks/my-jdk-11.0.1}, you can specify + * {@code env=JAVA11_HOME} to match the given JDK. + */ + @Parameter(property = "toolchain.jdk.env") + private String env; + + /** + * The matching mode, either {@code IfMatch} (the default), {@code IfSame}, or {@code Never}. + * If {@code IfMatch} is used, a toolchain will not be selected if the running JDK does + * match the provided constraints. This is the default and provides better performances as it + * avoids forking a different process when it's not required. The {@code IfSame} avoids + * selecting a toolchain if the toolchain selected is exactly the same as the running JDK. + * THe {@code Never} option will always select the toolchain. + */ + @Parameter(property = "toolchain.jdk.mode", defaultValue = "IfMatch") + private JdkMode useJdk = JdkMode.IfMatch; + + /** + * Automatically discover JDK toolchains using the built-in heuristic. + * The default value is {@code true}. + */ + @Parameter(property = "toolchain.jdk.discover", defaultValue = "true") + private boolean discoverToolchains = true; + + /** + * Comparator used to sort JDK toolchains for selection. + * This property is a comma separated list of values which may contains: + * <ul> + * <li>{@code lts}: prefer JDK with LTS version</li> + * <li>{@code current}: prefer the current JDK</li> + * <li>{@code env}: prefer JDKs defined using {@code JAVA\{xx\}_HOME} environment variables</li> + * <li>{@code version}: prefer JDK with higher versions</li> + * <li>{@code vendor}: order JDK by vendor name (usually as a last comparator to ensure a stable order)</li> + * </ul> + */ + @Parameter(property = "toolchain.jdk.comparator", defaultValue = "lts,current,env,version,vendor") + private String comparator; + + /** + * Toolchain manager + */ + @Inject + private ToolchainManagerPrivate toolchainManager; + + /** + * Toolchain factory + */ + @Inject + @Named(TOOLCHAIN_TYPE_JDK) + ToolchainFactory factory; + + /** + * The current build session instance. This is used for toolchain manager API calls. + */ + @Inject + private MavenSession session; + + /** + * Toolchain discoverer + */ + @Inject + ToolchainDiscoverer discoverer; + + @Override + public void execute() throws MojoFailureException { + try { + doExecute(); + } catch (MisconfiguredToolchainException e) { + throw new MojoFailureException("Unable to select toolchain: " + e, e); + } + } + + private void doExecute() throws MisconfiguredToolchainException, MojoFailureException { + if (version == null && runtimeName == null && runtimeVersion == null && vendor == null && env == null) { + return; + } + + Map<String, String> requirements = new HashMap<>(); + Optional.ofNullable(version).ifPresent(v -> requirements.put(VERSION, v)); + Optional.ofNullable(runtimeName).ifPresent(v -> requirements.put(RUNTIME_NAME, v)); + Optional.ofNullable(runtimeVersion).ifPresent(v -> requirements.put(RUNTIME_VERSION, v)); + Optional.ofNullable(vendor).ifPresent(v -> requirements.put(VENDOR, v)); + Optional.ofNullable(env).ifPresent(v -> requirements.put(ENV, v)); + + ToolchainModel currentJdkToolchainModel = + discoverer.getCurrentJdkToolchain().orElse(null); + ToolchainPrivate currentJdkToolchain = + currentJdkToolchainModel != null ? factory.createToolchain(currentJdkToolchainModel) : null; + + if (useJdk == JdkMode.IfMatch && currentJdkToolchain != null && matches(currentJdkToolchain, requirements)) { + getLog().info("Not using an external toolchain as the current JDK matches the requirements."); + return; + } + + ToolchainPrivate toolchain = Stream.of(toolchainManager.getToolchainsForType(TOOLCHAIN_TYPE_JDK, session)) + .filter(tc -> matches(tc, requirements)) + .findFirst() + .orElse(null); + if (toolchain != null) { + getLog().info("Found matching JDK toolchain: " + toolchain); + } + + if (toolchain == null && discoverToolchains) { + getLog().debug("No matching toolchains configured, trying to discover JDK toolchains"); + PersistedToolchains persistedToolchains = discoverer.discoverToolchains(comparator); + getLog().info("Discovered " + persistedToolchains.getToolchains().size() + " JDK toolchains"); Review Comment: this should probably be debug as well. ########## src/site/apt/toolchains/discovery.apt.vm: ########## @@ -0,0 +1,148 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + + ------ + Discovery mechanism + ------ + Guillaume Nodet + ------ + 2024-02-28 + ------ + +JDK Toolchain discovery mechanism + + Since version 3.2.0, the plugin provides a heuristic to discover installed JDK toolchains, by looking + at known installation directories and at environment variables. + + The list of discovered toolchains can be easily displayed using the command + <<<mvn org.apache.maven.plugins:maven-toolchains-plugin:${project.version}:display-discovered-jdk-toolchains>>>. + This will print something like: + ++---+ +[INFO] Discovered 10 JDK toolchains: +[INFO] - /Users/gnodet/.sdkman/candidates/java/21.0.2-graalce +[INFO] provides: +[INFO] version: 21.0.2 +[INFO] runtime.name: OpenJDK Runtime Environment +[INFO] runtime.version: 21.0.2+13-jvmci-23.1-b30 +[INFO] vendor: GraalVM Community +[INFO] vendor.version: GraalVM CE 21.0.2+13.1 +[INFO] current: true +[INFO] lts: true +[INFO] env: JAVA_HOME,JAVA21_HOME +... ++---+ + + If you have installed JDK using standard tools and they are not listed here, feel free + to {{{../issue-management.html}raise an issue}}. + + The discovery mechanism provides a few information for each discovered JDK: + + * <<<version>>>: the JDK version + + * <<<runtime.name>>>: the name of the JDK runtime + + * <<<runtime.version>>>: the version of the JDK runtime + + * <<<vendor>>>: the vendor name + + * <<<vendor.version>>>: the vendor version + + * <<<current>>>: set to <<<true>>> if this is the running JDK + + * <<<lts>>>: set to <<<true>>> if JDK version is a long-term supported version + + * <<<env>>>: set to the comma separated list of <<<JAVA\{xyz\}_HOME>>>> matching environment variables + + + The <<<select-jdk-toolchain>>> goal discovering and selecting a matching JDK. + The config below allows using the current JDK, or any other discovered JDK >= 17. + The benefit is that the current JDK can be kept for speed, but ensuring the usage of any JDK 17 or higher if + the current jdk is below the requirements. + ++---+ +<properties> + <toolchain.jdk.version>[17,)</toolchain.jdk.version> +<properties> + +<plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-toolchains-plugin</artifactId> + <version>${project.version}</version> + <executions> + <execution> + <goals> + <goal>select-jdk-toolchain</goal> + </goals> + </execution> + </executions> +</plugin> ++---+ + + If you use environment variables to configure your JDKs, you can use the following configuration to select + the toolchain which is configured using the <<<JAVA17_HOME>>> environment variable. + ++---+ +<properties> + <toolchain.jdk.version>JAVA17_HOME</toolchain.jdk.version> +<properties> ++---+ + +* Selection mechanism + + Several properties can be used to express requirements to match against discovered JDK toolchains: Review Comment: delete "be used to" ########## src/site/apt/toolchains/discovery.apt.vm: ########## @@ -0,0 +1,148 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + + ------ + Discovery mechanism + ------ + Guillaume Nodet + ------ + 2024-02-28 + ------ + +JDK Toolchain discovery mechanism + + Since version 3.2.0, the plugin provides a heuristic to discover installed JDK toolchains, by looking + at known installation directories and at environment variables. + + The list of discovered toolchains can be easily displayed using the command + <<<mvn org.apache.maven.plugins:maven-toolchains-plugin:${project.version}:display-discovered-jdk-toolchains>>>. + This will print something like: + ++---+ +[INFO] Discovered 10 JDK toolchains: +[INFO] - /Users/gnodet/.sdkman/candidates/java/21.0.2-graalce +[INFO] provides: +[INFO] version: 21.0.2 +[INFO] runtime.name: OpenJDK Runtime Environment +[INFO] runtime.version: 21.0.2+13-jvmci-23.1-b30 +[INFO] vendor: GraalVM Community +[INFO] vendor.version: GraalVM CE 21.0.2+13.1 +[INFO] current: true +[INFO] lts: true +[INFO] env: JAVA_HOME,JAVA21_HOME +... ++---+ + + If you have installed JDK using standard tools and they are not listed here, feel free + to {{{../issue-management.html}raise an issue}}. + + The discovery mechanism provides a few information for each discovered JDK: + + * <<<version>>>: the JDK version + + * <<<runtime.name>>>: the name of the JDK runtime + + * <<<runtime.version>>>: the version of the JDK runtime + + * <<<vendor>>>: the vendor name + + * <<<vendor.version>>>: the vendor version + + * <<<current>>>: set to <<<true>>> if this is the running JDK + + * <<<lts>>>: set to <<<true>>> if JDK version is a long-term supported version + + * <<<env>>>: set to the comma separated list of <<<JAVA\{xyz\}_HOME>>>> matching environment variables + + + The <<<select-jdk-toolchain>>> goal discovering and selecting a matching JDK. + The config below allows using the current JDK, or any other discovered JDK >= 17. + The benefit is that the current JDK can be kept for speed, but ensuring the usage of any JDK 17 or higher if + the current jdk is below the requirements. + ++---+ +<properties> + <toolchain.jdk.version>[17,)</toolchain.jdk.version> +<properties> + +<plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-toolchains-plugin</artifactId> + <version>${project.version}</version> + <executions> + <execution> + <goals> + <goal>select-jdk-toolchain</goal> + </goals> + </execution> + </executions> +</plugin> ++---+ + + If you use environment variables to configure your JDKs, you can use the following configuration to select + the toolchain which is configured using the <<<JAVA17_HOME>>> environment variable. + ++---+ +<properties> + <toolchain.jdk.version>JAVA17_HOME</toolchain.jdk.version> +<properties> ++---+ + +* Selection mechanism + + Several properties can be used to express requirements to match against discovered JDK toolchains: + + * <<<version>>> / <<<toolchain.jdk.version>>>: a version range such as <<<[17,18)>>> to match against the JDK version + + * <<<runtimeName>>> / <<<toolchain.jdk.runtime.name>>> + + * <<<runtimeVersion>>> / <<<toolchain.jdk.runtime.version>>> + + * <<<vendor>>> / <<<toolchain.jdk.vendor>>> + + * <<<env>>> / <<<toolchain.jdk.env>>>: the name of an environment variable that the JDK toolchain must match + + The <<<useJdk>>> can be used to define whether the current JDK can be used if it matches the requirements. + +* Sorting + + Multiple discovered JDK toolchains may match the above requirements. In such a case, you may want to express + preferences to use to sort the toolchains. This can be done using the <<<comparator>>> configuration which is a + comma separated list of criterions amongst the following: + + * <<<lts>>>: prefer LTS toolchains + + * <<<current>>>: prefer the current JDK + + * <<<env>>>: prefer toolchains discovered from environment variables + + * <<<version>>>: prefer higher JDK versions + + * <<<vendor>>>: sort alphabetically by vendor name + + The default value is <<<lts,current,env,version,vendor>>>. + +* Toolchains XML file + + The generation of the <<<toolchains.xml>>> file is not necessary to use discovered toolchains. + The <<<select-jdk-toolchain>>> will select a toolchain amongst explicitly configured toolchains and discovered + toolchains. The information for discovered toolchains are cached in <<<~/.m2/discovered-toolchains-cache.xml>>> file + by default, to speed up things. + + If you prefer, you can use the <<<generate-jdk-toolchains-xml>>> to generate a toolchain XML. This can be used in + conjunction to the <<<discoverToolchains=false>>> configuration to disable the discovery and only use explicitly Review Comment: conjunction to --> conjucntion with the discovery --> discovery ########## src/site/apt/toolchains/discovery.apt.vm: ########## @@ -0,0 +1,148 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + + ------ + Discovery mechanism + ------ + Guillaume Nodet + ------ + 2024-02-28 + ------ + +JDK Toolchain discovery mechanism + + Since version 3.2.0, the plugin provides a heuristic to discover installed JDK toolchains, by looking + at known installation directories and at environment variables. + + The list of discovered toolchains can be easily displayed using the command + <<<mvn org.apache.maven.plugins:maven-toolchains-plugin:${project.version}:display-discovered-jdk-toolchains>>>. + This will print something like: + ++---+ +[INFO] Discovered 10 JDK toolchains: +[INFO] - /Users/gnodet/.sdkman/candidates/java/21.0.2-graalce +[INFO] provides: +[INFO] version: 21.0.2 +[INFO] runtime.name: OpenJDK Runtime Environment +[INFO] runtime.version: 21.0.2+13-jvmci-23.1-b30 +[INFO] vendor: GraalVM Community +[INFO] vendor.version: GraalVM CE 21.0.2+13.1 +[INFO] current: true +[INFO] lts: true +[INFO] env: JAVA_HOME,JAVA21_HOME +... ++---+ + + If you have installed JDK using standard tools and they are not listed here, feel free + to {{{../issue-management.html}raise an issue}}. + + The discovery mechanism provides a few information for each discovered JDK: + + * <<<version>>>: the JDK version + + * <<<runtime.name>>>: the name of the JDK runtime + + * <<<runtime.version>>>: the version of the JDK runtime + + * <<<vendor>>>: the vendor name + + * <<<vendor.version>>>: the vendor version + + * <<<current>>>: set to <<<true>>> if this is the running JDK + + * <<<lts>>>: set to <<<true>>> if JDK version is a long-term supported version + + * <<<env>>>: set to the comma separated list of <<<JAVA\{xyz\}_HOME>>>> matching environment variables + + + The <<<select-jdk-toolchain>>> goal discovering and selecting a matching JDK. + The config below allows using the current JDK, or any other discovered JDK >= 17. + The benefit is that the current JDK can be kept for speed, but ensuring the usage of any JDK 17 or higher if + the current jdk is below the requirements. + ++---+ +<properties> + <toolchain.jdk.version>[17,)</toolchain.jdk.version> +<properties> + +<plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-toolchains-plugin</artifactId> + <version>${project.version}</version> + <executions> + <execution> + <goals> + <goal>select-jdk-toolchain</goal> + </goals> + </execution> + </executions> +</plugin> ++---+ + + If you use environment variables to configure your JDKs, you can use the following configuration to select + the toolchain which is configured using the <<<JAVA17_HOME>>> environment variable. + ++---+ +<properties> + <toolchain.jdk.version>JAVA17_HOME</toolchain.jdk.version> +<properties> ++---+ + +* Selection mechanism + + Several properties can be used to express requirements to match against discovered JDK toolchains: + + * <<<version>>> / <<<toolchain.jdk.version>>>: a version range such as <<<[17,18)>>> to match against the JDK version + + * <<<runtimeName>>> / <<<toolchain.jdk.runtime.name>>> + + * <<<runtimeVersion>>> / <<<toolchain.jdk.runtime.version>>> + + * <<<vendor>>> / <<<toolchain.jdk.vendor>>> + + * <<<env>>> / <<<toolchain.jdk.env>>>: the name of an environment variable that the JDK toolchain must match + + The <<<useJdk>>> can be used to define whether the current JDK can be used if it matches the requirements. + +* Sorting + + Multiple discovered JDK toolchains may match the above requirements. In such a case, you may want to express + preferences to use to sort the toolchains. This can be done using the <<<comparator>>> configuration which is a + comma separated list of criterions amongst the following: Review Comment: criterions --> criteria ########## src/site/apt/toolchains/discovery.apt.vm: ########## @@ -0,0 +1,148 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + + ------ + Discovery mechanism + ------ + Guillaume Nodet + ------ + 2024-02-28 + ------ + +JDK Toolchain discovery mechanism + + Since version 3.2.0, the plugin provides a heuristic to discover installed JDK toolchains, by looking + at known installation directories and at environment variables. + + The list of discovered toolchains can be easily displayed using the command + <<<mvn org.apache.maven.plugins:maven-toolchains-plugin:${project.version}:display-discovered-jdk-toolchains>>>. + This will print something like: + ++---+ +[INFO] Discovered 10 JDK toolchains: +[INFO] - /Users/gnodet/.sdkman/candidates/java/21.0.2-graalce +[INFO] provides: +[INFO] version: 21.0.2 +[INFO] runtime.name: OpenJDK Runtime Environment +[INFO] runtime.version: 21.0.2+13-jvmci-23.1-b30 +[INFO] vendor: GraalVM Community +[INFO] vendor.version: GraalVM CE 21.0.2+13.1 +[INFO] current: true +[INFO] lts: true +[INFO] env: JAVA_HOME,JAVA21_HOME +... ++---+ + + If you have installed JDK using standard tools and they are not listed here, feel free + to {{{../issue-management.html}raise an issue}}. + + The discovery mechanism provides a few information for each discovered JDK: + + * <<<version>>>: the JDK version + + * <<<runtime.name>>>: the name of the JDK runtime + + * <<<runtime.version>>>: the version of the JDK runtime + + * <<<vendor>>>: the vendor name + + * <<<vendor.version>>>: the vendor version + + * <<<current>>>: set to <<<true>>> if this is the running JDK + + * <<<lts>>>: set to <<<true>>> if JDK version is a long-term supported version + + * <<<env>>>: set to the comma separated list of <<<JAVA\{xyz\}_HOME>>>> matching environment variables + + + The <<<select-jdk-toolchain>>> goal discovering and selecting a matching JDK. + The config below allows using the current JDK, or any other discovered JDK >= 17. + The benefit is that the current JDK can be kept for speed, but ensuring the usage of any JDK 17 or higher if Review Comment: The current JDK can be kept for speed, but JDK 17 or higher will be used if the current JDK is older than 17. ########## src/main/java/org/apache/maven/plugins/toolchain/jdk/SelectJdkToolchainMojo.java: ########## @@ -0,0 +1,265 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.plugins.toolchain.jdk; + +import javax.inject.Inject; +import javax.inject.Named; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.toolchain.MisconfiguredToolchainException; +import org.apache.maven.toolchain.RequirementMatcherFactory; +import org.apache.maven.toolchain.ToolchainFactory; +import org.apache.maven.toolchain.ToolchainManagerPrivate; +import org.apache.maven.toolchain.ToolchainPrivate; +import org.apache.maven.toolchain.model.PersistedToolchains; +import org.apache.maven.toolchain.model.ToolchainModel; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.ENV; +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.RUNTIME_NAME; +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.RUNTIME_VERSION; +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.VENDOR; +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.VERSION; + +/** + * Discover JDK toolchains and select a matching one. + */ +@Mojo(name = "select-jdk-toolchain", defaultPhase = LifecyclePhase.VALIDATE) +public class SelectJdkToolchainMojo extends AbstractMojo { + + public static final String TOOLCHAIN_TYPE_JDK = "jdk"; + + /** Jdk usage mode */ + public enum JdkMode { + /** always ignore the current JDK */ + Never, + /** to not use a toolchain if the toolchains that would be selected is the current JDK */ + IfSame, + /** favor the current JDK if it matches the requirements */ + IfMatch + } + + /** + * The version constraint for the JDK toolchain to select. + */ + @Parameter(property = "toolchain.jdk.version") + private String version; + + /** + * The runtime name constraint for the JDK toolchain to select. + */ + @Parameter(property = "toolchain.jdk.runtime.name") + private String runtimeName; + + /** + * The runtime version constraint for the JDK toolchain to select. + */ + @Parameter(property = "toolchain.jdk.runtime.version") + private String runtimeVersion; + + /** + * The vendor constraint for the JDK toolchain to select. + */ + @Parameter(property = "toolchain.jdk.vendor") + private String vendor; + + /** + * The env constraint for the JDK toolchain to select. + * To match the constraint, an environment variable with the given name must point to the JDK. + * For example, if you define {@code JAVA11_HOME=~/jdks/my-jdk-11.0.1}, you can specify + * {@code env=JAVA11_HOME} to match the given JDK. + */ + @Parameter(property = "toolchain.jdk.env") + private String env; + + /** + * The matching mode, either {@code IfMatch} (the default), {@code IfSame}, or {@code Never}. + * If {@code IfMatch} is used, a toolchain will not be selected if the running JDK does + * match the provided constraints. This is the default and provides better performances as it + * avoids forking a different process when it's not required. The {@code IfSame} avoids + * selecting a toolchain if the toolchain selected is exactly the same as the running JDK. + * THe {@code Never} option will always select the toolchain. + */ + @Parameter(property = "toolchain.jdk.mode", defaultValue = "IfMatch") + private JdkMode useJdk = JdkMode.IfMatch; + + /** + * Automatically discover JDK toolchains using the built-in heuristic. + * The default value is {@code true}. + */ + @Parameter(property = "toolchain.jdk.discover", defaultValue = "true") + private boolean discoverToolchains = true; + + /** + * Comparator used to sort JDK toolchains for selection. + * This property is a comma separated list of values which may contains: + * <ul> + * <li>{@code lts}: prefer JDK with LTS version</li> + * <li>{@code current}: prefer the current JDK</li> + * <li>{@code env}: prefer JDKs defined using {@code JAVA\{xx\}_HOME} environment variables</li> + * <li>{@code version}: prefer JDK with higher versions</li> + * <li>{@code vendor}: order JDK by vendor name (usually as a last comparator to ensure a stable order)</li> + * </ul> + */ + @Parameter(property = "toolchain.jdk.comparator", defaultValue = "lts,current,env,version,vendor") + private String comparator; + + /** + * Toolchain manager + */ + @Inject + private ToolchainManagerPrivate toolchainManager; + + /** + * Toolchain factory + */ + @Inject + @Named(TOOLCHAIN_TYPE_JDK) + ToolchainFactory factory; + + /** + * The current build session instance. This is used for toolchain manager API calls. + */ + @Inject + private MavenSession session; + + /** + * Toolchain discoverer + */ + @Inject + ToolchainDiscoverer discoverer; + + @Override + public void execute() throws MojoFailureException { + try { + doExecute(); + } catch (MisconfiguredToolchainException e) { + throw new MojoFailureException("Unable to select toolchain: " + e, e); + } + } + + private void doExecute() throws MisconfiguredToolchainException, MojoFailureException { + if (version == null && runtimeName == null && runtimeVersion == null && vendor == null && env == null) { + return; + } + + Map<String, String> requirements = new HashMap<>(); + Optional.ofNullable(version).ifPresent(v -> requirements.put(VERSION, v)); + Optional.ofNullable(runtimeName).ifPresent(v -> requirements.put(RUNTIME_NAME, v)); + Optional.ofNullable(runtimeVersion).ifPresent(v -> requirements.put(RUNTIME_VERSION, v)); + Optional.ofNullable(vendor).ifPresent(v -> requirements.put(VENDOR, v)); + Optional.ofNullable(env).ifPresent(v -> requirements.put(ENV, v)); + + ToolchainModel currentJdkToolchainModel = + discoverer.getCurrentJdkToolchain().orElse(null); + ToolchainPrivate currentJdkToolchain = + currentJdkToolchainModel != null ? factory.createToolchain(currentJdkToolchainModel) : null; + + if (useJdk == JdkMode.IfMatch && currentJdkToolchain != null && matches(currentJdkToolchain, requirements)) { + getLog().info("Not using an external toolchain as the current JDK matches the requirements."); + return; + } + + ToolchainPrivate toolchain = Stream.of(toolchainManager.getToolchainsForType(TOOLCHAIN_TYPE_JDK, session)) + .filter(tc -> matches(tc, requirements)) + .findFirst() + .orElse(null); + if (toolchain != null) { + getLog().info("Found matching JDK toolchain: " + toolchain); + } + + if (toolchain == null && discoverToolchains) { + getLog().debug("No matching toolchains configured, trying to discover JDK toolchains"); + PersistedToolchains persistedToolchains = discoverer.discoverToolchains(comparator); + getLog().info("Discovered " + persistedToolchains.getToolchains().size() + " JDK toolchains"); + + for (ToolchainModel tcm : persistedToolchains.getToolchains()) { + ToolchainPrivate tc = factory.createToolchain(tcm); + if (tc != null && matches(tc, requirements)) { + toolchain = tc; + getLog().debug("Discovered matching JDK toolchain: " + toolchain); + break; + } + } + } + + if (toolchain == null) { + throw new MojoFailureException( + "Cannot find matching toolchain definitions for the following toolchain types:" + requirements + + System.lineSeparator() + + "Please make sure you define the required toolchains in your ~/.m2/toolchains.xml file."); Review Comment: "Please make sure you define" --> "Define" https://learn.microsoft.com/en-us/previous-versions/windows/desktop/bb226825(v=vs.85) ########## src/main/java/org/apache/maven/plugins/toolchain/jdk/SelectJdkToolchainMojo.java: ########## @@ -0,0 +1,265 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.plugins.toolchain.jdk; + +import javax.inject.Inject; +import javax.inject.Named; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.toolchain.MisconfiguredToolchainException; +import org.apache.maven.toolchain.RequirementMatcherFactory; +import org.apache.maven.toolchain.ToolchainFactory; +import org.apache.maven.toolchain.ToolchainManagerPrivate; +import org.apache.maven.toolchain.ToolchainPrivate; +import org.apache.maven.toolchain.model.PersistedToolchains; +import org.apache.maven.toolchain.model.ToolchainModel; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.ENV; +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.RUNTIME_NAME; +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.RUNTIME_VERSION; +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.VENDOR; +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.VERSION; + +/** + * Discover JDK toolchains and select a matching one. + */ +@Mojo(name = "select-jdk-toolchain", defaultPhase = LifecyclePhase.VALIDATE) +public class SelectJdkToolchainMojo extends AbstractMojo { + + public static final String TOOLCHAIN_TYPE_JDK = "jdk"; + + /** Jdk usage mode */ + public enum JdkMode { + /** always ignore the current JDK */ + Never, + /** to not use a toolchain if the toolchains that would be selected is the current JDK */ + IfSame, + /** favor the current JDK if it matches the requirements */ + IfMatch + } + + /** + * The version constraint for the JDK toolchain to select. + */ + @Parameter(property = "toolchain.jdk.version") + private String version; + + /** + * The runtime name constraint for the JDK toolchain to select. + */ + @Parameter(property = "toolchain.jdk.runtime.name") + private String runtimeName; + + /** + * The runtime version constraint for the JDK toolchain to select. + */ + @Parameter(property = "toolchain.jdk.runtime.version") + private String runtimeVersion; + + /** + * The vendor constraint for the JDK toolchain to select. + */ + @Parameter(property = "toolchain.jdk.vendor") + private String vendor; + + /** + * The env constraint for the JDK toolchain to select. + * To match the constraint, an environment variable with the given name must point to the JDK. + * For example, if you define {@code JAVA11_HOME=~/jdks/my-jdk-11.0.1}, you can specify + * {@code env=JAVA11_HOME} to match the given JDK. + */ + @Parameter(property = "toolchain.jdk.env") + private String env; + + /** + * The matching mode, either {@code IfMatch} (the default), {@code IfSame}, or {@code Never}. + * If {@code IfMatch} is used, a toolchain will not be selected if the running JDK does + * match the provided constraints. This is the default and provides better performances as it + * avoids forking a different process when it's not required. The {@code IfSame} avoids + * selecting a toolchain if the toolchain selected is exactly the same as the running JDK. + * THe {@code Never} option will always select the toolchain. + */ + @Parameter(property = "toolchain.jdk.mode", defaultValue = "IfMatch") + private JdkMode useJdk = JdkMode.IfMatch; + + /** + * Automatically discover JDK toolchains using the built-in heuristic. + * The default value is {@code true}. + */ + @Parameter(property = "toolchain.jdk.discover", defaultValue = "true") + private boolean discoverToolchains = true; + + /** + * Comparator used to sort JDK toolchains for selection. + * This property is a comma separated list of values which may contains: + * <ul> + * <li>{@code lts}: prefer JDK with LTS version</li> + * <li>{@code current}: prefer the current JDK</li> + * <li>{@code env}: prefer JDKs defined using {@code JAVA\{xx\}_HOME} environment variables</li> + * <li>{@code version}: prefer JDK with higher versions</li> + * <li>{@code vendor}: order JDK by vendor name (usually as a last comparator to ensure a stable order)</li> + * </ul> + */ + @Parameter(property = "toolchain.jdk.comparator", defaultValue = "lts,current,env,version,vendor") + private String comparator; + + /** + * Toolchain manager + */ + @Inject + private ToolchainManagerPrivate toolchainManager; + + /** + * Toolchain factory + */ + @Inject + @Named(TOOLCHAIN_TYPE_JDK) + ToolchainFactory factory; + + /** + * The current build session instance. This is used for toolchain manager API calls. + */ + @Inject + private MavenSession session; + + /** + * Toolchain discoverer + */ + @Inject + ToolchainDiscoverer discoverer; + + @Override + public void execute() throws MojoFailureException { + try { + doExecute(); + } catch (MisconfiguredToolchainException e) { + throw new MojoFailureException("Unable to select toolchain: " + e, e); + } + } + + private void doExecute() throws MisconfiguredToolchainException, MojoFailureException { + if (version == null && runtimeName == null && runtimeVersion == null && vendor == null && env == null) { + return; + } + + Map<String, String> requirements = new HashMap<>(); + Optional.ofNullable(version).ifPresent(v -> requirements.put(VERSION, v)); + Optional.ofNullable(runtimeName).ifPresent(v -> requirements.put(RUNTIME_NAME, v)); + Optional.ofNullable(runtimeVersion).ifPresent(v -> requirements.put(RUNTIME_VERSION, v)); + Optional.ofNullable(vendor).ifPresent(v -> requirements.put(VENDOR, v)); + Optional.ofNullable(env).ifPresent(v -> requirements.put(ENV, v)); + + ToolchainModel currentJdkToolchainModel = + discoverer.getCurrentJdkToolchain().orElse(null); + ToolchainPrivate currentJdkToolchain = + currentJdkToolchainModel != null ? factory.createToolchain(currentJdkToolchainModel) : null; + + if (useJdk == JdkMode.IfMatch && currentJdkToolchain != null && matches(currentJdkToolchain, requirements)) { + getLog().info("Not using an external toolchain as the current JDK matches the requirements."); + return; + } + + ToolchainPrivate toolchain = Stream.of(toolchainManager.getToolchainsForType(TOOLCHAIN_TYPE_JDK, session)) + .filter(tc -> matches(tc, requirements)) + .findFirst() + .orElse(null); + if (toolchain != null) { + getLog().info("Found matching JDK toolchain: " + toolchain); + } + + if (toolchain == null && discoverToolchains) { + getLog().debug("No matching toolchains configured, trying to discover JDK toolchains"); + PersistedToolchains persistedToolchains = discoverer.discoverToolchains(comparator); + getLog().info("Discovered " + persistedToolchains.getToolchains().size() + " JDK toolchains"); + + for (ToolchainModel tcm : persistedToolchains.getToolchains()) { + ToolchainPrivate tc = factory.createToolchain(tcm); + if (tc != null && matches(tc, requirements)) { + toolchain = tc; + getLog().debug("Discovered matching JDK toolchain: " + toolchain); + break; + } + } + } + + if (toolchain == null) { + throw new MojoFailureException( + "Cannot find matching toolchain definitions for the following toolchain types:" + requirements + + System.lineSeparator() + + "Please make sure you define the required toolchains in your ~/.m2/toolchains.xml file."); + } + + if (useJdk == JdkMode.IfSame + && currentJdkToolchain != null + && Objects.equals(getJdkHome(currentJdkToolchain), getJdkHome(toolchain))) { + getLog().info("Not using an external toolchain as the current JDK has been selected."); + return; + } + + toolchainManager.storeToolchainToBuildContext(toolchain, session); + getLog().info("Found matching JDK toolchain: " + toolchain); Review Comment: debug ########## src/site/apt/toolchains/discovery.apt.vm: ########## @@ -0,0 +1,148 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + + ------ + Discovery mechanism + ------ + Guillaume Nodet + ------ + 2024-02-28 + ------ + +JDK Toolchain discovery mechanism + + Since version 3.2.0, the plugin provides a heuristic to discover installed JDK toolchains, by looking + at known installation directories and at environment variables. + + The list of discovered toolchains can be easily displayed using the command + <<<mvn org.apache.maven.plugins:maven-toolchains-plugin:${project.version}:display-discovered-jdk-toolchains>>>. + This will print something like: + ++---+ +[INFO] Discovered 10 JDK toolchains: +[INFO] - /Users/gnodet/.sdkman/candidates/java/21.0.2-graalce +[INFO] provides: +[INFO] version: 21.0.2 +[INFO] runtime.name: OpenJDK Runtime Environment +[INFO] runtime.version: 21.0.2+13-jvmci-23.1-b30 +[INFO] vendor: GraalVM Community +[INFO] vendor.version: GraalVM CE 21.0.2+13.1 +[INFO] current: true +[INFO] lts: true +[INFO] env: JAVA_HOME,JAVA21_HOME +... ++---+ + + If you have installed JDK using standard tools and they are not listed here, feel free Review Comment: I'm not sure what you mean by "they are not listed here". Does this mean the toolchains plugin can't locate the JDK? ########## src/site/apt/toolchains/discovery.apt.vm: ########## @@ -0,0 +1,148 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + + ------ + Discovery mechanism + ------ + Guillaume Nodet + ------ + 2024-02-28 + ------ + +JDK Toolchain discovery mechanism + + Since version 3.2.0, the plugin provides a heuristic to discover installed JDK toolchains, by looking + at known installation directories and at environment variables. + + The list of discovered toolchains can be easily displayed using the command + <<<mvn org.apache.maven.plugins:maven-toolchains-plugin:${project.version}:display-discovered-jdk-toolchains>>>. + This will print something like: + ++---+ +[INFO] Discovered 10 JDK toolchains: +[INFO] - /Users/gnodet/.sdkman/candidates/java/21.0.2-graalce +[INFO] provides: +[INFO] version: 21.0.2 +[INFO] runtime.name: OpenJDK Runtime Environment +[INFO] runtime.version: 21.0.2+13-jvmci-23.1-b30 +[INFO] vendor: GraalVM Community +[INFO] vendor.version: GraalVM CE 21.0.2+13.1 +[INFO] current: true +[INFO] lts: true +[INFO] env: JAVA_HOME,JAVA21_HOME +... ++---+ + + If you have installed JDK using standard tools and they are not listed here, feel free + to {{{../issue-management.html}raise an issue}}. + + The discovery mechanism provides a few information for each discovered JDK: + + * <<<version>>>: the JDK version + + * <<<runtime.name>>>: the name of the JDK runtime + + * <<<runtime.version>>>: the version of the JDK runtime + + * <<<vendor>>>: the vendor name + + * <<<vendor.version>>>: the vendor version + + * <<<current>>>: set to <<<true>>> if this is the running JDK + + * <<<lts>>>: set to <<<true>>> if JDK version is a long-term supported version + + * <<<env>>>: set to the comma separated list of <<<JAVA\{xyz\}_HOME>>>> matching environment variables + + + The <<<select-jdk-toolchain>>> goal discovering and selecting a matching JDK. Review Comment: discovering and selecting --> finds ########## src/main/java/org/apache/maven/plugins/toolchain/jdk/SelectJdkToolchainMojo.java: ########## @@ -0,0 +1,265 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.plugins.toolchain.jdk; + +import javax.inject.Inject; +import javax.inject.Named; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.toolchain.MisconfiguredToolchainException; +import org.apache.maven.toolchain.RequirementMatcherFactory; +import org.apache.maven.toolchain.ToolchainFactory; +import org.apache.maven.toolchain.ToolchainManagerPrivate; +import org.apache.maven.toolchain.ToolchainPrivate; +import org.apache.maven.toolchain.model.PersistedToolchains; +import org.apache.maven.toolchain.model.ToolchainModel; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.ENV; +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.RUNTIME_NAME; +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.RUNTIME_VERSION; +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.VENDOR; +import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.VERSION; + +/** + * Discover JDK toolchains and select a matching one. + */ +@Mojo(name = "select-jdk-toolchain", defaultPhase = LifecyclePhase.VALIDATE) +public class SelectJdkToolchainMojo extends AbstractMojo { + + public static final String TOOLCHAIN_TYPE_JDK = "jdk"; + + /** Jdk usage mode */ + public enum JdkMode { + /** always ignore the current JDK */ + Never, + /** to not use a toolchain if the toolchains that would be selected is the current JDK */ + IfSame, + /** favor the current JDK if it matches the requirements */ + IfMatch + } + + /** + * The version constraint for the JDK toolchain to select. + */ + @Parameter(property = "toolchain.jdk.version") + private String version; + + /** + * The runtime name constraint for the JDK toolchain to select. + */ + @Parameter(property = "toolchain.jdk.runtime.name") + private String runtimeName; + + /** + * The runtime version constraint for the JDK toolchain to select. + */ + @Parameter(property = "toolchain.jdk.runtime.version") + private String runtimeVersion; + + /** + * The vendor constraint for the JDK toolchain to select. + */ + @Parameter(property = "toolchain.jdk.vendor") + private String vendor; + + /** + * The env constraint for the JDK toolchain to select. + * To match the constraint, an environment variable with the given name must point to the JDK. + * For example, if you define {@code JAVA11_HOME=~/jdks/my-jdk-11.0.1}, you can specify + * {@code env=JAVA11_HOME} to match the given JDK. + */ + @Parameter(property = "toolchain.jdk.env") + private String env; + + /** + * The matching mode, either {@code IfMatch} (the default), {@code IfSame}, or {@code Never}. + * If {@code IfMatch} is used, a toolchain will not be selected if the running JDK does + * match the provided constraints. This is the default and provides better performances as it + * avoids forking a different process when it's not required. The {@code IfSame} avoids + * selecting a toolchain if the toolchain selected is exactly the same as the running JDK. + * THe {@code Never} option will always select the toolchain. + */ + @Parameter(property = "toolchain.jdk.mode", defaultValue = "IfMatch") + private JdkMode useJdk = JdkMode.IfMatch; + + /** + * Automatically discover JDK toolchains using the built-in heuristic. + * The default value is {@code true}. + */ + @Parameter(property = "toolchain.jdk.discover", defaultValue = "true") + private boolean discoverToolchains = true; + + /** + * Comparator used to sort JDK toolchains for selection. + * This property is a comma separated list of values which may contains: + * <ul> + * <li>{@code lts}: prefer JDK with LTS version</li> + * <li>{@code current}: prefer the current JDK</li> + * <li>{@code env}: prefer JDKs defined using {@code JAVA\{xx\}_HOME} environment variables</li> + * <li>{@code version}: prefer JDK with higher versions</li> + * <li>{@code vendor}: order JDK by vendor name (usually as a last comparator to ensure a stable order)</li> + * </ul> + */ + @Parameter(property = "toolchain.jdk.comparator", defaultValue = "lts,current,env,version,vendor") + private String comparator; + + /** + * Toolchain manager + */ + @Inject + private ToolchainManagerPrivate toolchainManager; + + /** + * Toolchain factory + */ + @Inject + @Named(TOOLCHAIN_TYPE_JDK) + ToolchainFactory factory; + + /** + * The current build session instance. This is used for toolchain manager API calls. + */ + @Inject + private MavenSession session; + + /** + * Toolchain discoverer + */ + @Inject + ToolchainDiscoverer discoverer; + + @Override + public void execute() throws MojoFailureException { + try { + doExecute(); + } catch (MisconfiguredToolchainException e) { + throw new MojoFailureException("Unable to select toolchain: " + e, e); + } + } + + private void doExecute() throws MisconfiguredToolchainException, MojoFailureException { + if (version == null && runtimeName == null && runtimeVersion == null && vendor == null && env == null) { + return; + } + + Map<String, String> requirements = new HashMap<>(); + Optional.ofNullable(version).ifPresent(v -> requirements.put(VERSION, v)); + Optional.ofNullable(runtimeName).ifPresent(v -> requirements.put(RUNTIME_NAME, v)); + Optional.ofNullable(runtimeVersion).ifPresent(v -> requirements.put(RUNTIME_VERSION, v)); + Optional.ofNullable(vendor).ifPresent(v -> requirements.put(VENDOR, v)); + Optional.ofNullable(env).ifPresent(v -> requirements.put(ENV, v)); + + ToolchainModel currentJdkToolchainModel = + discoverer.getCurrentJdkToolchain().orElse(null); + ToolchainPrivate currentJdkToolchain = + currentJdkToolchainModel != null ? factory.createToolchain(currentJdkToolchainModel) : null; + + if (useJdk == JdkMode.IfMatch && currentJdkToolchain != null && matches(currentJdkToolchain, requirements)) { + getLog().info("Not using an external toolchain as the current JDK matches the requirements."); + return; + } + + ToolchainPrivate toolchain = Stream.of(toolchainManager.getToolchainsForType(TOOLCHAIN_TYPE_JDK, session)) + .filter(tc -> matches(tc, requirements)) + .findFirst() + .orElse(null); + if (toolchain != null) { + getLog().info("Found matching JDK toolchain: " + toolchain); + } + + if (toolchain == null && discoverToolchains) { + getLog().debug("No matching toolchains configured, trying to discover JDK toolchains"); + PersistedToolchains persistedToolchains = discoverer.discoverToolchains(comparator); + getLog().info("Discovered " + persistedToolchains.getToolchains().size() + " JDK toolchains"); + + for (ToolchainModel tcm : persistedToolchains.getToolchains()) { + ToolchainPrivate tc = factory.createToolchain(tcm); + if (tc != null && matches(tc, requirements)) { + toolchain = tc; + getLog().debug("Discovered matching JDK toolchain: " + toolchain); + break; + } + } + } + + if (toolchain == null) { + throw new MojoFailureException( + "Cannot find matching toolchain definitions for the following toolchain types:" + requirements + + System.lineSeparator() + + "Please make sure you define the required toolchains in your ~/.m2/toolchains.xml file."); + } + + if (useJdk == JdkMode.IfSame + && currentJdkToolchain != null + && Objects.equals(getJdkHome(currentJdkToolchain), getJdkHome(toolchain))) { + getLog().info("Not using an external toolchain as the current JDK has been selected."); Review Comment: debug ########## src/site/apt/toolchains/discovery.apt.vm: ########## @@ -0,0 +1,148 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + + ------ + Discovery mechanism + ------ + Guillaume Nodet + ------ + 2024-02-28 + ------ + +JDK Toolchain discovery mechanism + + Since version 3.2.0, the plugin provides a heuristic to discover installed JDK toolchains, by looking + at known installation directories and at environment variables. + + The list of discovered toolchains can be easily displayed using the command + <<<mvn org.apache.maven.plugins:maven-toolchains-plugin:${project.version}:display-discovered-jdk-toolchains>>>. + This will print something like: + ++---+ +[INFO] Discovered 10 JDK toolchains: +[INFO] - /Users/gnodet/.sdkman/candidates/java/21.0.2-graalce +[INFO] provides: +[INFO] version: 21.0.2 +[INFO] runtime.name: OpenJDK Runtime Environment +[INFO] runtime.version: 21.0.2+13-jvmci-23.1-b30 +[INFO] vendor: GraalVM Community +[INFO] vendor.version: GraalVM CE 21.0.2+13.1 +[INFO] current: true +[INFO] lts: true +[INFO] env: JAVA_HOME,JAVA21_HOME +... ++---+ + + If you have installed JDK using standard tools and they are not listed here, feel free + to {{{../issue-management.html}raise an issue}}. + + The discovery mechanism provides a few information for each discovered JDK: + + * <<<version>>>: the JDK version + + * <<<runtime.name>>>: the name of the JDK runtime + + * <<<runtime.version>>>: the version of the JDK runtime + + * <<<vendor>>>: the vendor name + + * <<<vendor.version>>>: the vendor version + + * <<<current>>>: set to <<<true>>> if this is the running JDK + + * <<<lts>>>: set to <<<true>>> if JDK version is a long-term supported version + + * <<<env>>>: set to the comma separated list of <<<JAVA\{xyz\}_HOME>>>> matching environment variables + + + The <<<select-jdk-toolchain>>> goal discovering and selecting a matching JDK. + The config below allows using the current JDK, or any other discovered JDK >= 17. + The benefit is that the current JDK can be kept for speed, but ensuring the usage of any JDK 17 or higher if + the current jdk is below the requirements. + ++---+ +<properties> + <toolchain.jdk.version>[17,)</toolchain.jdk.version> +<properties> + +<plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-toolchains-plugin</artifactId> + <version>${project.version}</version> + <executions> + <execution> + <goals> + <goal>select-jdk-toolchain</goal> + </goals> + </execution> + </executions> +</plugin> ++---+ + + If you use environment variables to configure your JDKs, you can use the following configuration to select + the toolchain which is configured using the <<<JAVA17_HOME>>> environment variable. + ++---+ +<properties> + <toolchain.jdk.version>JAVA17_HOME</toolchain.jdk.version> +<properties> ++---+ + +* Selection mechanism + + Several properties can be used to express requirements to match against discovered JDK toolchains: + + * <<<version>>> / <<<toolchain.jdk.version>>>: a version range such as <<<[17,18)>>> to match against the JDK version + + * <<<runtimeName>>> / <<<toolchain.jdk.runtime.name>>> + + * <<<runtimeVersion>>> / <<<toolchain.jdk.runtime.version>>> + + * <<<vendor>>> / <<<toolchain.jdk.vendor>>> + + * <<<env>>> / <<<toolchain.jdk.env>>>: the name of an environment variable that the JDK toolchain must match + + The <<<useJdk>>> can be used to define whether the current JDK can be used if it matches the requirements. + +* Sorting + + Multiple discovered JDK toolchains may match the above requirements. In such a case, you may want to express + preferences to use to sort the toolchains. This can be done using the <<<comparator>>> configuration which is a + comma separated list of criterions amongst the following: + + * <<<lts>>>: prefer LTS toolchains + + * <<<current>>>: prefer the current JDK + + * <<<env>>>: prefer toolchains discovered from environment variables + + * <<<version>>>: prefer higher JDK versions + + * <<<vendor>>>: sort alphabetically by vendor name + + The default value is <<<lts,current,env,version,vendor>>>. + +* Toolchains XML file + + The generation of the <<<toolchains.xml>>> file is not necessary to use discovered toolchains. + The <<<select-jdk-toolchain>>> will select a toolchain amongst explicitly configured toolchains and discovered + toolchains. The information for discovered toolchains are cached in <<<~/.m2/discovered-toolchains-cache.xml>>> file + by default, to speed up things. Review Comment: things --> builds ########## src/site/apt/toolchains/discovery.apt.vm: ########## @@ -0,0 +1,148 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + + ------ + Discovery mechanism + ------ + Guillaume Nodet + ------ + 2024-02-28 + ------ + +JDK Toolchain discovery mechanism + + Since version 3.2.0, the plugin provides a heuristic to discover installed JDK toolchains, by looking + at known installation directories and at environment variables. + + The list of discovered toolchains can be easily displayed using the command + <<<mvn org.apache.maven.plugins:maven-toolchains-plugin:${project.version}:display-discovered-jdk-toolchains>>>. + This will print something like: + ++---+ +[INFO] Discovered 10 JDK toolchains: +[INFO] - /Users/gnodet/.sdkman/candidates/java/21.0.2-graalce +[INFO] provides: +[INFO] version: 21.0.2 +[INFO] runtime.name: OpenJDK Runtime Environment +[INFO] runtime.version: 21.0.2+13-jvmci-23.1-b30 +[INFO] vendor: GraalVM Community +[INFO] vendor.version: GraalVM CE 21.0.2+13.1 +[INFO] current: true +[INFO] lts: true +[INFO] env: JAVA_HOME,JAVA21_HOME +... ++---+ + + If you have installed JDK using standard tools and they are not listed here, feel free + to {{{../issue-management.html}raise an issue}}. + + The discovery mechanism provides a few information for each discovered JDK: + + * <<<version>>>: the JDK version + + * <<<runtime.name>>>: the name of the JDK runtime + + * <<<runtime.version>>>: the version of the JDK runtime + + * <<<vendor>>>: the vendor name + + * <<<vendor.version>>>: the vendor version + + * <<<current>>>: set to <<<true>>> if this is the running JDK + + * <<<lts>>>: set to <<<true>>> if JDK version is a long-term supported version + + * <<<env>>>: set to the comma separated list of <<<JAVA\{xyz\}_HOME>>>> matching environment variables + + + The <<<select-jdk-toolchain>>> goal discovering and selecting a matching JDK. + The config below allows using the current JDK, or any other discovered JDK >= 17. + The benefit is that the current JDK can be kept for speed, but ensuring the usage of any JDK 17 or higher if + the current jdk is below the requirements. + ++---+ +<properties> + <toolchain.jdk.version>[17,)</toolchain.jdk.version> +<properties> + +<plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-toolchains-plugin</artifactId> + <version>${project.version}</version> + <executions> + <execution> + <goals> + <goal>select-jdk-toolchain</goal> + </goals> + </execution> + </executions> +</plugin> ++---+ + + If you use environment variables to configure your JDKs, you can use the following configuration to select + the toolchain which is configured using the <<<JAVA17_HOME>>> environment variable. + ++---+ +<properties> + <toolchain.jdk.version>JAVA17_HOME</toolchain.jdk.version> +<properties> ++---+ + +* Selection mechanism + + Several properties can be used to express requirements to match against discovered JDK toolchains: + + * <<<version>>> / <<<toolchain.jdk.version>>>: a version range such as <<<[17,18)>>> to match against the JDK version + + * <<<runtimeName>>> / <<<toolchain.jdk.runtime.name>>> + + * <<<runtimeVersion>>> / <<<toolchain.jdk.runtime.version>>> + + * <<<vendor>>> / <<<toolchain.jdk.vendor>>> + + * <<<env>>> / <<<toolchain.jdk.env>>>: the name of an environment variable that the JDK toolchain must match + + The <<<useJdk>>> can be used to define whether the current JDK can be used if it matches the requirements. + +* Sorting + + Multiple discovered JDK toolchains may match the above requirements. In such a case, you may want to express Review Comment: may match the above requirements --> match satisfy the requirements may want to --> can to use to sort -> for sorting ########## src/site/apt/toolchains/discovery.apt.vm: ########## @@ -0,0 +1,148 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + + ------ + Discovery mechanism + ------ + Guillaume Nodet + ------ + 2024-02-28 + ------ + +JDK Toolchain discovery mechanism + + Since version 3.2.0, the plugin provides a heuristic to discover installed JDK toolchains, by looking + at known installation directories and at environment variables. + + The list of discovered toolchains can be easily displayed using the command + <<<mvn org.apache.maven.plugins:maven-toolchains-plugin:${project.version}:display-discovered-jdk-toolchains>>>. + This will print something like: + ++---+ +[INFO] Discovered 10 JDK toolchains: +[INFO] - /Users/gnodet/.sdkman/candidates/java/21.0.2-graalce +[INFO] provides: +[INFO] version: 21.0.2 +[INFO] runtime.name: OpenJDK Runtime Environment +[INFO] runtime.version: 21.0.2+13-jvmci-23.1-b30 +[INFO] vendor: GraalVM Community +[INFO] vendor.version: GraalVM CE 21.0.2+13.1 +[INFO] current: true +[INFO] lts: true +[INFO] env: JAVA_HOME,JAVA21_HOME +... ++---+ + + If you have installed JDK using standard tools and they are not listed here, feel free + to {{{../issue-management.html}raise an issue}}. + + The discovery mechanism provides a few information for each discovered JDK: + + * <<<version>>>: the JDK version + + * <<<runtime.name>>>: the name of the JDK runtime + + * <<<runtime.version>>>: the version of the JDK runtime + + * <<<vendor>>>: the vendor name + + * <<<vendor.version>>>: the vendor version + + * <<<current>>>: set to <<<true>>> if this is the running JDK + + * <<<lts>>>: set to <<<true>>> if JDK version is a long-term supported version + + * <<<env>>>: set to the comma separated list of <<<JAVA\{xyz\}_HOME>>>> matching environment variables + + + The <<<select-jdk-toolchain>>> goal discovering and selecting a matching JDK. + The config below allows using the current JDK, or any other discovered JDK >= 17. + The benefit is that the current JDK can be kept for speed, but ensuring the usage of any JDK 17 or higher if + the current jdk is below the requirements. + ++---+ +<properties> + <toolchain.jdk.version>[17,)</toolchain.jdk.version> +<properties> + +<plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-toolchains-plugin</artifactId> + <version>${project.version}</version> + <executions> + <execution> + <goals> + <goal>select-jdk-toolchain</goal> + </goals> + </execution> + </executions> +</plugin> ++---+ + + If you use environment variables to configure your JDKs, you can use the following configuration to select + the toolchain which is configured using the <<<JAVA17_HOME>>> environment variable. + ++---+ +<properties> + <toolchain.jdk.version>JAVA17_HOME</toolchain.jdk.version> +<properties> ++---+ + +* Selection mechanism + + Several properties can be used to express requirements to match against discovered JDK toolchains: + + * <<<version>>> / <<<toolchain.jdk.version>>>: a version range such as <<<[17,18)>>> to match against the JDK version + + * <<<runtimeName>>> / <<<toolchain.jdk.runtime.name>>> + + * <<<runtimeVersion>>> / <<<toolchain.jdk.runtime.version>>> + + * <<<vendor>>> / <<<toolchain.jdk.vendor>>> + + * <<<env>>> / <<<toolchain.jdk.env>>>: the name of an environment variable that the JDK toolchain must match + + The <<<useJdk>>> can be used to define whether the current JDK can be used if it matches the requirements. + +* Sorting + + Multiple discovered JDK toolchains may match the above requirements. In such a case, you may want to express + preferences to use to sort the toolchains. This can be done using the <<<comparator>>> configuration which is a + comma separated list of criterions amongst the following: + + * <<<lts>>>: prefer LTS toolchains + + * <<<current>>>: prefer the current JDK + + * <<<env>>>: prefer toolchains discovered from environment variables + + * <<<version>>>: prefer higher JDK versions + + * <<<vendor>>>: sort alphabetically by vendor name + + The default value is <<<lts,current,env,version,vendor>>>. + +* Toolchains XML file + + The generation of the <<<toolchains.xml>>> file is not necessary to use discovered toolchains. + The <<<select-jdk-toolchain>>> will select a toolchain amongst explicitly configured toolchains and discovered + toolchains. The information for discovered toolchains are cached in <<<~/.m2/discovered-toolchains-cache.xml>>> file Review Comment: The information for discovered --> Discovered > Automatic discovery of JDK toolchains > ------------------------------------- > > Key: MTOOLCHAINS-49 > URL: https://issues.apache.org/jira/browse/MTOOLCHAINS-49 > Project: Maven Toolchains Plugin > Issue Type: Improvement > Reporter: Guillaume Nodet > Assignee: Guillaume Nodet > Priority: Major > -- This message was sent by Atlassian Jira (v8.20.10#820010)