This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/maven-clean-plugin.git
commit b5b7cb00e7ee015751764fb3099d534c3d741d39 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Mon Mar 31 17:07:23 2025 +0200 Try to port MCLEAN-93: Windows NTFS junctions support. --- .../org/apache/maven/plugins/clean/Cleaner.java | 48 ++++++++++++++++++---- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/apache/maven/plugins/clean/Cleaner.java b/src/main/java/org/apache/maven/plugins/clean/Cleaner.java index ffac936..624ef47 100644 --- a/src/main/java/org/apache/maven/plugins/clean/Cleaner.java +++ b/src/main/java/org/apache/maven/plugins/clean/Cleaner.java @@ -49,7 +49,9 @@ import org.apache.maven.api.plugin.Log; * @author Martin Desruisseaux */ final class Cleaner implements FileVisitor<Path> { - + /** + * Whether the host operating system is from the Windows family. + */ private static final boolean ON_WINDOWS = (File.separatorChar == '\\'); private static final SessionData.Key<Path> LAST_DIRECTORY_TO_DELETE = @@ -193,7 +195,7 @@ final class Cleaner implements FileVisitor<Path> { var options = EnumSet.noneOf(FileVisitOption.class); if (followSymlinks) { options.add(FileVisitOption.FOLLOW_LINKS); - basedir = getCanonicalPath(basedir); + basedir = getCanonicalPath(basedir, null); } if (selector == null && !followSymlinks && fastDir != null && session != null) { // If anything wrong happens, we'll just use the usual deletion mechanism @@ -275,12 +277,21 @@ final class Cleaner implements FileVisitor<Path> { */ @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if ((selector == null || selector.matches(file)) && tryDelete(file)) { - if (listDeletedFiles) { - logDelete(file, attrs); - } + if (ON_WINDOWS && followSymlinks && attrs.isDirectory() && attrs.isOther()) { + /* + * MCLEAN-93: NTFS junctions have isDirectory() and isOther() attributes set. + * Handle as a directory walked by a new `FileVisitor`. + */ + file = getCanonicalPath(file, null); + Files.walkFileTree(file, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, this); } else { - nonEmptyDirectoryLevels.set(currentDepth); // Remember that the directory will not be empty. + if ((selector == null || selector.matches(file)) && tryDelete(file)) { + if (listDeletedFiles) { + logDelete(file, attrs); + } + } else { + nonEmptyDirectoryLevels.set(currentDepth); // Remember that the directory will not be empty. + } } return FileVisitResult.CONTINUE; } @@ -333,11 +344,30 @@ final class Cleaner implements FileVisitor<Path> { return FileVisitResult.CONTINUE; } - private static Path getCanonicalPath(Path path) { + /** + * Returns the real path of the given file. If the real path cannot be obtained, + * this method tries to get the real path of the parent and to append the rest of + * the filename. + * + * @param path the path to get as a canonical path + * @param mainError should be {@code null} (reserved to recursive calls of this method) + * @return the real path of the given path + * @throws IOException if the canonical path cannot be obtained + */ + private static Path getCanonicalPath(final Path path, IOException mainError) throws IOException { try { return path.toRealPath(); } catch (IOException e) { - return getCanonicalPath(path.getParent()).resolve(path.getFileName()); + if (mainError == null) { + mainError = e; + } else { + mainError.addSuppressed(e); + } + final Path parent = path.getParent(); + if (parent != null) { + return getCanonicalPath(parent, mainError).resolve(path.getFileName()); + } + throw e; } }