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 -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
