Make .bazelignore affect globs, as it should.

Fixes #7433.

RELNOTES: None.
PiperOrigin-RevId: 276023691
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 8d1bb74..dbc48f9 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
@@ -17,6 +17,7 @@
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.SettableFuture;
 import com.google.devtools.build.lib.cmdline.PackageIdentifier;
@@ -24,6 +25,7 @@
 import com.google.devtools.build.lib.packages.Globber.BadGlobException;
 import com.google.devtools.build.lib.util.Pair;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.UnixGlob;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -95,6 +97,7 @@
   public GlobCache(
       final Path packageDirectory,
       final PackageIdentifier packageId,
+      final ImmutableSet<PathFragment> blacklistedGlobPrefixes,
       final CachingPackageLocator locator,
       AtomicReference<? extends UnixGlob.FilesystemCalls> syscalls,
       Executor globExecutor,
@@ -111,12 +114,18 @@
           if (directory.equals(packageDirectory)) {
             return true;
           }
+
+          PathFragment subPackagePath =
+              packageId.getPackageFragment().getRelative(directory.relativeTo(packageDirectory));
+
+          for (PathFragment blacklistedPrefix : blacklistedGlobPrefixes) {
+            if (subPackagePath.startsWith(blacklistedPrefix)) {
+              return false;
+            }
+          }
+
           PackageIdentifier subPackageId =
-              PackageIdentifier.create(
-                  packageId.getRepository(),
-                  packageId
-                      .getPackageFragment()
-                      .getRelative(directory.relativeTo(packageDirectory)));
+              PackageIdentifier.create(packageId.getRepository(), subPackagePath);
           return locator.getBuildFileForPackage(subPackageId) == null;
         };
   }
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
index 3ffeeac..82bcc56 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
@@ -19,6 +19,7 @@
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.LabelConstants;
 import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
@@ -80,6 +81,7 @@
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.RootedPath;
 import com.google.devtools.build.lib.vfs.UnixGlob;
 import java.io.IOException;
@@ -1507,7 +1509,8 @@
     }
 
     Globber globber =
-        createLegacyGlobber(buildFile.asPath().getParentDirectory(), packageId, locator);
+        createLegacyGlobber(
+            buildFile.asPath().getParentDirectory(), packageId, ImmutableSet.of(), locator);
     ParserInput input =
         ParserInput.create(
             FileSystemUtils.convertFromLatin1(buildFileBytes), buildFile.asPath().asFragment());
