Automated rollback of commit 16465d7613348e39e0bcdb22697cf67d257a3d91.
*** Reason for rollback ***
We need to reconsider this.
https://devblogs.microsoft.com/commandline/per-directory-case-sensitivity-and-wsl/ says case-sensitivity can be set per-directory. Requiring correct casing for every directory would make Bazel behave the same on Linux and Windows, and work with WSL-created paths.
*** Original change description ***
glob() now supports case-insensitive mode
The behavior is triggered by
FileSystem.isGlobCaseSensitive() returning false.
None of the production FileSystem implementaitions
return false yet, only some test implementations.
Motivation is to support case-insensitive glob()
on Windows.
See https://github.com/bazelbuild/bazel/issues/8705 and https://github.com/bazelbuild/bazel/issues/8759.
Next we need to add an incompatible flag that
enables this behavior, and add a relevant bit to
the WindowsFileSystem. See https://github.com/bazelbuild/bazel/issues/8767
We must also warn the user somehow if enabling
this feature would change the result of some
globs. A potential approach would be to glob
case-sensitively and case-insensitively at the
same time and warn the user if the results are
different.
PiperOrigin-RevId: 256556254
diff --git a/src/main/java/com/google/devtools/build/lib/packages/GlobCache.java b/src/main/java/com/google/devtools/build/lib/packages/GlobCache.java
index db58cfc..8d1bb74 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/GlobCache.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/GlobCache.java
@@ -255,13 +255,7 @@
}
results.addAll(items);
}
-
- // TODO(laszlocsomor): set `caseSensitive` from the value of
- // `--incompatible_windows_case_insensitive_glob` or from FileSystem.isGlobCaseSensitive()
- // See https://github.com/bazelbuild/bazel/issues/8767
- final boolean caseSensitive = true;
-
- UnixGlob.removeExcludes(results, excludes, caseSensitive);
+ UnixGlob.removeExcludes(results, excludes);
if (!allowEmpty && results.isEmpty()) {
throw new BadGlobException(
"all files in the glob have been excluded, but allow_empty is set to False.");
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/GlobFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/GlobFunction.java
index e40c76f..cabb54f 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/GlobFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/GlobFunction.java
@@ -43,10 +43,6 @@
*/
public final class GlobFunction implements SkyFunction {
- // TODO(laszlocsomor): we might need to create another regexPatternCache for case-insensitive
- // glob() mode to avoid reading wrong cache results when the user changes the
- // --incompatible_windows_case_ignoring_glob flag value between builds.
- // See https://github.com/bazelbuild/bazel/issues/8767.
private final ConcurrentHashMap<String, Pattern> regexPatternCache = new ConcurrentHashMap<>();
private final boolean alwaysUseDirListing;
@@ -156,11 +152,6 @@
}
}
- // TODO(laszlocsomor): set `caseSensitive` from the value of
- // `--incompatible_windows_case_insensitive_glob` or from FileSystem.isGlobCaseSensitive()
- // See https://github.com/bazelbuild/bazel/issues/8767
- final boolean caseSensitive = true;
-
// Now that we have the directory listing, we do three passes over it so as to maximize
// skyframe batching:
// (1) Process every dirent, keeping track of values we need to request if the dirent cannot
@@ -178,7 +169,7 @@
for (Dirent dirent : listingValue.getDirents()) {
Dirent.Type direntType = dirent.getType();
String fileName = dirent.getName();
- if (!UnixGlob.matches(patternHead, fileName, regexPatternCache, caseSensitive)) {
+ if (!UnixGlob.matches(patternHead, fileName, regexPatternCache)) {
continue;
}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
index cfbe006..23a6c77 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
@@ -1011,13 +1011,7 @@
if (legacyIncludesToken != null) {
matches.addAll(delegate.fetch(legacyIncludesToken));
}
-
- // TODO(laszlocsomor): set `caseSensitive` from the value of
- // `--incompatible_windows_case_insensitive_glob` or from FileSystem.isGlobCaseSensitive()
- // See https://github.com/bazelbuild/bazel/issues/8767
- final boolean caseSensitive = true;
-
- UnixGlob.removeExcludes(matches, excludes, caseSensitive);
+ UnixGlob.removeExcludes(matches, excludes);
List<String> result = new ArrayList<>(matches);
// Skyframe glob results are unsorted. And we used a LegacyGlobber that doesn't sort.
// Therefore, we want to unconditionally sort here.
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java
index 7f6b6e0..abaa6b1 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java
@@ -138,22 +138,6 @@
public abstract boolean isFilePathCaseSensitive();
/**
- * Returns true if glob() is case-sensitive.
- *
- * <p>When glob() is case-sensitive, it will only match (or exclude) file "Foo" if the include (or
- * exclude) pattern uses the same upper-case and lower-case letters.
- *
- * <p>When glob() is case-insensitive, it will match (or exclude) the file "Foo" even if the
- * include (or exclude) pattern uses a different casing such as "foO".
- */
- // TODO(laszlocsomor): After `--incompatible_windows_case_insensitive_glob` is flipped to true,
- // remove this method and all references to it and replace call sites with
- // isFilePathCaseSensitive(). See https://github.com/bazelbuild/bazel/issues/8767
- public boolean isGlobCaseSensitive() {
- return true;
- }
-
- /**
* Returns the type of the file system path belongs to.
*
* <p>The string returned is obtained directly from the operating system, so
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/UnixGlob.java b/src/main/java/com/google/devtools/build/lib/vfs/UnixGlob.java
index a6355ef..d9175a2 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/UnixGlob.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/UnixGlob.java
@@ -15,7 +15,6 @@
package com.google.devtools.build.lib.vfs;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Ascii;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
@@ -156,9 +155,9 @@
return null;
}
- /** Calls {@link #matches(String, String, Map) matches(pattern, str, null, boolean)} */
- public static boolean matches(String pattern, String str, boolean caseSensitive) {
- return matches(pattern, str, null, caseSensitive);
+ /** Calls {@link #matches(String, String, Map) matches(pattern, str, null)} */
+ public static boolean matches(String pattern, String str) {
+ return matches(pattern, str, null);
}
/**
@@ -170,8 +169,7 @@
* @param patternCache a cache from patterns to compiled Pattern objects, or {@code null} to skip
* caching
*/
- public static boolean matches(
- String pattern, String str, Map<String, Pattern> patternCache, boolean caseSensitive) {
+ public static boolean matches(String pattern, String str, Map<String, Pattern> patternCache) {
if (pattern.length() == 0 || str.length() == 0) {
return false;
}
@@ -193,30 +191,30 @@
// Common case: *.xyz
if (pattern.charAt(0) == '*' && pattern.lastIndexOf('*') == 0) {
- return endsWithCase(str, pattern.substring(1), caseSensitive);
+ return str.endsWith(pattern.substring(1));
}
// Common case: xyz*
int lastIndex = pattern.length() - 1;
// The first clause of this if statement is unnecessary, but is an
// optimization--charAt runs faster than indexOf.
if (pattern.charAt(lastIndex) == '*' && pattern.indexOf('*') == lastIndex) {
- return startsWithCase(str, pattern.substring(0, lastIndex), caseSensitive);
+ return str.startsWith(pattern.substring(0, lastIndex));
}
Pattern regex =
patternCache == null
- ? makePatternFromWildcard(pattern, caseSensitive)
- : patternCache.computeIfAbsent(pattern, p -> makePatternFromWildcard(p, caseSensitive));
+ ? makePatternFromWildcard(pattern)
+ : patternCache.computeIfAbsent(pattern, p -> makePatternFromWildcard(p));
return regex.matcher(str).matches();
}
/**
- * Returns a regular expression implementing a matcher for "pattern", in which "*" and "?" are
- * wildcards.
+ * Returns a regular expression implementing a matcher for "pattern", in which
+ * "*" and "?" are wildcards.
*
* <p>e.g. "foo*bar?.java" -> "foo.*bar.\\.java"
*/
- private static Pattern makePatternFromWildcard(String pattern, boolean caseSensitive) {
+ private static Pattern makePatternFromWildcard(String pattern) {
StringBuilder regexp = new StringBuilder();
for (int i = 0, len = pattern.length(); i < len; i++) {
char c = pattern.charAt(i);
@@ -249,15 +247,7 @@
regexp.append(c);
break;
default:
- if (caseSensitive || !isAlphaAscii(c)) {
- regexp.append(c);
- } else {
- regexp
- .append('[')
- .append(Ascii.toUpperCase(c))
- .append(Ascii.toLowerCase(c))
- .append(']');
- }
+ regexp.append(c);
break;
}
}
@@ -568,6 +558,7 @@
if (baseStat == null || patterns.isEmpty()) {
return Futures.immediateFuture(Collections.<Path>emptyList());
}
+
List<String[]> splitPatterns = checkAndSplitPatterns(patterns);
// We do a dumb loop, even though it will likely duplicate logical work (note that the
@@ -576,7 +567,6 @@
// glob [*/*.java, sub/*.java, */*.txt]).
pendingOps.incrementAndGet();
try {
- final boolean caseSensitive = base.getFileSystem().isGlobCaseSensitive();
for (String[] splitPattern : splitPatterns) {
int numRecursivePatterns = 0;
for (String pattern : splitPattern) {
@@ -584,12 +574,9 @@
++numRecursivePatterns;
}
}
- GlobTaskContext context =
- numRecursivePatterns > 1
- ? new RecursiveGlobTaskContext(
- splitPattern, excludeDirectories, caseSensitive, dirPred, syscalls)
- : new GlobTaskContext(
- splitPattern, excludeDirectories, caseSensitive, dirPred, syscalls);
+ GlobTaskContext context = numRecursivePatterns > 1
+ ? new RecursiveGlobTaskContext(splitPattern, excludeDirectories, dirPred, syscalls)
+ : new GlobTaskContext(splitPattern, excludeDirectories, dirPred, syscalls);
context.queueGlob(base, baseStat.isDirectory(), 0);
}
} finally {
@@ -698,19 +685,16 @@
private class GlobTaskContext {
private final String[] patternParts;
private final boolean excludeDirectories;
- private final boolean caseSensitive;
private final Predicate<Path> dirPred;
private final FilesystemCalls syscalls;
GlobTaskContext(
String[] patternParts,
boolean excludeDirectories,
- boolean caseSensitive,
Predicate<Path> dirPred,
FilesystemCalls syscalls) {
this.patternParts = patternParts;
this.excludeDirectories = excludeDirectories;
- this.caseSensitive = caseSensitive;
this.dirPred = dirPred;
this.syscalls = syscalls;
}
@@ -760,10 +744,9 @@
private RecursiveGlobTaskContext(
String[] patternParts,
boolean excludeDirectories,
- boolean caseSensitive,
Predicate<Path> dirPred,
FilesystemCalls syscalls) {
- super(patternParts, excludeDirectories, caseSensitive, dirPred, syscalls);
+ super(patternParts, excludeDirectories, dirPred, syscalls);
}
@Override
@@ -837,7 +820,7 @@
// The file is a special file (fifo, etc.). No need to even match against the pattern.
continue;
}
- if (matches(pattern, dent.getName(), cache, context.caseSensitive)) {
+ if (matches(pattern, dent.getName(), cache)) {
Path child = base.getChild(dent.getName());
if (childType == Dirent.Type.SYMLINK) {
@@ -885,17 +868,12 @@
* Filters out exclude patterns from a Set of paths. Common cases such as wildcard-free patterns
* or suffix patterns are special-cased to make this function efficient.
*/
- public static void removeExcludes(
- Set<String> paths, Collection<String> excludes, boolean caseSensitive) {
+ public static void removeExcludes(Set<String> paths, Collection<String> excludes) {
ArrayList<String> complexPatterns = new ArrayList<>(excludes.size());
Map<String, List<String>> starstarSlashStarHeadTailPairs = new HashMap<>();
for (String exclude : excludes) {
if (isWildcardFree(exclude)) {
- if (caseSensitive) {
- paths.remove(exclude);
- } else {
- paths.removeIf(p -> Ascii.equalsIgnoreCase(p, exclude));
- }
+ paths.remove(exclude);
continue;
}
int patternPos = exclude.indexOf("**/*");
@@ -912,9 +890,9 @@
for (Map.Entry<String, List<String>> headTailPair : starstarSlashStarHeadTailPairs.entrySet()) {
paths.removeIf(
path -> {
- if (startsWithCase(path, headTailPair.getKey(), caseSensitive)) {
+ if (path.startsWith(headTailPair.getKey())) {
for (String tail : headTailPair.getValue()) {
- if (endsWithCase(path, tail, caseSensitive)) {
+ if (path.endsWith(tail)) {
return true;
}
}
@@ -931,7 +909,7 @@
path -> {
String[] segments = Iterables.toArray(Splitter.on('/').split(path), String.class);
for (String[] splitPattern : splitPatterns) {
- if (matchesPattern(splitPattern, segments, 0, 0, patternCache, caseSensitive)) {
+ if (matchesPattern(splitPattern, segments, 0, 0, patternCache)) {
return true;
}
}
@@ -941,25 +919,19 @@
/** Returns true if {@code pattern} matches {@code path} starting from the given segments. */
private static boolean matchesPattern(
- String[] pattern,
- String[] path,
- int i,
- int j,
- Map<String, Pattern> patternCache,
- boolean caseSensitive) {
+ String[] pattern, String[] path, int i, int j, Map<String, Pattern> patternCache) {
if (i == pattern.length) {
return j == path.length;
}
if (pattern[i].equals("**")) {
- return matchesPattern(pattern, path, i + 1, j, patternCache, caseSensitive)
- || (j < path.length
- && matchesPattern(pattern, path, i, j + 1, patternCache, caseSensitive));
+ return matchesPattern(pattern, path, i + 1, j, patternCache)
+ || (j < path.length && matchesPattern(pattern, path, i, j + 1, patternCache));
}
if (j == path.length) {
return false;
}
- if (matches(pattern[i], path[j], patternCache, caseSensitive)) {
- return matchesPattern(pattern, path, i + 1, j + 1, patternCache, caseSensitive);
+ if (matches(pattern[i], path[j], patternCache)) {
+ return matchesPattern(pattern, path, i + 1, j + 1, patternCache);
}
return false;
}
@@ -967,28 +939,4 @@
private static boolean isWildcardFree(String pattern) {
return !pattern.contains("*") && !pattern.contains("?");
}
-
- @VisibleForTesting
- static boolean startsWithCase(String s, String p, boolean caseSensitive) {
- if (caseSensitive) {
- return s.startsWith(p);
- } else {
- return s.length() >= p.length() && s.regionMatches(true, 0, p, 0, p.length());
- }
- }
-
- @VisibleForTesting
- static boolean endsWithCase(String s, String p, boolean caseSensitive) {
- if (caseSensitive) {
- return s.endsWith(p);
- } else {
- return s.length() >= p.length()
- && s.regionMatches(true, s.length() - p.length(), p, 0, p.length());
- }
- }
-
- @VisibleForTesting
- static boolean isAlphaAscii(char c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
- }
}
diff --git a/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java b/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
index abb7c63..5b3669b 100644
--- a/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
@@ -106,13 +106,6 @@
}
@Override
- public boolean isGlobCaseSensitive() {
- // TODO(laszlocsomor): return the opposite of `--incompatible_windows_case_insensitive_glob`
- // here. See https://github.com/bazelbuild/bazel/issues/8767
- return true;
- }
-
- @Override
protected boolean fileIsSymbolicLink(File file) {
try {
if (isSymlinkOrJunction(file)) {