Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,27 @@ 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])/(.*)$");

/**
* 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) {
return null;
}
final Matcher matcher = MSYS_PATH.matcher(path);
if (matcher.matches()) {
return Character.toUpperCase(matcher.group(1).charAt(0)) + ":\\"
+ matcher.group(2).replace('/', '\\');
}
return path;
}

public MavenCli() {
this(null);
}
Expand Down Expand Up @@ -363,6 +384,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 key : new String[] {
Constants.MAVEN_REPO_LOCAL, // -Dmaven.repo.local
"user.settings", // -s / --settings
"alternateSettings",
"user.toolchains"
}) {
final String value = System.getProperty(key);
if (value != null) {
System.setProperty(key, msysToWindowsPath(value));
}
}
}

if (mavenHome != null) {
System.setProperty(
Constants.MAVEN_HOME,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -702,6 +706,50 @@ public void calculateTransferListener(boolean ciEnv, String[] cliArgs, Class<Tra
assertThat(transferListener.getClass(), is(expectedSubClass));
}

/**
* “/d/…” and “/c/…” should convert to proper Windows drive paths.
*/
@Test
void msysPathsAreConverted() {
assertEquals(
"D:/projects/foo", MavenCli.msysToWindowsPath("/d/projects/foo").replace('\\', '/'));
assertEquals("C:/x", MavenCli.msysToWindowsPath("/c/x").replace('\\', '/'));
}

/**
* Full `initialize` run: ensures the JVM properties are rewritten when running on Windows.
*/
@Test
@EnabledOnOs(OS.WINDOWS)
void initializeNormalisesUserHome(@TempDir Path tmpDir) throws Exception {
// Backup current system properties to restore afterwards
final Properties backup = (Properties) System.getProperties().clone();
try {
// Pretend we are on Windows so Os.isFamily("windows") returns true
System.setProperty("os.name", "Windows 10");

// Required by MavenCli.initialize
System.setProperty(MavenCli.MULTIMODULE_PROJECT_DIRECTORY, tmpDir.toString());

// Inject MSYS‑style paths
System.setProperty("user.home", "/d/test/home");
System.setProperty(Constants.MAVEN_REPO_LOCAL, "/d/test/repo");

final MavenCli cli = new MavenCli();
final CliRequest req = new CliRequest(new String[0], null);
cli.initialize(req);

// Assertions: both properties should now be real Windows paths
assertEquals("D:/test/home", System.getProperty("user.home").replace('\\', '/'));
assertEquals(
"D:/test/repo",
System.getProperty(Constants.MAVEN_REPO_LOCAL).replace('\\', '/'));
} finally {
// Restore original system properties
System.setProperties(backup);
}
}

public static Stream<Arguments> calculateTransferListenerArguments() {
return Stream.of(
Arguments.of(false, new String[] {}, ConsoleMavenTransferListener.class),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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"),
"""
<?xml version="1.0" encoding="UTF-8"?>
<toolchains xmlns="http://maven.apache.org/TOOLCHAINS/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd">
</toolchains>
""");

/* minimal POM so Maven has something to parse */
Files.writeString(
cwd.resolve("pom.xml"),
"""
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.maven.samples</groupId>
<artifactId>sample</artifactId>
<version>1.0-SNAPSHOT</version>
</project>
""");

/* 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<String> args = List.of("-Dmaven.repo.local=/c/projects/mmm/conf/.m2/repository");
final List<String> goals = List.of("validate");

final Map<String, String> 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);
}
}
}
}
Loading