From 8545e2c25cdfa7c61b3bd1c32dbc2f4e56d06688 Mon Sep 17 00:00:00 2001 From: Arturo Bernal Date: Thu, 10 Jul 2025 14:43:45 +0200 Subject: [PATCH 1/4] =?UTF-8?q?MNG-8018:=20translate=20MSYS-style=20?= =?UTF-8?q?=E2=80=9C/d/=E2=80=A6=E2=80=9D=20paths=20to=20real=20Windows=20?= =?UTF-8?q?drive=20paths=20early=20in=20MavenCli.initialize(),=20fixing=20?= =?UTF-8?q?nested=20repo=20location=20and=20settings=20lookup.=20Adds=20JU?= =?UTF-8?q?nit=205=20Windows-only=20test=20for=20path=20normalisation.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/apache/maven/cli/MavenCli.java | 35 ++++++++++++++ .../org/apache/maven/cli/MavenCliTest.java | 48 +++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java b/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java index 7f9e5f4a13ba..ce10195fab1e 100644 --- a/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java +++ b/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java @@ -174,6 +174,25 @@ public class MavenCli { private static final Pattern NEXT_LINE = Pattern.compile("\r?\n"); + /** Matches “/d/whatever” or “/C/…”. */ + private static final Pattern MSYS_PATH = Pattern.compile("^/([a-zA-Z])/(.*)$"); + + /** + * Turns “/d/projects/foo” into “D:\\projects\\foo”. + * Returns the original string if it isn't an MSYS-style path. + */ + static String msysToWindowsPath(final String p) { + if (p == null) { + return null; + } + final Matcher m = MSYS_PATH.matcher(p); + if (m.matches()) { + return Character.toUpperCase(m.group(1).charAt(0)) + ":\\" + + m.group(2).replace('/', '\\'); + } + return p; + } + public MavenCli() { this(null); } @@ -363,6 +382,22 @@ void initialize(CliRequest cliRequest) throws ExitException { // String mavenHome = System.getProperty(Constants.MAVEN_HOME); + if (org.codehaus.plexus.util.Os.isFamily("windows")) { + System.setProperty("user.home", msysToWindowsPath(System.getProperty("user.home"))); + + for (final String k : new String[] { + Constants.MAVEN_REPO_LOCAL, // -Dmaven.repo.local + "user.settings", // -s / --settings + "alternateSettings", + "user.toolchains" + }) { + final String v = System.getProperty(k); + if (v != null) { + System.setProperty(k, msysToWindowsPath(v)); + } + } + } + if (mavenHome != null) { System.setProperty( Constants.MAVEN_HOME, diff --git a/compat/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java b/compat/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java index 5867a4537cdf..5cf970345fd5 100644 --- a/compat/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java +++ b/compat/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java @@ -28,6 +28,7 @@ import java.nio.file.Paths; import java.util.Collections; import java.util.List; +import java.util.Properties; import java.util.stream.Stream; import com.google.common.jimfs.Configuration; @@ -60,6 +61,9 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -702,6 +706,50 @@ public void calculateTransferListener(boolean ciEnv, String[] cliArgs, Class calculateTransferListenerArguments() { return Stream.of( Arguments.of(false, new String[] {}, ConsoleMavenTransferListener.class), From 903153888ea4b10700d842e30256ce64393413a3 Mon Sep 17 00:00:00 2001 From: Arturo Bernal Date: Fri, 11 Jul 2025 11:36:24 +0200 Subject: [PATCH 2/4] normalize MSYS-style paths on Windows and add regression test to prevent wrong local-repo location. --- .../maven/cling/invoker/LookupInvoker.java | 38 +++++++++++- .../cling/invoker/mvn/MavenInvokerTest.java | 61 +++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java index f8390c628275..c543f88118c7 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java @@ -120,6 +120,17 @@ public final int invoke(InvokerRequest invokerRequest) { oldProps.putAll(System.getProperties()); ClassLoader oldCL = Thread.currentThread().getContextClassLoader(); try (C context = createContext(invokerRequest)) { + // Normalize user.home for Git Bash-style paths on Windows + if (isWindows()) { + String userHome = System.getProperty("user.home"); + if (userHome != null && userHome.startsWith("/")) { + final String normalizedUserHome = normalizeMsysPath(userHome, context); + if (normalizedUserHome != null) { + System.setProperty("user.home", normalizedUserHome); + context.logger.debug("Normalized user.home '" + userHome + "' to '" + normalizedUserHome + "'"); + } + } + } if (contextConsumer != null) { contextConsumer.accept(context); } @@ -736,7 +747,9 @@ protected Path localRepositoryPath(C context) { } } if (userDefinedLocalRepo != null) { - return context.cwd.resolve(userDefinedLocalRepo); + // Normalize MSYS-style paths on Windows + final String normalizedPath = normalizeMsysPath(userDefinedLocalRepo, context); + return context.cwd.resolve(normalizedPath); } // settings userDefinedLocalRepo = context.effectiveSettings.getLocalRepository(); @@ -918,6 +931,29 @@ protected int calculateDegreeOfConcurrency(String threadConfiguration) { + "'. Supported are int and float values ending with C."); } } + // Helper method to normalize MSYS-style paths on Windows + private String normalizeMsysPath(String path, C context) { + if (path != null && isWindows() && path.startsWith("/")) { + try { + // Convert /d/path to D:\path + final String drive = path.substring(1, 2).toUpperCase(Locale.ENGLISH); + final String rest = path.substring(2).replace("/", "\\"); + final String normalized = drive + ":" + rest; + context.logger.debug("Normalized MSYS path '" + path + "' to '" + normalized + "'"); + System.out.println("Normalized MSYS path '" + path + "' to '" + normalized + "'"); + return normalized; + } catch (final Exception e) { + context.logger.warn("Failed to normalize MSYS path '" + path + "': " + e.getMessage()); + return path; + } + } + return path; + } + + // Helper method to check if running on Windows + private boolean isWindows() { + return System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("win"); + } protected abstract int execute(C context) throws Exception; } diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java index eae08feb2d05..5b6e3b1007d8 100644 --- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTest.java @@ -34,6 +34,8 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.io.CleanupMode; import org.junit.jupiter.api.io.TempDir; @@ -233,4 +235,63 @@ void jimFs() throws Exception { invoke(fs.getPath("/cwd"), fs.getPath("/home"), List.of("verify"), List.of()); } } + + @Test + @EnabledOnOs(OS.WINDOWS) + void msysRepoPathIsNormalised(@TempDir Path tmp) throws Exception { + + final Path cwd = tmp.resolve("project"); + final Path userHome = tmp.resolve("userHome"); + Files.createDirectories(cwd); + Files.createDirectories(userHome.resolve(".m2")); + + /* ---------- PATCH: write minimal, well-formed toolchains.xml ---------- */ + Files.writeString( + userHome.resolve(".m2/toolchains.xml"), + """ + + + + """); + + /* minimal POM so Maven has something to parse */ + Files.writeString( + cwd.resolve("pom.xml"), + """ + + + 4.0.0 + org.apache.maven.samples + sample + 1.0-SNAPSHOT + + """); + + /* pretend we are on Windows so the normaliser runs */ + final String origOs = System.getProperty("os.name"); + System.setProperty("os.name", "Windows 10"); + try { + final List args = List.of("-Dmaven.repo.local=/c/projects/mmm/conf/.m2/repository"); + final List goals = List.of("validate"); + + final Map logs = invoke(cwd, userHome, goals, args); + final String log = String.join("", logs.values()); + + /* no doubled-drive repository attempt */ + assertFalse( + log.contains("\\c\\projects") || log.contains("\\d\\projects"), + "Maven still tried to use a doubled-drive path:\n" + log); + + } finally { + if (origOs == null) { + System.clearProperty("os.name"); + } else { + System.setProperty("os.name", origOs); + } + } + } } From d5c6ad70e818ff6357edc09105a9792ec4106ee4 Mon Sep 17 00:00:00 2001 From: Arturo Bernal Date: Mon, 11 Aug 2025 09:53:03 +0200 Subject: [PATCH 3/4] small changes --- .../java/org/apache/maven/cli/MavenCli.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java b/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java index ce10195fab1e..64e623738853 100644 --- a/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java +++ b/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java @@ -181,16 +181,16 @@ public class MavenCli { * Turns “/d/projects/foo” into “D:\\projects\\foo”. * Returns the original string if it isn't an MSYS-style path. */ - static String msysToWindowsPath(final String p) { - if (p == null) { + static String msysToWindowsPath(final String path) { + if (path == null) { return null; } - final Matcher m = MSYS_PATH.matcher(p); - if (m.matches()) { - return Character.toUpperCase(m.group(1).charAt(0)) + ":\\" - + m.group(2).replace('/', '\\'); + final Matcher matcher = MSYS_PATH.matcher(path); + if (matcher.matches()) { + return Character.toUpperCase(matcher.group(1).charAt(0)) + ":\\" + + matcher.group(2).replace('/', '\\'); } - return p; + return path; } public MavenCli() { @@ -385,15 +385,15 @@ void initialize(CliRequest cliRequest) throws ExitException { if (org.codehaus.plexus.util.Os.isFamily("windows")) { System.setProperty("user.home", msysToWindowsPath(System.getProperty("user.home"))); - for (final String k : new String[] { + for (final String key : new String[] { Constants.MAVEN_REPO_LOCAL, // -Dmaven.repo.local "user.settings", // -s / --settings "alternateSettings", "user.toolchains" }) { - final String v = System.getProperty(k); - if (v != null) { - System.setProperty(k, msysToWindowsPath(v)); + final String value = System.getProperty(key); + if (value != null) { + System.setProperty(key, msysToWindowsPath(value)); } } } From 16334628cdd9afbb66cf572bda31d1cb0baf2e66 Mon Sep 17 00:00:00 2001 From: Arturo Bernal Date: Mon, 11 Aug 2025 11:00:35 +0200 Subject: [PATCH 4/4] clear javadoc --- .../src/main/java/org/apache/maven/cli/MavenCli.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java b/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java index 64e623738853..0f251216e6c8 100644 --- a/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java +++ b/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java @@ -178,8 +178,10 @@ public class MavenCli { private static final Pattern MSYS_PATH = Pattern.compile("^/([a-zA-Z])/(.*)$"); /** - * Turns “/d/projects/foo” into “D:\\projects\\foo”. - * Returns the original string if it isn't an MSYS-style path. + * Converts a POSIX-style Windows path (as used by MSYS2/Git Bash/Cygwin), + * e.g. "/c/Users/alice/file.txt" or "/cygdrive/c/Users/alice/file.txt", + * to a native Windows path "C:\Users\alice\file.txt". + * Returns the original string if it is already a native Windows path. */ static String msysToWindowsPath(final String path) { if (path == null) {