Teach skyframe about excluded directories, paths

RecursivePkgFunction now expects both a rooted path to load packages
beneath and a set of paths to exclude. This also augments existing
machinery to deliver this set of paths to exclude.

This leads toward more efficient processing of target patterns in
target pattern sequence evaluation.

--
MOS_MIGRATED_REVID=94020331
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/RecursivePackageProviderBackedTargetPatternResolver.java b/src/main/java/com/google/devtools/build/lib/skyframe/RecursivePackageProviderBackedTargetPatternResolver.java
index a1b16f0..8facbe5 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/RecursivePackageProviderBackedTargetPatternResolver.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/RecursivePackageProviderBackedTargetPatternResolver.java
@@ -13,6 +13,7 @@
 // limitations under the License.
 package com.google.devtools.build.lib.skyframe;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.devtools.build.lib.cmdline.LabelValidator;
 import com.google.devtools.build.lib.cmdline.ResolvedTargets;
 import com.google.devtools.build.lib.cmdline.TargetParsingException;
@@ -156,35 +157,32 @@
   }
 
   @Override
-  public ResolvedTargets<Target> findTargetsBeneathDirectory(
-      String originalPattern, String pathPrefix, boolean rulesOnly)
+  public ResolvedTargets<Target> findTargetsBeneathDirectory(String originalPattern,
+      String directory, boolean rulesOnly, ImmutableSet<String> excludedSubdirectories)
       throws TargetParsingException, InterruptedException {
     FilteringPolicy actualPolicy = rulesOnly
         ? FilteringPolicies.and(FilteringPolicies.RULES_ONLY, policy)
         : policy;
 
-    PathFragment directory = new PathFragment(pathPrefix);
-    if (directory.containsUplevelReferences()) {
-      throw new TargetParsingException("up-level references are not permitted: '"
-          + directory.getPathString() + "'");
-    }
-    if (!pathPrefix.isEmpty() && (LabelValidator.validatePackageName(pathPrefix) != null)) {
-      throw new TargetParsingException("'" + pathPrefix + "' is not a valid package name");
+    PathFragment pathFragment = getPathFragment(directory);
+    ImmutableSet.Builder<PathFragment> excludedPathFragments = ImmutableSet.builder();
+    for (String excludedDirectory : excludedSubdirectories) {
+      excludedPathFragments.add(getPathFragment(excludedDirectory));
     }
 
     ResolvedTargets.Builder<Target> builder = ResolvedTargets.builder();
 
     for (Path root : pkgPath.getPathEntries()) {
-      RootedPath rootedPath = RootedPath.toRootedPath(root, directory);
+      RootedPath rootedPath = RootedPath.toRootedPath(root, pathFragment);
       Iterable<PathFragment> packagesUnderDirectory = recursivePackageProvider
-          .getPackagesUnderDirectory(rootedPath);
+          .getPackagesUnderDirectory(rootedPath, excludedPathFragments.build());
       for (PathFragment pkg : packagesUnderDirectory) {
         builder.merge(getTargetsInPackage(originalPattern, pkg, FilteringPolicies.NO_FILTER));
       }
     }
 
     if (builder.isEmpty()) {
-      throw new TargetParsingException("no targets found beneath '" + directory + "'");
+      throw new TargetParsingException("no targets found beneath '" + pathFragment + "'");
     }
 
     // Apply the transform after the check so we only return the
@@ -202,5 +200,16 @@
     return filteredBuilder.build();
   }
 
+  private static PathFragment getPathFragment(String pathPrefix) throws TargetParsingException {
+    PathFragment directory = new PathFragment(pathPrefix);
+    if (directory.containsUplevelReferences()) {
+      throw new TargetParsingException("up-level references are not permitted: '"
+          + directory.getPathString() + "'");
+    }
+    if (!pathPrefix.isEmpty() && (LabelValidator.validatePackageName(pathPrefix) != null)) {
+      throw new TargetParsingException("'" + pathPrefix + "' is not a valid package name");
+    }
+    return directory;
+  }
 }