You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2026/10/22 10:12:14 UTC

(maven) branch maven-4.0.x updated: Resolve multiModuleProjectDirectory lexically instead of canonically (fix #11167) (#13650) (#13673)

This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch maven-4.0.x
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/maven-4.0.x by this push:
     new 29a5346f9e Resolve multiModuleProjectDirectory lexically instead of canonically (fix #11167) (#13650) (#13673)
29a5346f9e is described below

commit 29a5346f9ecd3e2c91e6fb8b6ebac78509efb71c
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Thu Oct 22 12:12:08 2026 +0200

    Resolve multiModuleProjectDirectory lexically instead of canonically (fix #11167) (#13650) (#13673)
    
    getCanonicalFile() asks the OS to resolve the path, which can follow a
    symlink, a junction, or (since JDK-8355342, on JDK 25+) a mapped
    Windows network drive to a different location, corrupting the reactor
    root that every module/parent path is resolved against.
    
    Routed through the same getCanonicalPath(Path) helper this class
    already uses elsewhere since #11250, which only normalizes the path
    lexically.
    
    Co-authored-by: Arturo Bernal <ab...@apache.org>
---
 .../main/java/org/apache/maven/cli/MavenCli.java   | 11 +++++-----
 .../java/org/apache/maven/cli/MavenCliTest.java    | 25 ++++++++++++++++++++++
 2 files changed, 31 insertions(+), 5 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 c0f8f10ce9..e9f2cfc03b 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
@@ -346,11 +346,12 @@ void initialize(CliRequest cliRequest) throws ExitException {
                 throw new ExitException(1);
             }
             File basedir = new File(basedirProperty);
-            try {
-                cliRequest.multiModuleProjectDirectory = basedir.getCanonicalFile();
-            } catch (IOException e) {
-                cliRequest.multiModuleProjectDirectory = basedir.getAbsoluteFile();
-            }
+            // Resolve lexically rather than through getCanonicalFile(), which asks the OS to resolve the path
+            // and can substitute it with a different one when a segment is a symlink, junction, or (on Windows)
+            // a mapped network drive, corrupting the reactor root that every relative module/parent path is
+            // later resolved against (MNG-11167).
+            cliRequest.multiModuleProjectDirectory =
+                    getCanonicalPath(basedir.toPath()).toFile();
         }
 
         // We need to locate the top level project which may be pointed at using
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 5867a4537c..44c8ce6212 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
@@ -20,6 +20,7 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.IOException;
 import java.io.PrintStream;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.FileSystem;
@@ -60,6 +61,7 @@
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+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;
@@ -211,6 +213,29 @@ void testMavenConfigInvalid() throws Exception {
         assertThrows(ParseException.class, () -> cli.cli(request));
     }
 
+    /**
+     * MNG-11167: {@code multiModuleProjectDirectory} must keep the path as given, not the path that
+     * {@link File#getCanonicalFile()} resolves a symlinked (or, on Windows, a mapped network drive)
+     * segment to, or every module/parent path resolved relative to it inherits the wrong base.
+     */
+    @Test
+    void testMultiModuleProjectDirectoryIsNotResolvedThroughASymlink(@TempDir Path tempDir) throws Exception {
+        Path real = Files.createDirectories(tempDir.resolve("real"));
+        Path link = tempDir.resolve("link");
+        try {
+            Files.createSymbolicLink(link, real);
+        } catch (IOException | UnsupportedOperationException e) {
+            assumeTrue(false, "symbolic links are not supported on this machine");
+        }
+
+        System.setProperty(MavenCli.MULTIMODULE_PROJECT_DIRECTORY, link.toString());
+        CliRequest request = new CliRequest(new String[0], null);
+
+        cli.initialize(request);
+
+        assertEquals(link.toString(), request.multiModuleProjectDirectory.getPath());
+    }
+
     /**
      * Read .mvn/maven.config with the following definitions:
      * <pre>