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

bodewig pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ant.git

commit 198c8ea1b0e5cedcdc462927c3c9c54010d04ec3
Author: Stefan Bodewig <[email protected]>
AuthorDate: Sun Mar 1 12:25:54 2026 +0100

    make resolvePath work for not existing files as good as possible
---
 src/main/org/apache/tools/ant/util/FileUtils.java  |  34 +++++--
 .../org/apache/tools/ant/util/FileUtilsTest.java   | 101 +++++++++++++++++++++
 2 files changed, 128 insertions(+), 7 deletions(-)

diff --git a/src/main/org/apache/tools/ant/util/FileUtils.java 
b/src/main/org/apache/tools/ant/util/FileUtils.java
index dbc974980..d51218dd7 100644
--- a/src/main/org/apache/tools/ant/util/FileUtils.java
+++ b/src/main/org/apache/tools/ant/util/FileUtils.java
@@ -46,6 +46,7 @@ import java.text.DecimalFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.EnumSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Optional;
@@ -88,8 +89,12 @@ public class FileUtils {
     private static final boolean ON_WIN9X = Os.isFamily("win9x");
     private static final boolean ON_WINDOWS = Os.isFamily("windows");
 
+    // see https://bugs.openjdk.org/browse/JDK-8003887
     private static final boolean CAN_TRUST_GET_CANONICAL_PATH =
         !ON_WINDOWS || JavaEnvUtils.isAtLeastJavaVersion("24");
+    // bug report opened with OpenJDK but hasn't been published, yet
+    private static final boolean 
CAN_TRUST_GET_CANONICAL_PATH_FOR_NOT_EXISTING_FILES =
+        !ON_WINDOWS;
 
     static final int BUF_SIZE = 8192;
 
@@ -2015,15 +2020,30 @@ public class FileUtils {
      * @since Ant 1.10.16
      */
     public String getResolvedPath(File f) throws IOException {
-        if (!CAN_TRUST_GET_CANONICAL_PATH) {
-            try {
-                return f.toPath().toRealPath().toString();
-            } catch (FileNotFoundException ex) {
-                // file or link target doesn't exist, fall back to 
getCanonicalPath
-            } catch (NoSuchFileException ex) {
-                // file or link target doesn't exist, fall back to 
getCanonicalPath
+        if (CAN_TRUST_GET_CANONICAL_PATH_FOR_NOT_EXISTING_FILES
+            || (CAN_TRUST_GET_CANONICAL_PATH && f.exists())) {
+            return f.getCanonicalPath();
+        }
+        if (f.exists()) {
+            return f.toPath().toRealPath().toString();
+        }
+        LinkedList<String> trailer = new LinkedList<>();
+        trailer.push(f.getName());
+        File parent = f.getParentFile();
+        while (parent != null) {
+            if (parent.exists()) {
+                String resolvedParent = getResolvedPath(parent);
+                String[] parentStack = getPathStack(resolvedParent);
+                List<String> rebuilt =
+                    new ArrayList<String>(Arrays.asList(parentStack));
+                rebuilt.addAll(trailer);
+                return getPath(rebuilt, File.separatorChar);
             }
+            trailer.push(parent.getName());
+            parent = parent.getParentFile();
         }
+        // reached root of file system without ever encountering
+        // an existing directory. Giving up.
         return f.getCanonicalPath();
     }
 }
diff --git a/src/tests/junit/org/apache/tools/ant/util/FileUtilsTest.java 
b/src/tests/junit/org/apache/tools/ant/util/FileUtilsTest.java
index bdf1f3245..2d9bd0b1d 100644
--- a/src/tests/junit/org/apache/tools/ant/util/FileUtilsTest.java
+++ b/src/tests/junit/org/apache/tools/ant/util/FileUtilsTest.java
@@ -35,6 +35,8 @@ import org.apache.tools.ant.BuildException;
 import org.apache.tools.ant.MagicTestNames;
 import org.apache.tools.ant.Project;
 import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.taskdefs.condition.CanCreateSymbolicLink;
+import org.apache.tools.ant.taskdefs.optional.windows.Mklink;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -70,6 +72,8 @@ public class FileUtilsTest {
     @Rule
     public TemporaryFolder folder = new TemporaryFolder();
 
+    private static final boolean CAN_CREATE_SYMLINKS = new 
CanCreateSymbolicLink().eval();
+
     private static final String ROOT = 
System.getProperty(MagicTestNames.TEST_ROOT_DIRECTORY);
     private String root;
 
@@ -879,6 +883,92 @@ public class FileUtilsTest {
         assertEquals("file:/foo", 
getFileUtils().stripLeadingPathSeparator("file:/foo"));
     }
 
+    @Test
+    public void getResolvedPathWorksForNormalFilesThatExist() throws 
IOException {
+        File f = folder.newFile();
+        String resolvedPath = getFileUtils().getResolvedPath(f);
+        assertEquals(f.getAbsolutePath(), resolvedPath);
+    }
+
+    @Test
+    public void getResolvedPathWorksForNormalFilesThatDoesntExist() throws 
IOException {
+        File missingDir = new File(folder.getRoot(), "foo");
+        File f = new File(missingDir, "foo");
+        String resolvedPath = getFileUtils().getResolvedPath(f);
+        assertEquals(f.getAbsolutePath(), resolvedPath);
+    }
+
+    @Test
+    public void getResolvedPathWorksForSymlinksThatExist() throws IOException {
+        assumeTrue("setup doesn't support creation of symbolic links",
+                   CAN_CREATE_SYMLINKS);
+        File existingDir = folder.newFolder();
+        File f = new File(folder.getRoot(), "foo");
+        Files.createSymbolicLink(f.toPath(), existingDir.toPath());
+        String resolvedPath = getFileUtils().getResolvedPath(f);
+        assertEquals(existingDir.getAbsolutePath(), resolvedPath);
+    }
+
+    @Test
+    public void getResolvedPathDoesntResolveDanglingSymlinks() throws 
IOException {
+        assumeTrue("setup doesn't support creation of symbolic links",
+                   CAN_CREATE_SYMLINKS);
+        File missingDir = new File(folder.getRoot(), "foo");
+        File f = new File(folder.getRoot(), "bar");
+        Files.createSymbolicLink(f.toPath(), missingDir.toPath());
+        String resolvedPath = getFileUtils().getResolvedPath(f);
+        assertEquals(f.getAbsolutePath(), resolvedPath);
+    }
+
+    @Test
+    public void 
getResolvedPathWorksForNotExistingFilesInExistingSymlinkDirectories() throws 
IOException {
+        assumeTrue("setup doesn't support creation of symbolic links",
+                   CAN_CREATE_SYMLINKS);
+        File existingDir = folder.newFolder();
+        File link = new File(folder.getRoot(), "foo");
+        Files.createSymbolicLink(link.toPath(), existingDir.toPath());
+        File level2 = new File(link, "bar");
+        File f = new File(level2, "baz");
+        String resolvedPath = getFileUtils().getResolvedPath(f);
+        assertEquals(new File(new File(existingDir, "bar"), 
"baz").getAbsolutePath(), resolvedPath);
+    }
+
+    @Test
+    public void getResolvedPathWorksForJunctionsThatExist() throws IOException 
{
+        assumeTrue("setup doesn't support creation of windows NTFS junctions",
+                   Os.isFamily("windows"));
+        File existingDir = folder.newFolder();
+        File f = new File(folder.getRoot(), "foo");
+        createJunction(f, existingDir);
+        String resolvedPath = getFileUtils().getResolvedPath(f);
+        assertEquals(existingDir.getAbsolutePath(), resolvedPath);
+    }
+
+    @Test
+    public void getResolvedPathDoesntResolveDanglingJunctions() throws 
IOException {
+        assumeTrue("setup doesn't support creation of windows NTFS junctions",
+                   Os.isFamily("windows"));
+        File missingDir = folder.newFolder();
+        File f = new File(folder.getRoot(), "bar");
+        createJunction(f, missingDir);
+        assertTrue("failed to delete target directory", missingDir.delete());
+        String resolvedPath = getFileUtils().getResolvedPath(f);
+        assertEquals(f.getAbsolutePath(), resolvedPath);
+    }
+
+    @Test
+    public void 
getResolvedPathWorksForNotExistingFilesInExistingJunctionDirectories() throws 
IOException {
+        assumeTrue("setup doesn't support creation of windows NTFS junctions",
+                   Os.isFamily("windows"));
+        File existingDir = folder.newFolder();
+        File link = new File(folder.getRoot(), "foo");
+        createJunction(link, existingDir);
+        File level2 = new File(link, "bar");
+        File f = new File(level2, "baz");
+        String resolvedPath = getFileUtils().getResolvedPath(f);
+        assertEquals(new File(new File(existingDir, "bar"), 
"baz").getAbsolutePath(), resolvedPath);
+    }
+
     /**
      * adapt file separators to local conventions
      */
@@ -905,4 +995,15 @@ public class FileUtilsTest {
             assertEquals(s1, s2);
         }
     }
+
+    private static void createJunction(File junction, File junctionTarget) {
+        Mklink mklink = new Mklink();
+        mklink.setProject(new Project());
+        mklink.setLink(junction);
+        mklink.setTargetFile(junctionTarget);
+        Mklink.LinkType linkType = new Mklink.LinkType();
+        linkType.setValue("junction");
+        mklink.setLinkType(linkType);
+        mklink.execute();
+    }
 }

Reply via email to