Make Rule know about the name of the workspace it is in.

This is needed for taking the runfiles prefix from the WORKSPACE file instead of hardcoding it.

--
MOS_MIGRATED_REVID=87347883
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index 0af667a..1bbcd72 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -159,6 +159,13 @@
   }
 
   /**
+   * Returns the workspace name for the rule.
+   */
+  public String getWorkspaceName() {
+    return rule.getWorkspaceName();
+  }
+
+  /**
    * The configuration conditions that trigger this rule's configurable attributes.
    */
   Set<ConfigMatchingProvider> getConfigConditions() {
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 ac4920c..6f7b243 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,7 +34,6 @@
  */
 public class ExternalPackage extends Package {
 
-  private String workspaceName;
   private Map<RepositoryName, Rule> repositoryMap;
 
   ExternalPackage() {
@@ -42,13 +41,6 @@
   }
 
   /**
-   * 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.
    */
@@ -77,7 +69,7 @@
     }
 
     /**
-     * Checks if the label is bound, i.e., starts with //external:.
+     * Checks if the label is bound, i.e., starts with {@code //external:}.
      */
     public static boolean isBoundLabel(Label label) {
       return label.getPackageName().equals("external");
@@ -89,7 +81,6 @@
    */
   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();
 
@@ -106,7 +97,6 @@
 
     @Override
     public ExternalPackage build() {
-      pkg.workspaceName = workspaceName;
       pkg.repositoryMap = ImmutableMap.copyOf(repositoryMap);
       return super.build();
     }
@@ -114,8 +104,10 @@
     /**
      * Sets the name for this repository.
      */
-    public void setWorkspaceName(String name) {
-      workspaceName = name;
+    @Override
+    public Builder setWorkspaceName(String workspaceName) {
+      pkg.workspaceName = workspaceName;
+      return this;
     }
 
     public void addBinding(Label label, Binding binding) {
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Package.java b/src/main/java/com/google/devtools/build/lib/packages/Package.java
index a2216e0..fd48e5d 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Package.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Package.java
@@ -24,6 +24,7 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import com.google.devtools.build.lib.Constants;
 import com.google.devtools.build.lib.collect.CollectionUtils;
 import com.google.devtools.build.lib.collect.ImmutableSortedKeyMap;
 import com.google.devtools.build.lib.events.Event;
@@ -33,7 +34,6 @@
 import com.google.devtools.build.lib.packages.License.DistributionType;
 import com.google.devtools.build.lib.packages.PackageDeserializer.PackageDeserializationException;
 import com.google.devtools.build.lib.packages.PackageFactory.Globber;
-
 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;
@@ -103,6 +103,12 @@
   private Path packageDirectory;
 
   /**
+   * The name of the workspace this package is in. Used as a prefix for the runfiles directory.
+   * This can be set in the WORKSPACE file. This must be a valid target name.
+   */
+  protected String workspaceName = Constants.RUNFILES_PREFIX;
+
+  /**
    * The root of the source tree in which this package was found. It is an invariant that
    * {@code sourceRoot.getRelative(name).equals(packageDirectory)}.
    */
@@ -545,6 +551,16 @@
   }
 
   /**
+   * Returns this package's workspace name.
+   *
+   * <p>Package-private to encourage callers to get their workspace name from a rule, not a
+   * package.</p>
+   */
+  String getWorkspaceName() {
+    return workspaceName;
+  }
+
+  /**
    * Returns the features specified in the <code>package()</code> declaration.
    */
   public ImmutableSet<String> getFeatures() {
@@ -826,7 +842,7 @@
     protected Map<Label, EnvironmentGroup> environmentGroups = new HashMap<>();
 
     protected Map<Label, Path> subincludes = null;
-    protected ImmutableList<Label> skylarkFileDependencies = null;
+    protected ImmutableList<Label> skylarkFileDependencies = ImmutableList.of();
 
     /**
      * True iff the "package" function has already been called in this package.
@@ -942,6 +958,14 @@
     }
 
     /**
+     * Uses the workspace name from {@code //external} to set this package's workspace name.
+     */
+    B setWorkspaceName(String workspaceName) {
+      pkg.workspaceName = workspaceName;
+      return self();
+    }
+
+    /**
      * Returns whether the "package" function has been called yet
      */
     public boolean isPackageFunctionUsed() {
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 c8b6710..0cfdf0a 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
@@ -897,17 +897,18 @@
    * <p>Executes {@code globber.onCompletion()} on completion and executes
    * {@code globber.onInterrupt()} on an {@link InterruptedException}.
    */
-  private Package.LegacyBuilder createPackage(PackageIdentifier packageId, Path buildFile,
-      List<Statement> preludeStatements, ParserInputSource inputSource,
-      Map<PathFragment, SkylarkEnvironment> imports, ImmutableList<Label> skylarkFileDependencies,
-      CachingPackageLocator locator, RuleVisibility defaultVisibility, Globber globber)
+  private Package.LegacyBuilder createPackage(ExternalPackage externalPkg,
+      PackageIdentifier packageId, Path buildFile, List<Statement> preludeStatements,
+      ParserInputSource inputSource, Map<PathFragment, SkylarkEnvironment> imports,
+      ImmutableList<Label> skylarkFileDependencies, CachingPackageLocator locator,
+      RuleVisibility defaultVisibility, Globber globber)
           throws InterruptedException {
     StoredEventHandler localReporter = new StoredEventHandler();
     Preprocessor.Result preprocessingResult = preprocess(packageId, buildFile, inputSource, globber,
         localReporter);
-    return createPackageFromPreprocessingResult(packageId, buildFile, preprocessingResult,
-        localReporter.getEvents(), preludeStatements, imports, skylarkFileDependencies, locator,
-        defaultVisibility, globber);
+    return createPackageFromPreprocessingResult(externalPkg, packageId, buildFile,
+        preprocessingResult, localReporter.getEvents(), preludeStatements, imports,
+        skylarkFileDependencies, locator, defaultVisibility, globber);
   }
 
   /**
@@ -918,7 +919,8 @@
    * {@code globber.onInterrupt()} on an {@link InterruptedException}.
    */
   // Used outside of bazel!
-  public Package.LegacyBuilder createPackageFromPreprocessingResult(PackageIdentifier packageId,
+  public Package.LegacyBuilder createPackageFromPreprocessingResult(Package externalPkg,
+      PackageIdentifier packageId,
       Path buildFile,
       Preprocessor.Result preprocessingResult,
       Iterable<Event> preprocessingEvents,
@@ -947,7 +949,7 @@
       prefetchGlobs(packageId, buildFileAST, preprocessingResult.preprocessed,
           buildFile, globber, defaultVisibility, makeEnv);
       return evaluateBuildFile(
-          packageId, buildFileAST, buildFile, globber,
+          externalPkg, packageId, buildFileAST, buildFile, globber,
           Iterables.concat(preprocessingEvents, localReporter.getEvents()),
           defaultVisibility, preprocessingResult.containsErrors,
           preprocessingResult.containsTransientErrors, makeEnv, imports, skylarkFileDependencies);
@@ -965,9 +967,8 @@
    */
   @VisibleForTesting
   public Package createPackageForTesting(PackageIdentifier packageId,
-      Path buildFile,
-      CachingPackageLocator locator,
-      EventHandler eventHandler) throws NoSuchPackageException, InterruptedException {
+      Path buildFile, CachingPackageLocator locator, EventHandler eventHandler)
+          throws NoSuchPackageException, InterruptedException {
     String error = LabelValidator.validatePackageName(
         packageId.getPackageFragment().getPathString());
     if (error != null) {
@@ -978,11 +979,11 @@
     if (inputSource == null) {
       throw new BuildFileContainsErrorsException(packageId.toString(), "IOException occured");
     }
-    Package result = createPackage(packageId, buildFile,
-        ImmutableList.<Statement>of(), inputSource,
-        ImmutableMap.<PathFragment, SkylarkEnvironment>of(),
-        ImmutableList.<Label>of(),
-        locator, ConstantRuleVisibility.PUBLIC,
+
+    Package result = createPackage((new ExternalPackage.Builder(
+        buildFile.getRelative("WORKSPACE"))).build(), packageId, buildFile,
+        ImmutableList.<Statement>of(), inputSource, ImmutableMap.<PathFragment,
+        SkylarkEnvironment>of(), ImmutableList.<Label>of(), locator, ConstantRuleVisibility.PUBLIC,
         createLegacyGlobber(buildFile.getParentDirectory(), packageId, locator)).build();
     Event.replayEventsOn(eventHandler, result.getEvents());
     return result;
@@ -1135,8 +1136,8 @@
    * @see PackageFactory#PackageFactory
    */
   @VisibleForTesting // used by PackageFactoryApparatus
-  public Package.LegacyBuilder evaluateBuildFile(PackageIdentifier packageId,
-      BuildFileAST buildFileAST, Path buildFilePath, Globber globber,
+  public Package.LegacyBuilder evaluateBuildFile(Package externalPkg,
+      PackageIdentifier packageId, BuildFileAST buildFileAST, Path buildFilePath, Globber globber,
       Iterable<Event> pastEvents, RuleVisibility defaultVisibility, boolean containsError,
       boolean containsTransientError, MakeEnvironment.Builder pkgMakeEnv,
       Map<PathFragment, SkylarkEnvironment> imports,
@@ -1154,11 +1155,12 @@
         // "defaultVisibility" comes from the command line. Let's give the BUILD file a chance to
         // set default_visibility once, be reseting the PackageBuilder.defaultVisibilitySet flag.
         .setDefaultVisibilitySet(false)
-        .setSkylarkFileDependencies(skylarkFileDependencies);
+        .setSkylarkFileDependencies(skylarkFileDependencies)
+        .setWorkspaceName(externalPkg.getWorkspaceName());
 
     Event.replayEventsOn(eventHandler, pastEvents);
 
-    // Stuff that closes over the package context:
+    // Stuff that closes over the package context:`
     PackageContext context = new PackageContext(pkgBuilder, globber, eventHandler);
     buildPkgEnv(pkgEnv, packageId.toString(), pkgMakeEnv, context, ruleFactory);
 
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Rule.java b/src/main/java/com/google/devtools/build/lib/packages/Rule.java
index cff91f6..23ceb9f 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Rule.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Rule.java
@@ -147,6 +147,8 @@
 
   private final FuncallExpression ast; // may be null
 
+  private final String workspaceName;
+
   // Initialized in the call to populateOutputFiles.
   private List<OutputFile> outputFiles;
   private ListMultimap<String, OutputFile> outputFileMap;
@@ -160,6 +162,7 @@
     this.attributeMap = new RawAttributeMapper(pkg, ruleClass, label, attributes);
     this.containsErrors = false;
     this.ast = ast;
+    this.workspaceName = pkg.getWorkspaceName();
   }
 
   void setVisibility(RuleVisibility visibility) {
@@ -186,6 +189,13 @@
     this.containsErrors = true;
   }
 
+  /**
+   * Returns the name of the workspace that this rule is in.
+   */
+  public String getWorkspaceName() {
+    return workspaceName;
+  }
+
   @Override
   public Label getLabel() {
     return attributeMap.getLabel();
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 b318f3e..1679810 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
@@ -316,10 +316,10 @@
       throws PackageFunctionException {
     RootedPath workspacePath = RootedPath.toRootedPath(
         packageLookupPath, new PathFragment("WORKSPACE"));
-    SkyKey workspaceKey = WorkspaceFileValue.key(workspacePath);
-    WorkspaceFileValue workspace = null;
+    SkyKey workspaceKey = PackageValue.workspaceKey(workspacePath);
+    PackageValue workspace = null;
     try {
-      workspace = (WorkspaceFileValue) env.getValueOrThrow(workspaceKey, IOException.class,
+      workspace = (PackageValue) env.getValueOrThrow(workspaceKey, IOException.class,
           FileSymlinkCycleException.class, InconsistentFilesystemException.class,
           EvalException.class);
     } catch (IOException | FileSymlinkCycleException | InconsistentFilesystemException
@@ -385,6 +385,12 @@
     if (packageName.equals(EXTERNAL_PACKAGE_NAME)) {
       return getExternalPackage(env, packageLookupValue.getRoot());
     }
+    PackageValue externalPackage = (PackageValue) env.getValue(
+        PackageValue.key(PackageIdentifier.createInDefaultRepo(EXTERNAL_PACKAGE_NAME)));
+    if (externalPackage == null) {
+      return null;
+    }
+    Package externalPkg = externalPackage.getPackage();
 
     PathFragment buildFileFragment = packageNameFragment.getChild("BUILD");
     RootedPath buildFileRootedPath = RootedPath.toRootedPath(packageLookupValue.getRoot(),
@@ -462,8 +468,9 @@
       return null;
     }
 
-    Package.LegacyBuilder legacyPkgBuilder = loadPackage(inputSource, replacementContents,
-        packageId, buildFilePath, defaultVisibility, preludeStatements, importResult);
+    Package.LegacyBuilder legacyPkgBuilder = loadPackage(externalPkg, inputSource,
+        replacementContents, packageId, buildFilePath, defaultVisibility, preludeStatements,
+        importResult);
     legacyPkgBuilder.buildPartial();
     try {
       handleLabelsCrossingSubpackagesAndPropagateInconsistentFilesystemExceptions(
@@ -709,8 +716,8 @@
    * Constructs a {@link Package} object for the given package using legacy package loading.
    * Note that the returned package may be in error.
    */
-  private Package.LegacyBuilder loadPackage(ParserInputSource inputSource,
-      @Nullable String replacementContents,
+  private Package.LegacyBuilder loadPackage(Package externalPkg,
+      ParserInputSource inputSource, @Nullable String replacementContents,
       PackageIdentifier packageId, Path buildFilePath, RuleVisibility defaultVisibility,
       List<Statement> preludeStatements, SkylarkImportResult importResult)
           throws InterruptedException {
@@ -729,8 +736,8 @@
             ? packageFactory.preprocess(packageId, buildFilePath, inputSource, globber,
                 localReporter)
                 : Preprocessor.Result.noPreprocessing(replacementSource);
-        pkgBuilder = packageFactory.createPackageFromPreprocessingResult(packageId, buildFilePath,
-            preprocessingResult, localReporter.getEvents(), preludeStatements,
+        pkgBuilder = packageFactory.createPackageFromPreprocessingResult(externalPkg, packageId,
+            buildFilePath, preprocessingResult, localReporter.getEvents(), preludeStatements,
             importResult.importMap, importResult.fileDependencies, packageLocator,
             defaultVisibility, globber);
         if (eventBus.get() != null) {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PackageValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/PackageValue.java
index 65fd2af..d3292fb 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/PackageValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PackageValue.java
@@ -19,6 +19,7 @@
 import com.google.devtools.build.lib.packages.Package;
 import com.google.devtools.build.lib.packages.PackageIdentifier;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.RootedPath;
 import com.google.devtools.build.skyframe.SkyKey;
 import com.google.devtools.build.skyframe.SkyValue;
 
@@ -52,4 +53,11 @@
   public static SkyKey key(PackageIdentifier pkgIdentifier) {
     return new SkyKey(SkyFunctions.PACKAGE, pkgIdentifier);
   }
+
+  /**
+   * Returns a SkyKey to find the WORKSPACE file at the given path.
+   */
+  public static SkyKey workspaceKey(RootedPath workspacePath) {
+    return new SkyKey(SkyFunctions.WORKSPACE_FILE, workspacePath);
+  }
 }
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 89126cb..a5bbbef 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
@@ -21,7 +21,6 @@
 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.Builder;
 import com.google.devtools.build.lib.packages.ExternalPackage.Builder.NoSuchBindingException;
@@ -95,8 +94,7 @@
       throw new WorkspaceFileFunctionException(e);
     }
 
-    ExternalPackage pkg = builder.build();
-    return new WorkspaceFileValue(pkg.getWorkspaceName(), pkg);
+    return new PackageValue(builder.build());
   }
 
   private void parseWorkspaceFile(Path workspaceFilePath, Builder builder)
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/WorkspaceFileValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/WorkspaceFileValue.java
deleted file mode 100644
index 200f23f..0000000
--- a/src/main/java/com/google/devtools/build/lib/skyframe/WorkspaceFileValue.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2014 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.devtools.build.lib.skyframe;
-
-import com.google.devtools.build.lib.packages.ExternalPackage;
-import com.google.devtools.build.lib.vfs.RootedPath;
-import com.google.devtools.build.skyframe.SkyKey;
-import com.google.devtools.build.skyframe.SkyValue;
-
-import javax.annotation.Nullable;
-
-/**
- * Holds the contents of a WORKSPACE file as the //external package.
- */
-public class WorkspaceFileValue implements SkyValue {
-
-  private final String workspace;
-  private final ExternalPackage pkg;
-
-  public WorkspaceFileValue(String workspace, ExternalPackage pkg) {
-    this.workspace = workspace;
-    this.pkg = pkg;
-  }
-
-  /**
-   * Returns the name of this workspace (or null for the default workspace).
-   */
-  @Nullable
-  public String getWorkspace() {
-    return workspace;
-  }
-
-  /**
-   * Returns the //external package.
-   */
-  public ExternalPackage getPackage() {
-    return pkg;
-  }
-
-  /**
-   * Generates a SkyKey based on the path to the WORKSPACE file.
-   */
-  public static SkyKey key(RootedPath workspacePath) {
-    return new SkyKey(SkyFunctions.WORKSPACE_FILE, workspacePath);
-  }
-
-}