Parse built-in WORKSPACE files

This doesn't actually add any default workspace files, yet, but adds machinery
for them.  Also did some cleanup/renaming.

--
MOS_MIGRATED_REVID=86085127
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
index e84f9aa..26e0617 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
@@ -40,6 +40,7 @@
 import com.google.devtools.build.lib.syntax.SkylarkModule;
 import com.google.devtools.build.lib.syntax.SkylarkType;
 import com.google.devtools.build.lib.syntax.ValidationEnvironment;
+import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.common.options.OptionsClassProvider;
 
 import java.lang.reflect.Constructor;
@@ -76,6 +77,7 @@
    * Builder for {@link ConfiguredRuleClassProvider}.
    */
   public static class Builder implements RuleDefinitionEnvironment {
+    private final List<PathFragment> defaultWorkspaceFiles = new ArrayList<>();
     private final List<ConfigurationFragmentFactory> configurationFragments = new ArrayList<>();
     private final List<BuildInfoFactory> buildInfoFactories = new ArrayList<>();
     private final List<Class<? extends FragmentOptions>> configurationOptions = new ArrayList<>();
@@ -90,6 +92,10 @@
     private PrerequisiteValidator prerequisiteValidator;
     private ImmutableMap<String, SkylarkType> skylarkAccessibleJavaClasses = ImmutableMap.of();
 
+    public void addWorkspaceFile(PathFragment defaultWorkspace) {
+      defaultWorkspaceFiles.add(defaultWorkspace);
+    }
+
     public Builder setPrerequisiteValidator(PrerequisiteValidator prerequisiteValidator) {
       this.prerequisiteValidator = prerequisiteValidator;
       return this;
@@ -194,6 +200,7 @@
       return new ConfiguredRuleClassProvider(
           ImmutableMap.copyOf(ruleClassMap),
           ImmutableMap.copyOf(ruleDefinitionMap),
+          ImmutableList.copyOf(defaultWorkspaceFiles),
           ImmutableList.copyOf(buildInfoFactories),
           ImmutableList.copyOf(configurationOptions),
           ImmutableList.copyOf(configurationFragments),
@@ -225,6 +232,12 @@
   });
 
   /**
+   * A list of relative paths to the WORKSPACE files needed to provide external dependencies for
+   * the rule classes.
+   */
+  private ImmutableList<PathFragment> defaultWorkspaceFiles;
+
+  /**
    * Maps rule class name to the metaclass instance for that rule.
    */
   private final ImmutableMap<String, RuleClass> ruleClassMap;
@@ -260,6 +273,7 @@
   public ConfiguredRuleClassProvider(
       ImmutableMap<String, RuleClass> ruleClassMap,
       ImmutableMap<String, Class<? extends RuleDefinition>> ruleDefinitionMap,
+      ImmutableList<PathFragment> defaultWorkspaceFiles,
       ImmutableList<BuildInfoFactory> buildInfoFactories,
       ImmutableList<Class<? extends FragmentOptions>> configurationOptions,
       ImmutableList<ConfigurationFragmentFactory> configurationFragments,
@@ -269,6 +283,7 @@
 
     this.ruleClassMap = ruleClassMap;
     this.ruleDefinitionMap = ruleDefinitionMap;
+    this.defaultWorkspaceFiles = defaultWorkspaceFiles;
     this.buildInfoFactories = buildInfoFactories;
     this.configurationOptions = configurationOptions;
     this.configurationFragments = configurationFragments;
@@ -373,4 +388,9 @@
   public Object getNativeModule() {
     return nativeModule;
   }
+
+  @Override
+  public List<PathFragment> getWorkspaceFiles() {
+    return defaultWorkspaceFiles;
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaRuleClasses.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaRuleClasses.java
index 663b82a..f5d928d 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaRuleClasses.java
@@ -40,6 +40,7 @@
 import com.google.devtools.build.lib.packages.TriState;
 import com.google.devtools.build.lib.rules.java.JavaSemantics;
 import com.google.devtools.build.lib.util.FileTypeSet;
+import com.google.devtools.build.lib.vfs.PathFragment;
 
 import java.util.Set;
 
@@ -52,7 +53,7 @@
       PackageNameConstraint.ANY_SEGMENT, "java", "javatests");
 
   public static final ImplicitOutputsFunction JAVA_BINARY_IMPLICIT_OUTPUTS =
-      fromFunctions(JavaSemantics.JAVA_BINARY_CLASS_JAR, JavaSemantics.JAVA_BINARY_SOURCE_JAR, 
+      fromFunctions(JavaSemantics.JAVA_BINARY_CLASS_JAR, JavaSemantics.JAVA_BINARY_SOURCE_JAR,
           JavaSemantics.JAVA_BINARY_DEPLOY_JAR, JavaSemantics.JAVA_BINARY_DEPLOY_SOURCE_JAR);
 
   static final ImplicitOutputsFunction JAVA_LIBRARY_IMPLICIT_OUTPUTS =
@@ -170,4 +171,12 @@
           .build();
     }
   }
+
+  /**
+   * Returns the relative path to the WORKSPACE file describing the external dependencies necessary
+   * for the Java rules.
+   */
+  public static PathFragment getDefaultWorkspace() {
+    return new PathFragment("jdk.WORKSPACE");
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/packages/ExternalPackage.java b/src/main/java/com/google/devtools/build/lib/packages/ExternalPackage.java
index 9c92b33..ac4920c 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/ExternalPackage.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/ExternalPackage.java
@@ -34,6 +34,7 @@
  */
 public class ExternalPackage extends Package {
 
+  private String workspaceName;
   private Map<RepositoryName, Rule> repositoryMap;
 
   ExternalPackage() {
@@ -41,6 +42,13 @@
   }
 
   /**
+   * Returns the name for this repository.
+   */
+  public String getWorkspaceName() {
+    return workspaceName;
+  }
+
+  /**
    * Returns a description of the repository with the given name, or null if there's no such
    * repository.
    */
@@ -79,28 +87,37 @@
   /**
    * Given a workspace file path, creates an ExternalPackage.
    */
-  public static class ExternalPackageBuilder
-      extends AbstractBuilder<ExternalPackage, ExternalPackageBuilder> {
+  public static class Builder
+      extends AbstractBuilder<ExternalPackage, Builder> {
+    private String workspaceName;
     private Map<Label, Binding> bindMap = Maps.newHashMap();
     private Map<RepositoryName, Rule> repositoryMap = Maps.newHashMap();
 
-    public ExternalPackageBuilder(Path workspacePath) {
+    public Builder(Path workspacePath) {
       super(new ExternalPackage());
       setFilename(workspacePath);
       setMakeEnv(new MakeEnvironment.Builder());
     }
 
     @Override
-    protected ExternalPackageBuilder self() {
+    protected Builder self() {
       return this;
     }
 
     @Override
     public ExternalPackage build() {
+      pkg.workspaceName = workspaceName;
       pkg.repositoryMap = ImmutableMap.copyOf(repositoryMap);
       return super.build();
     }
 
+    /**
+     * Sets the name for this repository.
+     */
+    public void setWorkspaceName(String name) {
+      workspaceName = name;
+    }
+
     public void addBinding(Label label, Binding binding) {
       bindMap.put(label, binding);
     }
@@ -178,7 +195,7 @@
      * Creates an external repository rule.
      * @throws SyntaxException if the repository name is invalid.
      */
-    public ExternalPackageBuilder createAndAddRepositoryRule(RuleClass ruleClass,
+    public Builder createAndAddRepositoryRule(RuleClass ruleClass,
         Map<String, Object> kwargs, FuncallExpression ast)
             throws InvalidRuleException, NameConflictException, SyntaxException {
       StoredEventHandler eventHandler = new StoredEventHandler();
diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/packages/RuleClassProvider.java
index 90fdfca..07b1b18 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/RuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/RuleClassProvider.java
@@ -17,7 +17,9 @@
 import com.google.devtools.build.lib.events.EventHandler;
 import com.google.devtools.build.lib.syntax.SkylarkEnvironment;
 import com.google.devtools.build.lib.syntax.ValidationEnvironment;
+import com.google.devtools.build.lib.vfs.PathFragment;
 
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -46,4 +48,11 @@
    * Returns the Skylark module to register the native rules with.
    */
   Object getNativeModule();
+
+  /**
+   * Returns paths to the WORKSPACE files needed to provide external dependencies for built-in
+   * rules.  The PathFragments are relative to Bazel's install directory. Returns an empty list if
+   * there are none defined.
+   */
+  List<PathFragment> getWorkspaceFiles();
 }
diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java b/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java
index c79bbaa..cffef50 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java
@@ -23,7 +23,6 @@
 import com.google.devtools.build.lib.syntax.FuncallExpression;
 import com.google.devtools.build.lib.syntax.Label;
 import com.google.devtools.build.lib.syntax.Label.SyntaxException;
-import com.google.devtools.build.lib.vfs.PathFragment;
 
 import java.util.Map;
 import java.util.Set;
@@ -106,7 +105,7 @@
       throw new InvalidRuleException("illegal rule name: " + name + ": " + e.getMessage());
     }
     boolean inWorkspaceFile = location.getPath() != null
-        && location.getPath().endsWith(new PathFragment("WORKSPACE"));
+        && location.getPath().getBaseName().contains("WORKSPACE");
     if (ruleClass.getWorkspaceOnly() && !inWorkspaceFile) {
       throw new RuleFactory.InvalidRuleException(ruleClass + " must be in the WORKSPACE file "
           + "(used by " + label + ")");
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
index a30b5c7..b7d6385 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
@@ -303,7 +303,7 @@
         configurationFactory, clientEnv, configurationPackages));
     map.put(SkyFunctions.CONFIGURATION_FRAGMENT, new ConfigurationFragmentFunction(
         configurationFragments, configurationPackages));
-    map.put(SkyFunctions.WORKSPACE_FILE, new WorkspaceFileFunction(pkgFactory));
+    map.put(SkyFunctions.WORKSPACE_FILE, new WorkspaceFileFunction(pkgFactory, directories));
     map.put(SkyFunctions.TARGET_COMPLETION, new TargetCompletionFunction(eventBus));
     map.put(SkyFunctions.TEST_COMPLETION, new TestCompletionFunction());
     map.put(SkyFunctions.ARTIFACT, new ArtifactFunction(allowedMissingInputs));
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/WorkspaceFileFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/WorkspaceFileFunction.java
index f518b8a..89126cb 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/WorkspaceFileFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/WorkspaceFileFunction.java
@@ -17,12 +17,14 @@
 import static com.google.devtools.build.lib.syntax.Environment.NONE;
 
 import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.analysis.BlazeDirectories;
 import com.google.devtools.build.lib.cmdline.LabelValidator;
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.events.StoredEventHandler;
+import com.google.devtools.build.lib.packages.ExternalPackage;
 import com.google.devtools.build.lib.packages.ExternalPackage.Binding;
-import com.google.devtools.build.lib.packages.ExternalPackage.ExternalPackageBuilder;
-import com.google.devtools.build.lib.packages.ExternalPackage.ExternalPackageBuilder.NoSuchBindingException;
+import com.google.devtools.build.lib.packages.ExternalPackage.Builder;
+import com.google.devtools.build.lib.packages.ExternalPackage.Builder.NoSuchBindingException;
 import com.google.devtools.build.lib.packages.Package.NameConflictException;
 import com.google.devtools.build.lib.packages.PackageFactory;
 import com.google.devtools.build.lib.packages.RuleClass;
@@ -39,6 +41,7 @@
 import com.google.devtools.build.lib.syntax.MixedModeFunction;
 import com.google.devtools.build.lib.syntax.ParserInputSource;
 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.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyFunctionException;
@@ -58,22 +61,46 @@
   private static final String BIND = "bind";
 
   private final PackageFactory packageFactory;
+  private final Path installDir;
 
-  WorkspaceFileFunction(PackageFactory packageFactory) {
+  WorkspaceFileFunction(PackageFactory packageFactory, BlazeDirectories directories) {
     this.packageFactory = packageFactory;
+    this.installDir = directories.getEmbeddedBinariesRoot();
   }
 
   @Override
   public SkyValue compute(SkyKey skyKey, Environment env) throws WorkspaceFileFunctionException,
       InterruptedException {
     RootedPath workspaceRoot = (RootedPath) skyKey.argument();
-    // Explicitly make skyframe load this file.
     if (env.getValue(FileValue.key(workspaceRoot)) == null) {
       return null;
     }
-    Path workspaceFilePath = workspaceRoot.getRoot().getRelative(workspaceRoot.getRelativePath());
-    WorkspaceNameHolder holder = new WorkspaceNameHolder();
-    ExternalPackageBuilder builder = new ExternalPackageBuilder(workspaceFilePath);
+
+    Path repoWorkspace = workspaceRoot.getRoot().getRelative(workspaceRoot.getRelativePath());
+    Builder builder = new Builder(repoWorkspace);
+    List<PathFragment> workspaceFiles = packageFactory.getRuleClassProvider().getWorkspaceFiles();
+    for (PathFragment workspaceFile : workspaceFiles) {
+      workspaceRoot = RootedPath.toRootedPath(installDir, workspaceFile);
+      if (env.getValue(FileValue.key(workspaceRoot)) == null) {
+        return null;
+      }
+      parseWorkspaceFile(installDir.getRelative(workspaceFile), builder);
+    }
+    parseWorkspaceFile(repoWorkspace, builder);
+    try {
+      builder.resolveBindTargets(packageFactory.getRuleClass(BIND));
+    } catch (NoSuchBindingException e) {
+      throw new WorkspaceFileFunctionException(e);
+    } catch (EvalException e) {
+      throw new WorkspaceFileFunctionException(e);
+    }
+
+    ExternalPackage pkg = builder.build();
+    return new WorkspaceFileValue(pkg.getWorkspaceName(), pkg);
+  }
+
+  private void parseWorkspaceFile(Path workspaceFilePath, Builder builder)
+      throws WorkspaceFileFunctionException, InterruptedException {
     StoredEventHandler localReporter = new StoredEventHandler();
     BuildFileAST buildFileAST;
     ParserInputSource inputSource = null;
@@ -87,13 +114,9 @@
     if (buildFileAST.containsErrors()) {
       localReporter.handle(Event.error("WORKSPACE file could not be parsed"));
     } else {
-      try {
-        if (!evaluateWorkspaceFile(buildFileAST, holder, builder)) {
-          localReporter.handle(
-              Event.error("Error evaluating WORKSPACE file " + workspaceFilePath));
-        }
-      } catch (EvalException e) {
-        throw new WorkspaceFileFunctionException(e);
+      if (!evaluateWorkspaceFile(buildFileAST, builder)) {
+        localReporter.handle(
+            Event.error("Error evaluating WORKSPACE file " + workspaceFilePath));
       }
     }
 
@@ -101,7 +124,6 @@
     if (localReporter.hasErrors()) {
       builder.setContainsErrors();
     }
-    return new WorkspaceFileValue(holder.workspaceName, builder.build());
   }
 
   @Override
@@ -109,7 +131,7 @@
     return null;
   }
 
-  private static Function newWorkspaceNameFunction(final WorkspaceNameHolder holder) {
+  private static Function newWorkspaceNameFunction(final Builder builder) {
     List<String> params = ImmutableList.of("name");
     return new MixedModeFunction("workspace", params, 1, true) {
       @Override
@@ -120,13 +142,13 @@
         if (errorMessage != null) {
           throw new EvalException(ast.getLocation(), errorMessage);
         }
-        holder.workspaceName = name;
+        builder.setWorkspaceName(name);
         return NONE;
       }
     };
   }
 
-  private static Function newBindFunction(final ExternalPackageBuilder builder) {
+  private static Function newBindFunction(final Builder builder) {
     List<String> params = ImmutableList.of("name", "actual");
     return new MixedModeFunction(BIND, params, 2, true) {
       @Override
@@ -154,7 +176,7 @@
    * specified package context.
    */
   private static Function newRuleFunction(final RuleFactory ruleFactory,
-      final ExternalPackageBuilder builder, final String ruleClassName) {
+      final Builder builder, final String ruleClassName) {
     return new AbstractFunction(ruleClassName) {
       @Override
       public Object call(List<Object> args, Map<String, Object> kwargs, FuncallExpression ast,
@@ -176,9 +198,8 @@
     };
   }
 
-  public boolean evaluateWorkspaceFile(BuildFileAST buildFileAST, WorkspaceNameHolder holder,
-      ExternalPackageBuilder builder)
-          throws InterruptedException, EvalException, WorkspaceFileFunctionException {
+  public boolean evaluateWorkspaceFile(BuildFileAST buildFileAST, Builder builder)
+      throws InterruptedException {
     // Environment is defined in SkyFunction and the syntax package.
     com.google.devtools.build.lib.syntax.Environment workspaceEnv =
         new com.google.devtools.build.lib.syntax.Environment();
@@ -190,22 +211,10 @@
     }
 
     workspaceEnv.update(BIND, newBindFunction(builder));
-    workspaceEnv.update("workspace", newWorkspaceNameFunction(holder));
+    workspaceEnv.update("workspace", newWorkspaceNameFunction(builder));
 
     StoredEventHandler eventHandler = new StoredEventHandler();
-    if (!buildFileAST.exec(workspaceEnv, eventHandler)) {
-      return false;
-    }
-    try {
-      builder.resolveBindTargets(packageFactory.getRuleClass(BIND));
-    } catch (NoSuchBindingException e) {
-      throw new WorkspaceFileFunctionException(e);
-    }
-    return true;
-  }
-
-  private static final class WorkspaceNameHolder {
-    String workspaceName;
+    return buildFileAST.exec(workspaceEnv, eventHandler);
   }
 
   private static final class WorkspaceFileFunctionException extends SkyFunctionException {