fileSystemCaseSensitivity = new HashMap<>();
static final int BUF_SIZE = 8192;
@@ -1769,4 +1775,67 @@ public class FileUtils {
return Files.newOutputStream(path);
}
}
+
+ /**
+ * Tries to determine the case sensitivity of the filesystem corresponding to the
+ * {@code path}. While doing so, this method might create a temporary file under
+ * the directory represented by the {@code path}, if it's a directory or in the
+ * parent directory of the {@code path}, if it's a file.
+ *
+ * This method works on a best effort basis and will return an {@link Optional#empty()}
+ * if it cannot determine the case sensitivity, either due to exception or for any other
+ * reason.
+ *
+ * @param path The path whose filesystem case sensitivity needs to be checked
+ * @return Returns true if the filesystem corresponding to the passed {@code path}
+ * is case sensitive. Else returns false. If the case sensitivity
+ * cannot be determined for whatever reason, this method returns an
+ * {@link Optional#empty()}
+ * @throws IllegalArgumentException If the passed path is null
+ * @since Ant 1.10.6
+ */
+ public static Optional isCaseSensitiveFileSystem(final Path path) {
+ if (path == null) {
+ throw new IllegalArgumentException("Path cannot be null");
+ }
+ final FileSystem fileSystem = path.getFileSystem();
+ final Boolean caseSensitivity = fileSystemCaseSensitivity.get(fileSystem);
+ if (caseSensitivity != null) {
+ return Optional.of(caseSensitivity);
+ }
+ final String mixedCaseFileNamePrefix = "aNt";
+ Path mixedCaseTmpFile = null;
+ final boolean caseSensitive;
+ try {
+ synchronized (fileSystemCaseSensitivity) {
+ if (fileSystemCaseSensitivity.containsKey(fileSystem)) {
+ return Optional.of(fileSystemCaseSensitivity.get(fileSystem));
+ }
+ if (Files.isRegularFile(path)) {
+ mixedCaseTmpFile = Files.createTempFile(path.getParent(), mixedCaseFileNamePrefix, null);
+ } else if (Files.isDirectory(path)) {
+ mixedCaseTmpFile = Files.createTempFile(path, mixedCaseFileNamePrefix, null);
+ } else {
+ // we can only do our tricks to figure out whether the filesystem is
+ // case sensitive, only if the path is a directory or a file.
+ // In other cases (like the path being non-existent), we don't
+ // have a way to determine that detail
+ return Optional.empty();
+ }
+ final Path lowerCasePath = Paths.get(mixedCaseTmpFile.toString().toLowerCase(Locale.US));
+ caseSensitive = !Files.isSameFile(mixedCaseTmpFile, lowerCasePath);
+ fileSystemCaseSensitivity.put(fileSystem, caseSensitive);
+ }
+ } catch (IOException ioe) {
+ System.err.println("Could not determine the case sensitivity of the " +
+ "filesystem for path " + path + " due to " + ioe);
+ return Optional.empty();
+ } finally {
+ // delete the tmp file
+ if (mixedCaseTmpFile != null) {
+ FileUtils.delete(mixedCaseTmpFile.toFile());
+ }
+ }
+ return Optional.of(caseSensitive);
+ }
}
diff --git a/src/tests/junit/org/apache/tools/ant/taskdefs/SyncTest.java b/src/tests/junit/org/apache/tools/ant/taskdefs/SyncTest.java
index e98b56d60..7a77526a2 100644
--- a/src/tests/junit/org/apache/tools/ant/taskdefs/SyncTest.java
+++ b/src/tests/junit/org/apache/tools/ant/taskdefs/SyncTest.java
@@ -139,6 +139,21 @@ public class SyncTest {
assertThat(buildRule.getFullLog(), not(containsString("Removing orphan file:")));
}
+ /**
+ * Test for bz-62890 bug fix
+ */
+ @Test
+ public void testCaseSensitivityOfDest() {
+ buildRule.executeTarget("casesensitivity-test");
+ final String destDir = buildRule.getProject().getProperty("dest") + "/casecheck";
+ assertFileIsPresent(destDir + "/a.txt");
+ assertFileIsPresent(destDir + "/A.txt");
+ assertFileIsPresent(destDir + "/foo.txt");
+
+ assertFileIsNotPresent(destDir + "/bar.txt");
+ }
+
+
public void assertFileIsPresent(String f) {
assertTrue("Expected file " + f, buildRule.getProject().resolveFile(f).exists());
}
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 f602a25af..d9ef5eadd 100644
--- a/src/tests/junit/org/apache/tools/ant/util/FileUtilsTest.java
+++ b/src/tests/junit/org/apache/tools/ant/util/FileUtilsTest.java
@@ -21,11 +21,18 @@ package org.apache.tools.ant.util;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Locale;
+import java.util.Optional;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.MagicTestNames;
import org.apache.tools.ant.taskdefs.condition.Os;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -681,6 +688,37 @@ public class FileUtilsTest {
assertTrue(FILE_UTILS.isLeadingPath(new File("c:\\foo"), new File("c:\\foo\\"), false));
}
+ /**
+ * Tests {@link FileUtils#isCaseSensitiveFileSystem(Path)} method
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testCaseSensitiveFileSystem() throws Exception {
+ // create a temp file in a fresh directory
+ final Path tmpDir = Files.createTempDirectory(null);
+ final Path tmpFile = Files.createTempFile(tmpDir, null, null);
+ tmpFile.toFile().deleteOnExit();
+ tmpDir.toFile().deleteOnExit();
+ // now check if a file with that same name but different case is considered to exist
+ final boolean existsAsLowerCase = Files.exists(Paths.get(tmpDir.toString(), tmpFile.getFileName().toString().toLowerCase(Locale.US)));
+ final boolean existsAsUpperCase = Files.exists(Paths.get(tmpDir.toString(), tmpFile.getFileName().toString().toUpperCase(Locale.US)));
+ // if the temp file that we created is found to not exist in a particular "case", then
+ // the filesystem is case sensitive
+ final Boolean expectedCaseSensitivity = existsAsLowerCase == false || existsAsUpperCase == false;
+
+ // call the method and pass it a directory
+ Optional actualCaseSensitivity = FileUtils.isCaseSensitiveFileSystem(tmpDir);
+ Assert.assertTrue("Filesystem case sensitivity was expected to be determined", actualCaseSensitivity.isPresent());
+ Assert.assertEquals("Filesystem was expected to be case " + (expectedCaseSensitivity
+ ? "sensitive" : "insensitive"), expectedCaseSensitivity, actualCaseSensitivity.get());
+
+ // now test it out by passing it a file
+ actualCaseSensitivity = FileUtils.isCaseSensitiveFileSystem(tmpFile);
+ Assert.assertTrue("Filesystem case sensitivity was expected to be determined", actualCaseSensitivity.isPresent());
+ Assert.assertEquals("Filesystem was expected to be case " + (expectedCaseSensitivity
+ ? "sensitive" : "insensitive"), expectedCaseSensitivity, actualCaseSensitivity.get());
+ }
/**
* adapt file separators to local conventions
*/