@@ -1536,11 +1539,13 @@
   public LegacyGlobber createLegacyGlobber(
       Path packageDirectory,
       PackageIdentifier packageId,
+      ImmutableSet<PathFragment> blacklistedGlobPrefixes,
       CachingPackageLocator locator) {
     return createLegacyGlobber(
         new GlobCache(
             packageDirectory,
             packageId,
+            blacklistedGlobPrefixes,
             locator,
             syscalls,
             executor,
@@ -1552,26 +1557,6 @@
     return new LegacyGlobber(globCache, /*sort=*/ true);
   }
 
-  /**
-   * Returns a new {@link LegacyGlobber}, the same as in {@link #createLegacyGlobber}, except that
-   * the implementation of {@link Globber#fetch} intentionally breaks the contract and doesn't
-   * return sorted results.
-   */
-  public LegacyGlobber createLegacyGlobberThatDoesntSort(
-      Path packageDirectory,
-      PackageIdentifier packageId,
-      CachingPackageLocator locator) {
-    return new LegacyGlobber(
-        new GlobCache(
-            packageDirectory,
-            packageId,
-            locator,
-            syscalls,
-            executor,
-            maxDirectoriesToEagerlyVisitInGlobbing),
-        /*sort=*/ false);
-  }
-
   @Nullable
   private byte[] maybeGetBuildFileBytes(Path buildFile, ExtendedEventHandler eventHandler) {
     try {
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 cabb54f..8f125a1 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
@@ -56,9 +56,23 @@
       throws GlobFunctionException, InterruptedException {
     GlobDescriptor glob = (GlobDescriptor) skyKey.argument();
 
+    BlacklistedPackagePrefixesValue blacklistedPackagePrefixes =
+        (BlacklistedPackagePrefixesValue) env.getValue(BlacklistedPackagePrefixesValue.key());
+    if (env.valuesMissing()) {
+      return null;
+    }
+
+    PathFragment globSubdir = glob.getSubdir();
+    PathFragment dirPathFragment = glob.getPackageId().getPackageFragment().getRelative(globSubdir);
+
+    for (PathFragment blacklistedPrefix : blacklistedPackagePrefixes.getPatterns()) {
+      if (dirPathFragment.startsWith(blacklistedPrefix)) {
+        return GlobValue.EMPTY;
+      }
+    }
+
     // Note that the glob's package is assumed to exist which implies that the package's BUILD file
     // exists which implies that the package's directory exists.
-    PathFragment globSubdir = glob.getSubdir();
     if (!globSubdir.equals(PathFragment.EMPTY_FRAGMENT)) {
       PackageLookupValue globSubdirPkgLookupValue =
           (PackageLookupValue)
@@ -99,7 +113,7 @@
 
     boolean globMatchesBareFile = patternTail == null;
 
-    PathFragment dirPathFragment = glob.getPackageId().getPackageFragment().getRelative(globSubdir);
+
     RootedPath dirRootedPath = RootedPath.toRootedPath(glob.getPackageRoot(), dirPathFragment);
     if (alwaysUseDirListing || containsGlobs(patternHead)) {
       // Pattern contains globs, so a directory listing is required.
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 6a2593e..a738236 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
@@ -380,36 +380,24 @@
     }
     WorkspaceNameValue workspaceNameValue =
         (WorkspaceNameValue) env.getValue(WorkspaceNameValue.key());
-    if (workspaceNameValue == null) {
-      return null;
-    }
-    String workspaceName = workspaceNameValue.getName();
 
     RepositoryMappingValue repositoryMappingValue =
         (RepositoryMappingValue)
             env.getValue(RepositoryMappingValue.key(packageId.getRepository()));
-    if (repositoryMappingValue == null) {
-      return null;
-    }
-    ImmutableMap<RepositoryName, RepositoryName> repositoryMapping =
-        repositoryMappingValue.getRepositoryMapping();
-
     RootedPath buildFileRootedPath = packageLookupValue.getRootedPath(packageId);
 
     FileValue buildFileValue = getBuildFileValue(env, buildFileRootedPath);
-    if (buildFileValue == null) {
-      return null;
-    }
-
     RuleVisibility defaultVisibility = PrecomputedValue.DEFAULT_VISIBILITY.get(env);
-    if (defaultVisibility == null) {
+    StarlarkSemantics starlarkSemantics = PrecomputedValue.STARLARK_SEMANTICS.get(env);
+    BlacklistedPackagePrefixesValue blacklistedPackagePrefixes =
+        (BlacklistedPackagePrefixesValue) env.getValue(BlacklistedPackagePrefixesValue.key());
+    if (env.valuesMissing()) {
       return null;
     }
 
-    StarlarkSemantics starlarkSemantics = PrecomputedValue.STARLARK_SEMANTICS.get(env);
-    if (starlarkSemantics == null) {
-      return null;
-    }
+    String workspaceName = workspaceNameValue.getName();
+    ImmutableMap<RepositoryName, RepositoryName> repositoryMapping =
+        repositoryMappingValue.getRepositoryMapping();
 
     // Load the prelude from the same repository as the package being loaded.  Can't use
     // Label.resolveRepositoryRelative because preludeLabel is in the main repository, not the
@@ -452,6 +440,7 @@
           loadPackage(
               workspaceName,
               repositoryMapping,
+              blacklistedPackagePrefixes.getPatterns(),
               packageId,
               buildFileRootedPath,
               buildFileValue,
@@ -1102,10 +1091,12 @@
   private GlobberWithSkyframeGlobDeps makeGlobber(
       Path buildFilePath,
       PackageIdentifier packageId,
+      ImmutableSet<PathFragment> blacklistedGlobPrefixes,
       Root packageRoot,
       SkyFunction.Environment env) {
-    LegacyGlobber legacyGlobber = packageFactory.createLegacyGlobber(
-        buildFilePath.getParentDirectory(), packageId, packageLocator);
+    LegacyGlobber legacyGlobber =
+        packageFactory.createLegacyGlobber(
+            buildFilePath.getParentDirectory(), packageId, blacklistedGlobPrefixes, packageLocator);
     switch (incrementalityIntent) {
       case INCREMENTAL:
         return new SkyframeHybridGlobber(packageId, packageRoot, env, legacyGlobber);
@@ -1133,6 +1124,7 @@
   private LoadedPackageCacheEntry loadPackage(
       String workspaceName,
       ImmutableMap<RepositoryName, RepositoryName> repositoryMapping,
+      ImmutableSet<PathFragment> blacklistedGlobPrefixes,
       PackageIdentifier packageId,
       RootedPath buildFilePath,
       @Nullable FileValue buildFileValue,
@@ -1212,7 +1204,7 @@
       // Therefore, it is safe to invalidate the astCache entry for this packageId here.
       astCache.invalidate(packageId);
       GlobberWithSkyframeGlobDeps globberWithSkyframeGlobDeps =
-          makeGlobber(inputFile, packageId, packageRoot, env);
+          makeGlobber(inputFile, packageId, blacklistedGlobPrefixes, packageRoot, env);
       long startTimeNanos = BlazeClock.nanoTime();
       Package.Builder pkgBuilder =
           packageFactory.createPackageFromAst(
diff --git a/src/test/java/com/google/devtools/build/lib/packages/GlobCacheTest.java b/src/test/java/com/google/devtools/build/lib/packages/GlobCacheTest.java
index 1ea1658c..f8eeb1f 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/GlobCacheTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/GlobCacheTest.java
@@ -16,6 +16,7 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.devtools.build.lib.cmdline.PackageIdentifier;
 import com.google.devtools.build.lib.packages.Globber.BadGlobException;
@@ -23,6 +24,7 @@
 import com.google.devtools.build.lib.testutil.TestUtils;
 import com.google.devtools.build.lib.util.Pair;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -86,10 +88,15 @@
     scratch.file("isolated/sub/sub.js",
         "# this is sub/sub.js");
 
+    createCache();
+  }
+
+  private void createCache(PathFragment... blacklistedDirectories) {
     cache =
         new GlobCache(
             packageDirectory,
             PackageIdentifier.createInMainRepo("isolated"),
+            ImmutableSet.copyOf(blacklistedDirectories),
             new CachingPackageLocator() {
               @Override
               public Path getBuildFileForPackage(PackageIdentifier packageId) {
@@ -114,6 +121,18 @@
   }
 
   @Test
+  public void testBlacklistedDirectory() throws Exception {
+    createCache(PathFragment.create("isolated/foo"));
+    List<Path> paths = cache.safeGlobUnsorted("**/*.js", true).get();
+    assertPathsAre(
+        paths,
+        "/workspace/isolated/first.js",
+        "/workspace/isolated/second.js",
+        "/workspace/isolated/bar/first.js",
+        "/workspace/isolated/bar/second.js");
+  }
+
+  @Test
   public void testSafeGlob() throws Exception {
     List<Path> paths = cache.safeGlobUnsorted("*.js", false).get();
     assertPathsAre(paths,
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryApparatus.java b/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryApparatus.java
index e1b2bdb..fd9a311 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryApparatus.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryApparatus.java
@@ -15,6 +15,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.LabelConstants;
 import com.google.devtools.build.lib.cmdline.PackageIdentifier;
@@ -152,6 +153,7 @@
         new GlobCache(
             buildFile.asPath().getParentDirectory(),
             packageId,
+            ImmutableSet.of(),
             getPackageLocator(),
             null,
             TestUtils.getPool(),
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryTestBase.java b/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryTestBase.java
index ec94250..c79eda7 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryTestBase.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryTestBase.java
@@ -18,6 +18,7 @@
 import static org.junit.Assert.fail;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.devtools.build.lib.cmdline.PackageIdentifier;
 import com.google.devtools.build.lib.events.Event;
@@ -113,6 +114,7 @@
         new GlobCache(
             pkg.getFilename().asPath().getParentDirectory(),
             pkg.getPackageIdentifier(),
+            ImmutableSet.of(),
             PackageFactoryApparatus.createEmptyLocator(),
             null,
             TestUtils.getPool(),
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java
index 3e5f330..f5ee322 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java
@@ -170,10 +170,11 @@
             deletedPackages,
             CrossRepositoryLabelViolationStrategy.ERROR,
             BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
-    skyFunctions.put(SkyFunctions.BLACKLISTED_PACKAGE_PREFIXES,
+    skyFunctions.put(
+        SkyFunctions.BLACKLISTED_PACKAGE_PREFIXES,
         new BlacklistedPackagePrefixesFunction(
             /*hardcodedBlacklistedPackagePrefixes=*/ ImmutableSet.of(),
-            /*additionalBlacklistedPackagePrefixesFile=*/ PathFragment.EMPTY_FRAGMENT));
+            BazelSkyframeExecutorConstants.ADDITIONAL_BLACKLISTED_PACKAGE_PREFIXES_FILE));
     skyFunctions.put(
         FileStateValue.FILE_STATE,
         new FileStateFunction(
@@ -229,6 +230,27 @@
   }
 
   @Test
+  public void testBlacklist() throws Exception {
+    FileSystemUtils.writeContentAsLatin1(root.getRelative(".bazelignore"), "pkg/foo/bar");
+    assertGlobMatches("foo/**", "foo/barnacle/wiz", "foo/barnacle", "foo");
+    differencer.invalidate(
+        ImmutableList.of(
+            FileStateValue.key(
+                RootedPath.toRootedPath(
+                    Root.fromPath(root), PathFragment.create(".bazelignore")))));
+
+    FileSystemUtils.createEmptyFile(root.getRelative(".bazelignore"));
+    assertGlobMatches(
+        "foo/**",
+        "foo/bar/wiz",
+        "foo/bar/wiz/file",
+        "foo/bar",
+        "foo/barnacle/wiz",
+        "foo/barnacle",
+        "foo");
+  }
+
+  @Test
   public void testStartsWithStar() throws Exception {
     assertGlobMatches("*oo", /* => */ "foo");
   }
diff --git a/src/test/shell/bazel/bazelignore_test.sh b/src/test/shell/bazel/bazelignore_test.sh
index a294a38..e144ab9 100755
--- a/src/test/shell/bazel/bazelignore_test.sh
+++ b/src/test/shell/bazel/bazelignore_test.sh
@@ -25,6 +25,41 @@
 
 set -e
 
+test_does_not_glob_into_ignored_directory() {
+    rm -rf work && mkdir work && cd work
+    create_workspace_with_default_repos WORKSPACE
+
+    echo 'filegroup(name="f", srcs=glob(["**"]))' > BUILD
+    echo 'ignored' > .bazelignore
+    mkdir -p ignored/pkg
+    echo 'filegroup(name="f", srcs=glob(["**"]))' > ignored/pkg/BUILD
+    touch ignored/pkg/a
+    touch ignored/file
+    bazel query //:all-targets > "$TEST_TMPDIR/targets"
+    assert_not_contains "//:ignored/file" "$TEST_TMPDIR/targets"
+    assert_not_contains "//:ignored/pkg/BUILD" "$TEST_TMPDIR/targets"
+    assert_not_contains "//:ignored/pkg/a" "$TEST_TMPDIR/targets"
+
+    # This weird line tests whether .bazelignore also stops the Skyframe-based
+    # glob. Globbing (as of 2019 Oct) is done in a hybrid fashion: we do the
+    # legacy globbing because it's faster and Skyframe globbing because it's
+    # more incremental. In the first run, we get the results of the legacy
+    # globbing, but if we invalidate the BUILD file, the result of the legacy
+    # glob is invalidated and but the better incrementality allows the result of
+    # the Skyframe glob to be cached.
+    echo "# change" >> BUILD
+    bazel query //:all-targets > "$TEST_TMPDIR/targets"
+    assert_not_contains "//:ignored/file" "$TEST_TMPDIR/targets"
+    assert_not_contains "//:ignored/pkg/BUILD" "$TEST_TMPDIR/targets"
+    assert_not_contains "//:ignored/pkg/a" "$TEST_TMPDIR/targets"
+
+    echo > .bazelignore
+    bazel query //:all-targets > "$TEST_TMPDIR/targets"
+    assert_contains "//:ignored/file" "$TEST_TMPDIR/targets"
+    bazel query //ignored/pkg:all-targets > "$TEST_TMPDIR/targets"
+    assert_contains "//ignored/pkg:a" "$TEST_TMPDIR/targets"
+}
+
 test_broken_BUILD_files_ignored() {
     rm -rf work && mkdir work && cd work
     create_workspace_with_default_repos WORKSPACE