Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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,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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's MSYS?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minimal System, is a software distribution and build platform for Windows

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MSYS2?

Might need to spell this out. Is MSYS really the style of string here, or is this just a Windows path?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

POSIX-style Windows paths produced by MSYS2/Git Bash (and Cygwin), e.g. /c/Users/... or /cygdrive/c/.... The method converts those to native C:... and returns the input if it’s already a native Windows path.

*/
static String msysToWindowsPath(final String p) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p --> path

if (p == null) {
return null;
}
final Matcher m = MSYS_PATH.matcher(p);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m --> matcher

if (m.matches()) {
return Character.toUpperCase(m.group(1).charAt(0)) + ":\\"
+ m.group(2).replace('/', '\\');
}
return p;
}

public MavenCli() {
this(null);
}
Expand Down Expand Up @@ -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[] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

k?

Constants.MAVEN_REPO_LOCAL, // -Dmaven.repo.local
"user.settings", // -s / --settings
"alternateSettings",
"user.toolchains"
}) {
final String v = System.getProperty(k);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid single letter variable names

if (v != null) {
System.setProperty(k, msysToWindowsPath(v));
}
}
}

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);
}
}
}
}