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/cmdline/TargetPattern.java b/src/main/java/com/google/devtools/build/lib/cmdline/TargetPattern.java
index ead03af..deeec79 100644
--- a/src/main/java/com/google/devtools/build/lib/cmdline/TargetPattern.java
+++ b/src/main/java/com/google/devtools/build/lib/cmdline/TargetPattern.java
@@ -19,6 +19,7 @@
 import com.google.common.base.Preconditions;
 import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.devtools.build.lib.cmdline.LabelValidator.BadLabelException;
 import com.google.devtools.build.lib.cmdline.LabelValidator.PackageAndTarget;
 
@@ -119,7 +120,20 @@
   /**
    * Evaluates the current target pattern and returns the result.
    */
-  public abstract <T> ResolvedTargets<T> eval(TargetPatternResolver<T> resolver)
+  public <T> ResolvedTargets<T> eval(TargetPatternResolver<T> resolver)
+      throws TargetParsingException, InterruptedException {
+    return eval(resolver, ImmutableSet.<String>of());
+  }
+
+  /**
+   * Evaluates the current target pattern, excluding targets under directories in
+   * {@code excludedSubdirectories}, and returns the result.
+   *
+   * @throws IllegalArgumentException if {@code excludedSubdirectories} is nonempty and this
+   *      pattern does not have type {@code Type.TARGETS_BELOW_DIRECTORY}.
+   */
+  public abstract <T> ResolvedTargets<T> eval(TargetPatternResolver<T> resolver,
+      ImmutableSet<String> excludedSubdirectories)
       throws TargetParsingException, InterruptedException;
 
   /**
@@ -153,8 +167,12 @@
     }
 
     @Override
-    public <T> ResolvedTargets<T> eval(TargetPatternResolver<T> resolver)
+    public <T> ResolvedTargets<T> eval(TargetPatternResolver<T> resolver,
+        ImmutableSet<String> excludedSubdirectories)
         throws TargetParsingException, InterruptedException {
+      Preconditions.checkArgument(excludedSubdirectories.isEmpty(),
+          "Target pattern \"%s\" of type %s cannot be evaluated with excluded subdirectories: %s.",
+          targetName, getType(), excludedSubdirectories);
       return resolver.getExplicitTarget(targetName);
     }
 
@@ -196,8 +214,12 @@
     }
 
     @Override
-    public <T> ResolvedTargets<T> eval(TargetPatternResolver<T> resolver)
+    public <T> ResolvedTargets<T> eval(TargetPatternResolver<T> resolver,
+        ImmutableSet<String> excludedSubdirectories)
         throws TargetParsingException, InterruptedException {
+      Preconditions.checkArgument(excludedSubdirectories.isEmpty(),
+          "Target pattern \"%s\" of type %s cannot be evaluated with excluded subdirectories: %s.",
+          path, getType(), excludedSubdirectories);
       if (resolver.isPackage(path)) {
         // User has specified a package name. lookout for default target.
         return resolver.getExplicitTarget("//" + path);
@@ -268,8 +290,12 @@
     }
 
     @Override
-    public <T> ResolvedTargets<T> eval(TargetPatternResolver<T> resolver)
+    public <T> ResolvedTargets<T> eval(TargetPatternResolver<T> resolver,
+        ImmutableSet<String> excludedSubdirectories)
         throws TargetParsingException, InterruptedException {
+      Preconditions.checkArgument(excludedSubdirectories.isEmpty(),
+          "Target pattern \"%s\" of type %s cannot be evaluated with excluded subdirectories: %s.",
+          originalPattern, getType(), excludedSubdirectories);
       if (checkWildcardConflict) {
         ResolvedTargets<T> targets = getWildcardConflict(resolver);
         if (targets != null) {
@@ -359,9 +385,11 @@
     }
 
     @Override
-    public <T> ResolvedTargets<T> eval(TargetPatternResolver<T> resolver)
+    public <T> ResolvedTargets<T> eval(TargetPatternResolver<T> resolver,
+        ImmutableSet<String> excludedSubdirectories)
         throws TargetParsingException, InterruptedException {
-      return resolver.findTargetsBeneathDirectory(originalPattern, directory, rulesOnly);
+      return resolver.findTargetsBeneathDirectory(originalPattern, directory, rulesOnly,
+          excludedSubdirectories);
     }
 
     @Override
diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/TargetPatternResolver.java b/src/main/java/com/google/devtools/build/lib/cmdline/TargetPatternResolver.java
index ae91caa..3566b23 100644
--- a/src/main/java/com/google/devtools/build/lib/cmdline/TargetPatternResolver.java
+++ b/src/main/java/com/google/devtools/build/lib/cmdline/TargetPatternResolver.java
@@ -14,6 +14,8 @@
 
 package com.google.devtools.build.lib.cmdline;
 
+import com.google.common.collect.ImmutableSet;
+
 /**
  * A callback interface that is used during the process of converting target patterns (such as
  * <code>//foo:all</code>) into one or more lists of targets (such as <code>//foo:foo,
@@ -53,25 +55,30 @@
       boolean rulesOnly) throws TargetParsingException, InterruptedException;
 
   /**
-   * Returns the set containing the targets found below the given {@code pathPrefix}. Conceptually,
-   * this method should look for all packages that start with the {@code pathPrefix} (as a proper
+   * Returns the set containing the targets found below the given {@code directory}. Conceptually,
+   * this method should look for all packages that start with the {@code directory} (as a proper
    * prefix directory, i.e., "foo/ba" is not a proper prefix of "foo/bar/"), and then collect all
    * targets in each such package (subject to {@code rulesOnly}) as if calling {@link
    * #getTargetsInPackage}. The specified directory is not necessarily a valid package name.
    *
-   * <p>Note that the {@code pathPrefix} can be empty, which corresponds to the "//..." pattern.
-   * Implementations may choose not to support this case and throw an exception instead, or may
-   * restrict the set of directories that are considered by default.
+   * <p>Note that the {@code directory} can be empty, which corresponds to the "//..." pattern.
+   * Implementations may choose not to support this case and throw an {@link
+   * IllegalArgumentException} exception instead, or may restrict the set of directories that are
+   * considered by default.
    *
-   * <p>If the {@code pathPrefix} points to a package, then that package should also be part of the
+   * <p>If the {@code directory} points to a package, then that package should also be part of the
    * result.
    *
    * @param originalPattern the original target pattern for error reporting purposes
-   * @param pathPrefix the directory in which to look for packages
+   * @param directory the directory in which to look for packages
    * @param rulesOnly whether to return rules only
+   * @param excludedSubdirectories a set of transitive subdirectories beneath {@code directory}
+   *    to ignore
+   * @throws TargetParsingException under implementation-specific failure conditions
    */
-  ResolvedTargets<T> findTargetsBeneathDirectory(String originalPattern, String pathPrefix,
-      boolean rulesOnly) throws TargetParsingException, InterruptedException;
+  ResolvedTargets<T> findTargetsBeneathDirectory(String originalPattern, String directory,
+      boolean rulesOnly, ImmutableSet<String> excludedSubdirectories)
+      throws TargetParsingException, InterruptedException;
 
   /**
    * Returns true, if and only if the given name corresponds to a package, i.e., a file with the