From f860a8693d38292c8ce9ee3b6a883559e7a94796 Mon Sep 17 00:00:00 2001 From: Konrad Windszus Date: Thu, 7 Mar 2024 16:14:20 +0100 Subject: [PATCH] [MNG-5726] Support regular expression matching in profile activation for (#1431) OS version This requires using the reserved prefix "regex:" in the version element. --- maven-model-builder/pom.xml | 4 - .../OperatingSystemProfileActivator.java | 65 +++-- .../main/java/org/apache/maven/utils/Os.java | 233 ++++++++++++++++++ .../OperatingSystemProfileActivatorTest.java | 144 +++++++++++ 4 files changed, 415 insertions(+), 31 deletions(-) create mode 100644 maven-model-builder/src/main/java/org/apache/maven/utils/Os.java create mode 100644 maven-model-builder/src/test/java/org/apache/maven/model/profile/activation/OperatingSystemProfileActivatorTest.java diff --git a/maven-model-builder/pom.xml b/maven-model-builder/pom.xml index 8357f7ec38..97347635c9 100644 --- a/maven-model-builder/pom.xml +++ b/maven-model-builder/pom.xml @@ -32,10 +32,6 @@ under the License. The effective model builder, with inheritance, profile activation, interpolation, ... - - org.codehaus.plexus - plexus-utils - org.codehaus.plexus plexus-interpolation diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/activation/OperatingSystemProfileActivator.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/activation/OperatingSystemProfileActivator.java index f7445199ff..a9c2c76a88 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/activation/OperatingSystemProfileActivator.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/activation/OperatingSystemProfileActivator.java @@ -21,12 +21,14 @@ package org.apache.maven.model.profile.activation; import javax.inject.Named; import javax.inject.Singleton; +import java.util.Locale; + import org.apache.maven.model.Activation; import org.apache.maven.model.ActivationOS; import org.apache.maven.model.Profile; import org.apache.maven.model.building.ModelProblemCollector; import org.apache.maven.model.profile.ProfileActivationContext; -import org.codehaus.plexus.util.Os; +import org.apache.maven.utils.Os; /** * Determines profile activation based on the operating system of the current runtime platform. @@ -38,6 +40,8 @@ import org.codehaus.plexus.util.Os; @Singleton public class OperatingSystemProfileActivator implements ProfileActivator { + private static final String REGEX_PREFIX = "regex:"; + @Override public boolean isActive(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) { Activation activation = profile.getActivation(); @@ -54,17 +58,21 @@ public class OperatingSystemProfileActivator implements ProfileActivator { boolean active = ensureAtLeastOneNonNull(os); + String actualOsName = context.getSystemProperties().get("os.name").toLowerCase(Locale.ENGLISH); + String actualOsArch = context.getSystemProperties().get("os.arch").toLowerCase(Locale.ENGLISH); + String actualOsVersion = context.getSystemProperties().get("os.version").toLowerCase(Locale.ENGLISH); + if (active && os.getFamily() != null) { - active = determineFamilyMatch(os.getFamily()); + active = determineFamilyMatch(os.getFamily(), actualOsName); } if (active && os.getName() != null) { - active = determineNameMatch(os.getName()); + active = determineNameMatch(os.getName(), actualOsName); } if (active && os.getArch() != null) { - active = determineArchMatch(os.getArch()); + active = determineArchMatch(os.getArch(), actualOsArch); } if (active && os.getVersion() != null) { - active = determineVersionMatch(os.getVersion()); + active = determineVersionMatch(os.getVersion(), actualOsVersion); } return active; @@ -90,8 +98,25 @@ public class OperatingSystemProfileActivator implements ProfileActivator { return os.getArch() != null || os.getFamily() != null || os.getName() != null || os.getVersion() != null; } - private boolean determineVersionMatch(String version) { - String test = version; + private boolean determineVersionMatch(String expectedVersion, String actualVersion) { + String test = expectedVersion; + boolean reverse = false; + final boolean result; + if (test.startsWith(REGEX_PREFIX)) { + result = actualVersion.matches(test.substring(REGEX_PREFIX.length())); + } else { + if (test.startsWith("!")) { + reverse = true; + test = test.substring(1); + } + result = actualVersion.equals(test); + } + + return reverse != result; + } + + private boolean determineArchMatch(String expectedArch, String actualArch) { + String test = expectedArch; boolean reverse = false; if (test.startsWith("!")) { @@ -99,13 +124,13 @@ public class OperatingSystemProfileActivator implements ProfileActivator { test = test.substring(1); } - boolean result = Os.isVersion(test); + boolean result = actualArch.equals(test); return reverse ? !result : result; } - private boolean determineArchMatch(String arch) { - String test = arch; + private boolean determineNameMatch(String expectedName, String actualName) { + String test = expectedName; boolean reverse = false; if (test.startsWith("!")) { @@ -113,26 +138,12 @@ public class OperatingSystemProfileActivator implements ProfileActivator { test = test.substring(1); } - boolean result = Os.isArch(test); + boolean result = actualName.equals(test); return reverse ? !result : result; } - private boolean determineNameMatch(String name) { - String test = name; - boolean reverse = false; - - if (test.startsWith("!")) { - reverse = true; - test = test.substring(1); - } - - boolean result = Os.isName(test); - - return reverse ? !result : result; - } - - private boolean determineFamilyMatch(String family) { + private boolean determineFamilyMatch(String family, String actualName) { String test = family; boolean reverse = false; @@ -141,7 +152,7 @@ public class OperatingSystemProfileActivator implements ProfileActivator { test = test.substring(1); } - boolean result = Os.isFamily(test); + boolean result = Os.isFamily(test, actualName); return reverse ? !result : result; } diff --git a/maven-model-builder/src/main/java/org/apache/maven/utils/Os.java b/maven-model-builder/src/main/java/org/apache/maven/utils/Os.java new file mode 100644 index 0000000000..3bc84fcec1 --- /dev/null +++ b/maven-model-builder/src/main/java/org/apache/maven/utils/Os.java @@ -0,0 +1,233 @@ +/* + * 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.utils; + +import java.util.Locale; +import java.util.stream.Stream; + +/** + * OS support + */ +public class Os { + + /** + * The OS Name. + */ + public static final String OS_NAME = System.getProperty("os.name").toLowerCase(Locale.ENGLISH); + + /** + * The OA architecture. + */ + public static final String OS_ARCH = System.getProperty("os.arch").toLowerCase(Locale.ENGLISH); + + /** + * The OS version. + */ + public static final String OS_VERSION = System.getProperty("os.version").toLowerCase(Locale.ENGLISH); + + /** + * OS Family + */ + public static final String OS_FAMILY; + + /** + * Boolean indicating if the running OS is a Windows system. + */ + public static final boolean IS_WINDOWS; + + /** + * OS family that can be tested for. {@value} + */ + private static final String FAMILY_WINDOWS = "windows"; + + /** + * OS family that can be tested for. {@value} + */ + private static final String FAMILY_WIN9X = "win9x"; + + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NT = "winnt"; + + /** + * OS family that can be tested for. {@value} + */ + private static final String FAMILY_OS2 = "os/2"; + + /** + * OS family that can be tested for. {@value} + */ + private static final String FAMILY_NETWARE = "netware"; + + /** + * OS family that can be tested for. {@value} + */ + private static final String FAMILY_DOS = "dos"; + + /** + * OS family that can be tested for. {@value} + */ + private static final String FAMILY_MAC = "mac"; + + /** + * OS family that can be tested for. {@value} + */ + private static final String FAMILY_TANDEM = "tandem"; + + /** + * OS family that can be tested for. {@value} + */ + private static final String FAMILY_UNIX = "unix"; + + /** + * OS family that can be tested for. {@value} + */ + private static final String FAMILY_OPENVMS = "openvms"; + + /** + * OS family that can be tested for. {@value} + */ + private static final String FAMILY_ZOS = "z/os"; + + /** + * OS family that can be tested for. {@value} + */ + private static final String FAMILY_OS390 = "os/390"; + + /** + * OS family that can be tested for. {@value} + */ + private static final String FAMILY_OS400 = "os/400"; + + /** + * OpenJDK is reported to call MacOS X "Darwin" + * + * @see bugzilla issue + * @see HADOOP-3318 + */ + private static final String DARWIN = "darwin"; + + /** + * The path separator. + */ + private static final String PATH_SEP = System.getProperty("path.separator"); + + static { + // Those two public constants are initialized here, as they need all the private constants + // above to be initialized first, but the code style imposes the public constants to be + // defined above the private ones... + OS_FAMILY = getOsFamily(); + IS_WINDOWS = isFamily(FAMILY_WINDOWS); + } + + private Os() {} + + /** + * Determines if the OS on which Maven is executing matches the + * given OS family. + * + * @param family the family to check for + * @return true if the OS matches + * + */ + public static boolean isFamily(String family) { + return isFamily(family, OS_NAME); + } + + /** + * Determines if the OS on which Maven is executing matches the + * given OS family derived from the given OS name + * + * @param family the family to check for + * @param actualOsName the OS name to check against + * @return true if the OS matches + * + */ + public static boolean isFamily(String family, String actualOsName) { + // windows probing logic relies on the word 'windows' in the OS + boolean isWindows = actualOsName.contains(FAMILY_WINDOWS); + boolean is9x = false; + boolean isNT = false; + if (isWindows) { + // there are only four 9x platforms that we look for + is9x = (actualOsName.contains("95") + || actualOsName.contains("98") + || actualOsName.contains("me") + // wince isn't really 9x, but crippled enough to + // be a muchness. Maven doesnt run on CE, anyway. + || actualOsName.contains("ce")); + isNT = !is9x; + } + switch (family) { + case FAMILY_WINDOWS: + return isWindows; + case FAMILY_WIN9X: + return isWindows && is9x; + case FAMILY_NT: + return isWindows && isNT; + case FAMILY_OS2: + return actualOsName.contains(FAMILY_OS2); + case FAMILY_NETWARE: + return actualOsName.contains(FAMILY_NETWARE); + case FAMILY_DOS: + return PATH_SEP.equals(";") && !isFamily(FAMILY_NETWARE, actualOsName) && !isWindows; + case FAMILY_MAC: + return actualOsName.contains(FAMILY_MAC) || actualOsName.contains(DARWIN); + case FAMILY_TANDEM: + return actualOsName.contains("nonstop_kernel"); + case FAMILY_UNIX: + return PATH_SEP.equals(":") + && !isFamily(FAMILY_OPENVMS, actualOsName) + && (!isFamily(FAMILY_MAC, actualOsName) || actualOsName.endsWith("x")); + case FAMILY_ZOS: + return actualOsName.contains(FAMILY_ZOS) || actualOsName.contains(FAMILY_OS390); + case FAMILY_OS400: + return actualOsName.contains(FAMILY_OS400); + case FAMILY_OPENVMS: + return actualOsName.contains(FAMILY_OPENVMS); + default: + return actualOsName.contains(family.toLowerCase(Locale.US)); + } + } + + /** + * Helper method to determine the current OS family. + * + * @return name of current OS family. + */ + private static String getOsFamily() { + return Stream.of( + FAMILY_DOS, + FAMILY_MAC, + FAMILY_NETWARE, + FAMILY_NT, + FAMILY_OPENVMS, + FAMILY_OS2, + FAMILY_OS400, + FAMILY_TANDEM, + FAMILY_UNIX, + FAMILY_WIN9X, + FAMILY_WINDOWS, + FAMILY_ZOS) + .filter(Os::isFamily) + .findFirst() + .orElse(null); + } +} diff --git a/maven-model-builder/src/test/java/org/apache/maven/model/profile/activation/OperatingSystemProfileActivatorTest.java b/maven-model-builder/src/test/java/org/apache/maven/model/profile/activation/OperatingSystemProfileActivatorTest.java new file mode 100644 index 0000000000..7944e25503 --- /dev/null +++ b/maven-model-builder/src/test/java/org/apache/maven/model/profile/activation/OperatingSystemProfileActivatorTest.java @@ -0,0 +1,144 @@ +/* + * 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.model.profile.activation; + +import java.util.Properties; + +import org.apache.maven.model.Activation; +import org.apache.maven.model.ActivationOS; +import org.apache.maven.model.Profile; + +/** + * Tests {@link OperatingSystemProfileActivator}. + * + */ +public class OperatingSystemProfileActivatorTest extends AbstractProfileActivatorTest { + + public OperatingSystemProfileActivatorTest() throws Exception { + super(OperatingSystemProfileActivator.class); + } + + private Profile newProfile(ActivationOS os) { + org.apache.maven.model.Activation a = new Activation(); + a.setOs(os); + + Profile p = new Profile(); + p.setActivation(a); + + return p; + } + + private Properties newProperties(String osName, String osVersion, String osArch) { + Properties props = new Properties(); + props.setProperty("os.name", osName); + props.setProperty("os.version", osVersion); + props.setProperty("os.arch", osArch); + return props; + } + + public void testVersionStringComparison() throws Exception { + ActivationOS os = new ActivationOS(); + os.setVersion("6.5.0-1014-aws"); + Profile profile = newProfile(os); + + assertActivation(true, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64"))); + assertActivation(true, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64"))); + + assertActivation(false, profile, newContext(null, newProperties("linux", "3.1.0", "amd64"))); + } + + public void testVersionRegexMatching() throws Exception { + ActivationOS os = new ActivationOS(); + os.setVersion("regex:.*aws"); + Profile profile = newProfile(os); + + assertActivation(true, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64"))); + assertActivation(true, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64"))); + + assertActivation(false, profile, newContext(null, newProperties("linux", "3.1.0", "amd64"))); + } + + public void testName() { + ActivationOS os = new ActivationOS(); + os.setName("windows"); + Profile profile = newProfile(os); + + assertActivation(false, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64"))); + assertActivation(true, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64"))); + } + + public void testNegatedName() { + ActivationOS os = new ActivationOS(); + os.setName("!windows"); + Profile profile = newProfile(os); + + assertActivation(true, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64"))); + assertActivation(false, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64"))); + } + + public void testArch() { + ActivationOS os = new ActivationOS(); + os.setArch("amd64"); + Profile profile = newProfile(os); + + assertActivation(true, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64"))); + assertActivation(false, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64"))); + } + + public void testNegatedArch() { + ActivationOS os = new ActivationOS(); + os.setArch("!amd64"); + Profile profile = newProfile(os); + + assertActivation(false, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64"))); + assertActivation(true, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64"))); + } + + public void testFamily() { + ActivationOS os = new ActivationOS(); + os.setFamily("windows"); + Profile profile = newProfile(os); + + assertActivation(false, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64"))); + assertActivation(true, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64"))); + } + + public void testNegatedFamily() { + ActivationOS os = new ActivationOS(); + os.setFamily("!windows"); + Profile profile = newProfile(os); + + assertActivation(true, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64"))); + assertActivation(false, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64"))); + } + + public void testAllOsConditions() { + ActivationOS os = new ActivationOS(); + os.setFamily("windows"); + os.setName("windows"); + os.setArch("aarch64"); + os.setVersion("99"); + Profile profile = newProfile(os); + + assertActivation(false, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64"))); + assertActivation(false, profile, newContext(null, newProperties("windows", "1", "aarch64"))); + assertActivation(false, profile, newContext(null, newProperties("windows", "99", "amd64"))); + assertActivation(true, profile, newContext(null, newProperties("windows", "99", "aarch64"))); + } +}