Thread a RootedPath into the Package's BUILD file, versus just a Path. This allows us to use that Root in the common case that the Package is not external, saving the creation of a new Root.

This has a miniscule memory impact, but the more important impact is that we can enumerate all possible Roots now, so Roots can be serialized as constants.

PiperOrigin-RevId: 215261674
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 4c65f02..e91dce9 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
@@ -42,6 +42,7 @@
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.Root;
+import com.google.devtools.build.lib.vfs.RootedPath;
 import com.google.protobuf.CodedInputStream;
 import com.google.protobuf.CodedOutputStream;
 import java.io.IOException;
@@ -94,10 +95,8 @@
    */
   private final PathFragment nameFragment;
 
-  /**
-   * The filename of this package's BUILD file.
-   */
-  private Path filename;
+  /** The filename of this package's BUILD file. */
+  private RootedPath filename;
 
   /**
    * The directory in which this package's BUILD file resides.  All InputFile
@@ -310,17 +309,22 @@
     return sourceRoot;
   }
 
-  // This must always be consistent with Root.computeSourceRoot; otherwise computing source roots
-  // from exec paths does not work, which can break the action cache for input-discovering actions.
-  private static Root getSourceRoot(Path buildFile, PathFragment packageFragment) {
-    Path current = buildFile.getParentDirectory();
-    for (int i = 0, len = packageFragment.segmentCount();
-         i < len && !packageFragment.equals(PathFragment.EMPTY_FRAGMENT); i++) {
-      if (current != null) {
-        current = current.getParentDirectory();
-      }
+  private static Root getSourceRoot(RootedPath buildFileRootedPath, PathFragment packageFragment) {
+    PathFragment packageDirectory = buildFileRootedPath.getRootRelativePath().getParentDirectory();
+    if (packageFragment.equals(packageDirectory)) {
+      // Fast path: BUILD file path and package name are the same, don't create an extra root.
+      return buildFileRootedPath.getRoot();
     }
-    return Root.fromPath(current);
+    PathFragment current = buildFileRootedPath.asPath().asFragment().getParentDirectory();
+    for (int i = 0, len = packageFragment.segmentCount(); i < len && current != null; i++) {
+      current = current.getParentDirectory();
+    }
+    if (current == null || current.isEmpty()) {
+      // This is never really expected to work. The check below in #finishInit should fail.
+      return buildFileRootedPath.getRoot();
+    }
+    // Note that current is an absolute path.
+    return Root.fromPath(buildFileRootedPath.getRoot().getRelative(current));
   }
 
   /**
@@ -342,14 +346,24 @@
       }
     }
     this.filename = builder.getFilename();
-    this.packageDirectory = filename.getParentDirectory();
+    this.packageDirectory = filename.asPath().getParentDirectory();
 
     this.sourceRoot = getSourceRoot(filename, packageIdentifier.getSourceRoot());
     if ((sourceRoot.asPath() == null
             || !sourceRoot.getRelative(packageIdentifier.getSourceRoot()).equals(packageDirectory))
-        && !filename.getBaseName().equals("WORKSPACE")) {
+        && !filename.getRootRelativePath().getBaseName().equals("WORKSPACE")) {
       throw new IllegalArgumentException(
-          "Invalid BUILD file name for package '" + packageIdentifier + "': " + filename);
+          "Invalid BUILD file name for package '"
+              + packageIdentifier
+              + "': "
+              + filename
+              + " (in source "
+              + sourceRoot
+              + " with packageDirectory "
+              + packageDirectory
+              + " and package identifier source root "
+              + packageIdentifier.getSourceRoot()
+              + ")");
     }
 
     this.makeEnv = ImmutableMap.copyOf(builder.makeEnv);
@@ -395,10 +409,10 @@
   }
 
   /**
-   * Returns the filename of the BUILD file which defines this package. The
-   * parent directory of the BUILD file is the package directory.
+   * Returns the filename of the BUILD file which defines this package. The parent directory of the
+   * BUILD file is the package directory.
    */
-  public Path getFilename() {
+  public RootedPath getFilename() {
     return filename;
   }
 
@@ -577,12 +591,10 @@
     } catch (LabelSyntaxException e) {
       throw new IllegalArgumentException(targetName);
     }
-    String msg = String.format(
-        "target '%s' not declared in package '%s'%s defined by %s",
-        targetName,
-        name,
-        suffix,
-        filename);
+    String msg =
+        String.format(
+            "target '%s' not declared in package '%s'%s defined by %s",
+            targetName, name, suffix, filename.asPath().getPathString());
     return new NoSuchTargetException(label, msg);
   }
 
@@ -683,7 +695,7 @@
    * output.
    */
   public void dump(PrintStream out) {
-    out.println("  Package " + getName() + " (" + getFilename() + ")");
+    out.println("  Package " + getName() + " (" + getFilename().asPath() + ")");
 
     // Rules:
     out.println("    Rules");
@@ -709,8 +721,8 @@
     }
   }
 
-  public static Builder newExternalPackageBuilder(Builder.Helper helper, Path workspacePath,
-      String runfilesPrefix) {
+  public static Builder newExternalPackageBuilder(
+      Builder.Helper helper, RootedPath workspacePath, String runfilesPrefix) {
     Builder b = new Builder(helper.createFreshPackage(
         Label.EXTERNAL_PACKAGE_IDENTIFIER, runfilesPrefix));
     b.setFilename(workspacePath);
@@ -778,7 +790,7 @@
     // It contains an entry from "@<main workspace name>" to "@" for packages within
     // the main workspace.
     private ImmutableMap<RepositoryName, RepositoryName> repositoryMapping = ImmutableMap.of();
-    private Path filename = null;
+    private RootedPath filename = null;
     private Label buildFileLabel = null;
     private InputFile buildFile = null;
     // TreeMap so that the iteration order of variables is predictable. This is useful so that the
@@ -903,14 +915,12 @@
       return this.repositoryMapping;
     }
 
-    /**
-     * Sets the name of this package's BUILD file.
-     */
-    Builder setFilename(Path filename) {
+    /** Sets the name of this package's BUILD file. */
+    Builder setFilename(RootedPath filename) {
       this.filename = filename;
       try {
-        buildFileLabel = createLabel(filename.getBaseName());
-        addInputFile(buildFileLabel, Location.fromFile(filename));
+        buildFileLabel = createLabel(filename.getRootRelativePath().getBaseName());
+        addInputFile(buildFileLabel, Location.fromPathFragment(filename.getRootRelativePath()));
       } catch (LabelSyntaxException e) {
         // This can't actually happen.
         throw new AssertionError("Package BUILD file has an illegal name: " + filename);
@@ -922,7 +932,7 @@
       return buildFileLabel;
     }
 
-    Path getFilename() {
+    RootedPath getFilename() {
       return filename;
     }
 
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 4da0e13..f23fba5 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
@@ -63,6 +63,7 @@
 import com.google.devtools.build.lib.syntax.Type.ConversionException;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.RootedPath;
 import com.google.devtools.build.lib.vfs.UnixGlob;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -1211,10 +1212,6 @@
   }
 
   /**
-   * Package creation.
-   */
-
-  /**
    * Loads, scans parses and evaluates the build file at "buildFile", and creates and returns a
    * Package builder instance capable of building a package identified by "packageId".
    *
@@ -1231,7 +1228,7 @@
   private Package.Builder createPackage(
       String workspaceName,
       PackageIdentifier packageId,
-      Path buildFile,
+      RootedPath buildFile,
       ParserInputSource input,
       List<Statement> preludeStatements,
       Map<String, Extension> imports,
@@ -1283,7 +1280,7 @@
       String workspaceName,
       ImmutableMap<RepositoryName, RepositoryName> repositoryMapping,
       PackageIdentifier packageId,
-      Path buildFile,
+      RootedPath buildFile,
       AstParseResult astParseResult,
       Map<String, Extension> imports,
       ImmutableList<Label> skylarkFileDependencies,
@@ -1316,7 +1313,8 @@
   }
 
   @VisibleForTesting
-  public Package.Builder newExternalPackageBuilder(Path workspacePath, String runfilesPrefix) {
+  public Package.Builder newExternalPackageBuilder(
+      RootedPath workspacePath, String runfilesPrefix) {
     return Package.newExternalPackageBuilder(packageBuilderHelper, workspacePath, runfilesPrefix);
   }
 
@@ -1328,12 +1326,17 @@
   @VisibleForTesting
   public Package createPackageForTesting(
       PackageIdentifier packageId,
-      Path buildFile,
+      RootedPath buildFile,
       CachingPackageLocator locator,
       ExtendedEventHandler eventHandler)
       throws NoSuchPackageException, InterruptedException {
-    Package externalPkg = newExternalPackageBuilder(
-        buildFile.getRelative(Label.WORKSPACE_FILE_NAME), "TESTING").build();
+    Package externalPkg =
+        newExternalPackageBuilder(
+                RootedPath.toRootedPath(
+                    buildFile.getRoot(),
+                    buildFile.getRootRelativePath().getRelative(Label.WORKSPACE_FILE_NAME)),
+                "TESTING")
+            .build();
     return createPackageForTesting(
         packageId,
         externalPkg,
@@ -1351,7 +1354,7 @@
   public Package createPackageForTesting(
       PackageIdentifier packageId,
       Package externalPkg,
-      Path buildFile,
+      RootedPath buildFile,
       CachingPackageLocator locator,
       ExtendedEventHandler eventHandler,
       SkylarkSemantics semantics)
@@ -1362,15 +1365,16 @@
       throw new BuildFileNotFoundException(
           packageId, "illegal package name: '" + packageId + "' (" + error + ")");
     }
-    byte[] buildFileBytes = maybeGetBuildFileBytes(buildFile, eventHandler);
+    byte[] buildFileBytes = maybeGetBuildFileBytes(buildFile.asPath(), eventHandler);
     if (buildFileBytes == null) {
       throw new BuildFileContainsErrorsException(packageId, "IOException occurred");
     }
 
-    Globber globber = createLegacyGlobber(buildFile.getParentDirectory(), packageId, locator);
+    Globber globber =
+        createLegacyGlobber(buildFile.asPath().getParentDirectory(), packageId, locator);
     ParserInputSource input =
         ParserInputSource.create(
-            FileSystemUtils.convertFromLatin1(buildFileBytes), buildFile.asFragment());
+            FileSystemUtils.convertFromLatin1(buildFileBytes), buildFile.asPath().asFragment());
 
     Package result =
         createPackage(
@@ -1601,7 +1605,7 @@
       String workspaceName,
       PackageIdentifier packageId,
       BuildFileAST buildFileAST,
-      Path buildFilePath,
+      RootedPath buildFilePath,
       Globber globber,
       Iterable<Event> pastEvents,
       Iterable<Postable> pastPosts,
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 d72c676..e19c7d9 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
@@ -436,7 +436,6 @@
 
     RootedPath buildFileRootedPath = packageLookupValue.getRootedPath(packageId);
     FileValue buildFileValue = null;
-    Path buildFilePath = buildFileRootedPath.asPath();
     String replacementContents = null;
 
     if (isDefaultsPackage(packageId) && PrecomputedValue.isInMemoryToolsDefaults(env)) {
@@ -492,7 +491,7 @@
             repositoryMapping,
             replacementContents,
             packageId,
-            buildFilePath,
+            buildFileRootedPath,
             buildFileValue,
             defaultVisibility,
             skylarkSemantics,
@@ -589,12 +588,12 @@
   }
 
   /**
-   * Fetch the skylark loads for this BUILD file. If any of them haven't been computed yet,
-   * returns null.
+   * Fetch the skylark loads for this BUILD file. If any of them haven't been computed yet, returns
+   * null.
    */
   @Nullable
   static SkylarkImportResult fetchImportsFromBuildFile(
-      Path buildFilePath,
+      PathFragment buildFilePath,
       PackageIdentifier packageId,
       BuildFileAST buildFileAST,
       Environment env,
@@ -1165,8 +1164,8 @@
   }
 
   /**
-   * Constructs a {@link Package} object for the given package. Note that the returned package
-   * may be in error.
+   * Constructs a {@link Package} object for the given package. Note that the returned package may
+   * be in error.
    *
    * <p>May return null if the computation has to be restarted.
    *
@@ -1180,7 +1179,7 @@
       ImmutableMap<RepositoryName, RepositoryName> repositoryMapping,
       @Nullable String replacementContents,
       PackageIdentifier packageId,
-      Path buildFilePath,
+      RootedPath buildFilePath,
       @Nullable FileValue buildFileValue,
       RuleVisibility defaultVisibility,
       SkylarkSemantics skylarkSemantics,
@@ -1196,6 +1195,7 @@
       try (SilentCloseable c =
           Profiler.instance().profile(ProfilerTask.CREATE_PACKAGE, packageId.toString())) {
         AstParseResult astParseResult = astCache.getIfPresent(packageId);
+        Path inputFile = buildFilePath.asPath();
         if (astParseResult == null) {
           if (showLoadingProgress.get()) {
             env.getListener().handle(Event.progress("Loading package: " + packageId));
@@ -1207,12 +1207,12 @@
             try {
               buildFileBytes =
                   buildFileValue.isSpecialFile()
-                      ? FileSystemUtils.readContent(buildFilePath)
-                      : FileSystemUtils.readWithKnownFileSize(
-                          buildFilePath, buildFileValue.getSize());
+                      ? FileSystemUtils.readContent(inputFile)
+                      : FileSystemUtils.readWithKnownFileSize(inputFile, buildFileValue.getSize());
             } catch (IOException e) {
-              buildFileBytes = actionOnIOExceptionReadingBuildFile.maybeGetBuildFileContentsToUse(
-                  buildFilePath.asFragment(), e);
+              buildFileBytes =
+                  actionOnIOExceptionReadingBuildFile.maybeGetBuildFileContentsToUse(
+                      inputFile.asFragment(), e);
               if (buildFileBytes == null) {
                 // Note that we did the work that led to this IOException, so we should
                 // conservatively report this error as transient.
@@ -1224,10 +1224,10 @@
             }
             input =
                 ParserInputSource.create(
-                    FileSystemUtils.convertFromLatin1(buildFileBytes),
-                    buildFilePath.asFragment());
+                    FileSystemUtils.convertFromLatin1(buildFileBytes), inputFile.asFragment());
           } else {
-            input = ParserInputSource.create(replacementContents, buildFilePath.asFragment());
+            input =
+                ParserInputSource.create(replacementContents, buildFilePath.asPath().asFragment());
           }
           StoredEventHandler astParsingEventHandler = new StoredEventHandler();
           BuildFileAST ast =
@@ -1240,7 +1240,7 @@
         try {
           importResult =
               fetchImportsFromBuildFile(
-                  buildFilePath,
+                  buildFilePath.getRootRelativePath(),
                   packageId,
                   astParseResult.ast,
                   env,
@@ -1256,7 +1256,7 @@
         }
         astCache.invalidate(packageId);
         GlobberWithSkyframeGlobDeps globberWithSkyframeGlobDeps =
-            makeGlobber(buildFilePath, packageId, packageRoot, env);
+            makeGlobber(inputFile, packageId, packageRoot, env);
         long startTimeNanos = BlazeClock.nanoTime();
         Package.Builder pkgBuilder =
             packageFactory.createPackageFromAst(
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 778ced3..b1691a7 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
@@ -28,7 +28,6 @@
 import com.google.devtools.build.lib.syntax.Environment.Extension;
 import com.google.devtools.build.lib.syntax.Mutability;
 import com.google.devtools.build.lib.syntax.SkylarkSemantics;
-import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.RootedPath;
 import com.google.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyFunctionException;
@@ -75,7 +74,8 @@
       return null;
     }
 
-    Path repoWorkspace = workspaceRoot.getRoot().getRelative(workspaceRoot.getRootRelativePath());
+    RootedPath repoWorkspace =
+        RootedPath.toRootedPath(workspaceRoot.getRoot(), workspaceRoot.getRootRelativePath());
     Package.Builder builder = packageFactory.newExternalPackageBuilder(
         repoWorkspace, ruleClassProvider.getRunfilesPrefix());
 
@@ -120,8 +120,9 @@
         parser.setParent(prevValue.getPackage(), prevValue.getImportMap(), prevValue.getBindings());
       }
       BuildFileAST ast = workspaceASTValue.getASTs().get(key.getIndex());
-      PackageFunction.SkylarkImportResult importResult = PackageFunction.fetchImportsFromBuildFile(
-          repoWorkspace, rootPackage, ast, env, null);
+      PackageFunction.SkylarkImportResult importResult =
+          PackageFunction.fetchImportsFromBuildFile(
+              repoWorkspace.asPath().asFragment(), rootPackage, ast, env, null);
       if (importResult == null) {
         return null;
       }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoader.java b/src/main/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoader.java
index 41ad785..8a9cbec 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoader.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoader.java
@@ -146,7 +146,7 @@
     int legacyGlobbingThreads = 1;
     int skyframeThreads = 1;
 
-    protected Builder(Path workspaceDir, Path installBase, Path outputBase) {
+    protected Builder(Root root, Path workspaceDir, Path installBase, Path outputBase) {
       this.workspaceDir = workspaceDir;
       Path devNull = workspaceDir.getFileSystem().getPath("/dev/null");
       directories =
@@ -159,7 +159,7 @@
       this.pkgLocator =
           new PathPackageLocator(
               directories.getOutputBase(),
-              ImmutableList.of(Root.fromPath(workspaceDir)),
+              ImmutableList.of(root),
               BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY);
       this.pkgLocatorRef = new AtomicReference<>(pkgLocator);
       this.externalFilesHelper =
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoader.java b/src/main/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoader.java
index a0d600a..0246c6d 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoader.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoader.java
@@ -42,6 +42,7 @@
 import com.google.devtools.build.lib.skyframe.PrecomputedValue;
 import com.google.devtools.build.lib.skyframe.SkyFunctions;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.Root;
 import com.google.devtools.build.lib.vfs.RootedPath;
 import com.google.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyFunctionName;
@@ -54,12 +55,12 @@
  */
 public class BazelPackageLoader extends AbstractPackageLoader {
   /** Returns a fresh {@link Builder} instance. */
-  public static Builder builder(Path workspaceDir, Path installBase, Path outputBase) {
+  public static Builder builder(Root root, Path workspaceDir, Path installBase, Path outputBase) {
     // Prevent PackageLoader from fetching any remote repositories; these should only be fetched by
     // Bazel before calling PackageLoader.
     AtomicBoolean isFetch = new AtomicBoolean(false);
 
-    Builder builder = new Builder(workspaceDir, installBase, outputBase, isFetch);
+    Builder builder = new Builder(root, workspaceDir, installBase, outputBase, isFetch);
 
     RepositoryCache repositoryCache = new RepositoryCache();
     HttpDownloader httpDownloader = new HttpDownloader(repositoryCache);
@@ -121,8 +122,9 @@
       return classProvider.build();
     }
 
-    private Builder(Path workspaceDir, Path installBase, Path outputBase, AtomicBoolean isFetch) {
-      super(workspaceDir, installBase, outputBase);
+    private Builder(
+        Root root, Path workspaceDir, Path installBase, Path outputBase, AtomicBoolean isFetch) {
+      super(root, workspaceDir, installBase, outputBase);
       this.isFetch = isFetch;
     }
 
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
index bbdef89..5cdcfb2 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
@@ -283,7 +283,7 @@
     skyframeExecutor.preparePackageLoading(
         new PathPackageLocator(
             outputBase,
-            ImmutableList.of(Root.fromPath(rootDirectory)),
+            ImmutableList.of(root),
             BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY),
         packageCacheOptions,
         skylarkSemanticsOptions,
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContextTest.java b/src/test/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContextTest.java
index fc3aa0d..37ed659 100644
--- a/src/test/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContextTest.java
+++ b/src/test/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContextTest.java
@@ -37,6 +37,8 @@
 import com.google.devtools.build.lib.syntax.Type;
 import com.google.devtools.build.lib.testutil.Scratch;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.Root;
+import com.google.devtools.build.lib.vfs.RootedPath;
 import com.google.devtools.build.skyframe.SkyFunction;
 import java.io.IOException;
 import java.io.InputStreamReader;
@@ -57,6 +59,7 @@
 
   private Scratch scratch;
   private Path outputDirectory;
+  private Root root;
   private Path workspaceFile;
   private SkylarkRepositoryContext context;
 
@@ -64,6 +67,7 @@
   public void setUp() throws Exception {
     scratch = new Scratch("/");
     outputDirectory = scratch.dir("/outputDir");
+    root = Root.fromPath(scratch.dir("/"));
     workspaceFile = scratch.file("/WORKSPACE");
   }
 
@@ -80,8 +84,11 @@
 
   protected void setUpContextForRule(Map<String, Object> kwargs, Attribute... attributes)
       throws Exception {
-    Package.Builder packageBuilder = Package.newExternalPackageBuilder(
-        Package.Builder.DefaultHelper.INSTANCE, workspaceFile, "runfiles");
+    Package.Builder packageBuilder =
+        Package.newExternalPackageBuilder(
+            Package.Builder.DefaultHelper.INSTANCE,
+            RootedPath.toRootedPath(root, workspaceFile),
+            "runfiles");
     FuncallExpression ast =
         new FuncallExpression(Identifier.of("test"), ImmutableList.<Passed>of());
     ast.setLocation(Location.BUILTIN);
diff --git a/src/test/java/com/google/devtools/build/lib/packages/EnvironmentGroupTest.java b/src/test/java/com/google/devtools/build/lib/packages/EnvironmentGroupTest.java
index b2e4aec..947723c 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/EnvironmentGroupTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/EnvironmentGroupTest.java
@@ -21,6 +21,7 @@
 import com.google.devtools.build.lib.cmdline.PackageIdentifier;
 import com.google.devtools.build.lib.packages.util.PackageLoadingTestCase;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.RootedPath;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -52,7 +53,10 @@
             ")");
     pkg =
         packageFactory.createPackageForTesting(
-            PackageIdentifier.createInMainRepo("pkg"), buildfile, getPackageManager(), reporter);
+            PackageIdentifier.createInMainRepo("pkg"),
+            RootedPath.toRootedPath(root, buildfile),
+            getPackageManager(),
+            reporter);
 
     group = (EnvironmentGroup) pkg.getTarget("group");
   }
@@ -106,8 +110,12 @@
         "a/BUILD",
         "environment_group(name = 'empty_group', environments = [], defaults = [])");
     reporter.removeHandler(failFastHandler);
-    Package emptyGroupPkg = packageFactory.createPackageForTesting(
-        PackageIdentifier.createInMainRepo("a"), buildfile, getPackageManager(), reporter);
+    Package emptyGroupPkg =
+        packageFactory.createPackageForTesting(
+            PackageIdentifier.createInMainRepo("a"),
+            RootedPath.toRootedPath(root, buildfile),
+            getPackageManager(),
+            reporter);
     assertThat(emptyGroupPkg.containsErrors()).isTrue();
     assertContainsEvent(
         "environment group empty_group must contain at least one environment");
diff --git a/src/test/java/com/google/devtools/build/lib/packages/ExportsFilesTest.java b/src/test/java/com/google/devtools/build/lib/packages/ExportsFilesTest.java
index a968d4a..b61ca1f 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/ExportsFilesTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/ExportsFilesTest.java
@@ -21,9 +21,12 @@
 import com.google.devtools.build.lib.packages.util.PackageFactoryApparatus;
 import com.google.devtools.build.lib.testutil.Scratch;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.Root;
+import com.google.devtools.build.lib.vfs.RootedPath;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -35,11 +38,17 @@
   private Scratch scratch = new Scratch("/workspace");
   private EventCollectionApparatus events = new EventCollectionApparatus();
   private PackageFactoryApparatus packages = new PackageFactoryApparatus(events.reporter());
+  private Root root;
+
+  @Before
+  public void setUp() throws Exception {
+    root = Root.fromPath(scratch.dir(""));
+  }
 
   private Package pkg() throws Exception {
     Path buildFile = scratch.file("pkg/BUILD",
                                   "exports_files(['foo.txt', 'bar.txt'])");
-    return packages.createPackage("pkg", buildFile);
+    return packages.createPackage("pkg", RootedPath.toRootedPath(root, buildFile));
   }
 
   @Test
@@ -88,7 +97,7 @@
         "exports_files(['foo'])",
         "genrule(name = 'foo', srcs = ['bar'], outs = [],",
         "        cmd = '/bin/true')");
-    Package pkg = packages.createPackage("pkg2", buildFile);
+    Package pkg = packages.createPackage("pkg2", RootedPath.toRootedPath(root, buildFile));
     events.assertContainsError("rule 'foo' in package 'pkg2' conflicts with "
                                + "existing source file");
     assertThat(pkg.getTarget("foo") instanceof InputFile).isTrue();
diff --git a/src/test/java/com/google/devtools/build/lib/packages/InputFileTest.java b/src/test/java/com/google/devtools/build/lib/packages/InputFileTest.java
index a7d7839..08cee58 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/InputFileTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/InputFileTest.java
@@ -21,6 +21,8 @@
 import com.google.devtools.build.lib.packages.util.PackageFactoryApparatus;
 import com.google.devtools.build.lib.testutil.Scratch;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.Root;
+import com.google.devtools.build.lib.vfs.RootedPath;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -39,6 +41,12 @@
   private EventCollectionApparatus events = new EventCollectionApparatus();
   private Scratch scratch = new Scratch("/workspace");
   private PackageFactoryApparatus packages = new PackageFactoryApparatus(events.reporter());
+  private Root root;
+
+  @Before
+  public void setUp() throws Exception {
+    root = Root.fromPath(scratch.dir(""));
+  }
 
   @Before
   public final void writeFiles() throws Exception  {
@@ -49,7 +57,7 @@
             "        cmd = '', ",
             "        outs = [], ",
             "        srcs = ['x', 'subdir/y'])");
-    pkg = packages.createPackage("pkg", buildfile);
+    pkg = packages.createPackage("pkg", RootedPath.toRootedPath(root, buildfile));
     events.assertNoWarningsOrErrors();
 
     this.pathX = scratch.file("pkg/x", "blah");
diff --git a/src/test/java/com/google/devtools/build/lib/packages/OutputFileTest.java b/src/test/java/com/google/devtools/build/lib/packages/OutputFileTest.java
index c422f77..e1fddad 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/OutputFileTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/OutputFileTest.java
@@ -19,6 +19,7 @@
 import com.google.devtools.build.lib.cmdline.PackageIdentifier;
 import com.google.devtools.build.lib.packages.util.PackageLoadingTestCase;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.RootedPath;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -40,7 +41,10 @@
             "        outs=['x', 'subdir/y'])");
     this.pkg =
         packageFactory.createPackageForTesting(
-            PackageIdentifier.createInMainRepo("pkg"), buildfile, getPackageManager(), reporter);
+            PackageIdentifier.createInMainRepo("pkg"),
+            RootedPath.toRootedPath(root, buildfile),
+            getPackageManager(),
+            reporter);
     assertNoEvents();
 
     this.rule = (Rule) pkg.getTarget("foo");
@@ -110,7 +114,7 @@
     reporter.removeHandler(failFastHandler);
     packageFactory.createPackageForTesting(
         PackageIdentifier.createInMainRepo("two_outs"),
-        buildfile,
+        RootedPath.toRootedPath(root, buildfile),
         getPackageManager(),
         reporter);
     assertContainsEvent(
@@ -134,7 +138,7 @@
     reporter.removeHandler(failFastHandler);
     packageFactory.createPackageForTesting(
         PackageIdentifier.createInMainRepo("out_is_rule"),
-        buildfile,
+        RootedPath.toRootedPath(root, buildfile),
         getPackageManager(),
         reporter);
     assertContainsEvent("generated file 'a' in rule 'b' conflicts with existing genrule rule");
@@ -152,7 +156,7 @@
     reporter.removeHandler(failFastHandler);
     packageFactory.createPackageForTesting(
         PackageIdentifier.createInMainRepo("two_outs"),
-        buildfile,
+        RootedPath.toRootedPath(root, buildfile),
         getPackageManager(),
         reporter);
     assertContainsEvent(
@@ -172,7 +176,7 @@
     reporter.removeHandler(failFastHandler);
     packageFactory.createPackageForTesting(
         PackageIdentifier.createInMainRepo("bad_out_name"),
-        buildfile,
+        RootedPath.toRootedPath(root, buildfile),
         getPackageManager(),
         reporter);
     assertContainsEvent("illegal output file name '!@#:' in rule //bad_out_name:a");
@@ -190,7 +194,7 @@
     reporter.removeHandler(failFastHandler);
     packageFactory.createPackageForTesting(
         PackageIdentifier.createInMainRepo("cross_package_out"),
-        buildfile,
+        RootedPath.toRootedPath(root, buildfile),
         getPackageManager(),
         reporter);
     assertContainsEvent("label '//foo:bar' is not in the current package");
@@ -207,8 +211,10 @@
 
     reporter.removeHandler(failFastHandler);
     packageFactory.createPackageForTesting(
-        PackageIdentifier.createInMainRepo("output_called_build"), buildfile,
-        getPackageManager(), reporter);
+        PackageIdentifier.createInMainRepo("output_called_build"),
+        RootedPath.toRootedPath(root, buildfile),
+        getPackageManager(),
+        reporter);
     assertContainsEvent("generated file 'BUILD' in rule 'a' conflicts with existing source file");
   }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java b/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java
index 44b0f58..4da0436 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java
@@ -32,6 +32,7 @@
 import com.google.devtools.build.lib.testutil.TestUtils;
 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 java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -55,7 +56,7 @@
   @Test
   public void testCreatePackage() throws Exception {
     Path buildFile = scratch.file("/pkgname/BUILD", "# empty build file ");
-    Package pkg = packages.createPackage("pkgname", buildFile);
+    Package pkg = packages.createPackage("pkgname", RootedPath.toRootedPath(root, buildFile));
     assertThat(pkg.getName()).isEqualTo("pkgname");
     assertThat(Sets.newHashSet(pkg.getTargets(Rule.class))).isEmpty();
   }
@@ -90,7 +91,7 @@
     events.setFailFast(false);
 
     Path buildFile = scratch.file("/badrulename/BUILD", "cc_library(name = 3)");
-    Package pkg = packages.createPackage("badrulename", buildFile);
+    Package pkg = packages.createPackage("badrulename", RootedPath.toRootedPath(root, buildFile));
 
     events.assertContainsError("cc_library 'name' attribute must be a string");
     assertThat(pkg.containsErrors()).isTrue();
@@ -101,7 +102,7 @@
     events.setFailFast(false);
 
     Path buildFile = scratch.file("/badrulename/BUILD", "cc_library()");
-    Package pkg = packages.createPackage("badrulename", buildFile);
+    Package pkg = packages.createPackage("badrulename", RootedPath.toRootedPath(root, buildFile));
 
     events.assertContainsError("cc_library rule has no 'name' attribute");
     assertThat(pkg.containsErrors()).isTrue();
@@ -112,8 +113,9 @@
     try {
       // PathFragment parsing de-double slashes, and normalization of the path fragment removes
       // up reference (/../), so use triple dot /.../ that will always be a forbidden package name.
-      packages.createPackage("not even a legal/.../label",
-          emptyBuildFile("not even a legal/.../label"));
+      packages.createPackage(
+          "not even a legal/.../label",
+          RootedPath.toRootedPath(root, emptyBuildFile("not even a legal/.../label")));
       fail();
     } catch (NoSuchPackageException e) {
       assertThat(e)
@@ -131,7 +133,7 @@
         scratch.file(
             "/googledata/cafe/BUILD",
             "exports_files(['houseads/house_ads:ca-aol_parenting_html'])");
-    Package pkg = packages.createPackage("googledata/cafe", path);
+    Package pkg = packages.createPackage("googledata/cafe", RootedPath.toRootedPath(root, path));
     events.assertContainsError("target names may not contain ':'");
     assertThat(pkg.getTargets(FileTarget.class).toString())
         .doesNotContain("houseads/house_ads:ca-aol_parenting_html");
@@ -156,7 +158,8 @@
             "# -*- python -*-",
             "proto_library(name = 'spell_proto', srcs = ['spell.proto'], cc_api_version = 2)",
             "cc_library(name = 'spell_proto')");
-    Package pkg = packages.createPackage("duplicaterulename", buildFile);
+    Package pkg =
+        packages.createPackage("duplicaterulename", RootedPath.toRootedPath(root, buildFile));
 
     events.assertContainsError(
         "cc_library rule 'spell_proto' in package "
@@ -173,7 +176,7 @@
             "cc_library(name='dep')",
             "cc_library(name='has_dupe', deps=[':dep', ':dep'])");
 
-    Package pkg = packages.createPackage("has_dupe", buildFile);
+    Package pkg = packages.createPackage("has_dupe", RootedPath.toRootedPath(root, buildFile));
     events.assertContainsError(
         "Label '//has_dupe:dep' is duplicated in the 'deps' " + "attribute of rule 'has_dupe'");
     assertThat(pkg.containsErrors()).isTrue();
@@ -193,7 +196,7 @@
             "/fruit/orange/BUILD",
             "genrule(name='orange', srcs=[], outs=['a', 'a/b'], cmd='')");
 
-    packages.createPackage("fruit/orange", buildFile);
+    packages.createPackage("fruit/orange", RootedPath.toRootedPath(root, buildFile));
     events.assertContainsError("rule 'orange' has conflicting output files 'a/b' and 'a");
   }
 
@@ -205,7 +208,7 @@
             "/fruit/orange/BUILD",
             "genrule(name='orange', srcs=[], outs=['a/b', 'a'], cmd='')");
 
-    packages.createPackage("fruit/orange", buildFile);
+    packages.createPackage("fruit/orange", RootedPath.toRootedPath(root, buildFile));
     events.assertContainsError("rule 'orange' has conflicting output files 'a' and 'a/b");
   }
 
@@ -217,7 +220,7 @@
             "/fruit/kiwi/BUILD",
             "genrule(name='kiwi1', srcs=[], outs=['a'], cmd='')",
             "genrule(name='kiwi2', srcs=[], outs=['a/b'], cmd='')");
-    packages.createPackage("fruit/kiwi", buildFile);
+    packages.createPackage("fruit/kiwi", RootedPath.toRootedPath(root, buildFile));
     events.assertContainsError(
         "output file 'a/b' of rule 'kiwi2' conflicts " + "with output file 'a' of rule 'kiwi1'");
   }
@@ -230,7 +233,7 @@
             "/fruit/kiwi/BUILD",
             "genrule(name='kiwi1', srcs=[], outs=['a/b'], cmd='')",
             "genrule(name='kiwi2', srcs=[], outs=['a'], cmd='')");
-    packages.createPackage("fruit/kiwi", buildFile);
+    packages.createPackage("fruit/kiwi", RootedPath.toRootedPath(root, buildFile));
     events.assertContainsError(
         "output file 'a' of rule 'kiwi2' conflicts " + "with output file 'a/b' of rule 'kiwi1'");
   }
@@ -242,7 +245,9 @@
 
     Package pkg =
         packages.createPackage(
-            "pina", buildFile, "--incompatible_package_name_is_a_function=false");
+            "pina",
+            RootedPath.toRootedPath(root, buildFile),
+            "--incompatible_package_name_is_a_function=false");
     events.assertNoWarningsOrErrors();
     assertThat(pkg.containsErrors()).isFalse();
     assertThat(pkg.getRule("pina-colada")).isNotNull();
@@ -254,7 +259,10 @@
   public void testPackageConstantIsForbidden() throws Exception {
     events.setFailFast(false);
     Path buildFile = scratch.file("/pina/BUILD", "cc_library(name=PACKAGE_NAME + '-colada')");
-    packages.createPackage("pina", buildFile, "--incompatible_package_name_is_a_function=true");
+    packages.createPackage(
+        "pina",
+        RootedPath.toRootedPath(root, buildFile),
+        "--incompatible_package_name_is_a_function=true");
     events.assertContainsError("The value 'PACKAGE_NAME' has been removed");
   }
 
@@ -262,7 +270,7 @@
   public void testPackageNameFunction() throws Exception {
     Path buildFile = scratch.file("/pina/BUILD", "cc_library(name=package_name() + '-colada')");
 
-    Package pkg = packages.createPackage("pina", buildFile);
+    Package pkg = packages.createPackage("pina", RootedPath.toRootedPath(root, buildFile));
     events.assertNoWarningsOrErrors();
     assertThat(pkg.containsErrors()).isFalse();
     assertThat(pkg.getRule("pina-colada")).isNotNull();
@@ -279,7 +287,7 @@
     Package pkg =
         packages.createPackage(
             PackageIdentifier.create("@a", PathFragment.create("b")),
-            buildFile,
+            RootedPath.toRootedPath(root, buildFile),
             events.reporter(),
             "--incompatible_package_name_is_a_function=false");
     Rule c = pkg.getRule("c");
@@ -294,7 +302,7 @@
             "/external/a/b/BUILD", "genrule(name='c', srcs=[], outs=['ao'], cmd=REPOSITORY_NAME)");
     packages.createPackage(
         PackageIdentifier.create("@a", PathFragment.create("b")),
-        buildFile,
+        RootedPath.toRootedPath(root, buildFile),
         events.reporter(),
         "--incompatible_package_name_is_a_function=true");
     events.assertContainsError("The value 'REPOSITORY_NAME' has been removed");
@@ -308,7 +316,9 @@
             "genrule(name='c', srcs=[], outs=['o'], cmd=repository_name() + ' ' + package_name())");
     Package pkg =
         packages.createPackage(
-            PackageIdentifier.create("@a", PathFragment.create("b")), buildFile, events.reporter());
+            PackageIdentifier.create("@a", PathFragment.create("b")),
+            RootedPath.toRootedPath(root, buildFile),
+            events.reporter());
     Rule c = pkg.getRule("c");
     assertThat(AggregatingAttributeMapper.of(c).get("cmd", Type.STRING)).isEqualTo("@a b");
   }
@@ -329,7 +339,9 @@
             "         srcs = ['spell.proto'],",
             "         cc_api_version = 2)",
             "cc_library(name = 'spell_proto')");
-    Package pkg = packages.createPackage("multipleduplicaterulename", buildFile);
+    Package pkg =
+        packages.createPackage(
+            "multipleduplicaterulename", RootedPath.toRootedPath(root, buildFile));
 
     events.assertContainsError(
         "cc_library rule 'spellcheck_proto' in package "
@@ -343,7 +355,7 @@
   @Test
   public void testBuildFileTargetExists() throws Exception {
     Path buildFile = scratch.file("/foo/BUILD", "");
-    Package pkg = packages.createPackage("foo", buildFile);
+    Package pkg = packages.createPackage("foo", RootedPath.toRootedPath(root, buildFile));
 
     Target target = pkg.getTarget("BUILD");
     assertThat(target.getName()).isEqualTo("BUILD");
@@ -361,7 +373,7 @@
             "cc_library(name='W', deps=['X', 'Y'])",
             "cc_library(name='X', srcs=['X'])",
             "cc_library(name='Y')");
-    Package pkg = packages.createPackage("foo", buildFile);
+    Package pkg = packages.createPackage("foo", RootedPath.toRootedPath(root, buildFile));
     assertThat(pkg.containsErrors()).isFalse();
 
     // X is a rule with a circular self-dependency.
@@ -397,7 +409,8 @@
     events.setFailFast(false);
     Path buildFile =
         scratch.file("/third_party/foo/BUILD", "# line 1", "cc_library(name='bar')", "# line 3");
-    Package pkg = packages.createPackage("third_party/foo", buildFile);
+    Package pkg =
+        packages.createPackage("third_party/foo", RootedPath.toRootedPath(root, buildFile));
     events.assertContainsError(
         "third-party rule '//third_party/foo:bar' lacks a license "
             + "declaration with one of the following types: "
@@ -409,7 +422,8 @@
   public void testThirdPartyLicenseExportsFileError() throws Exception {
     events.setFailFast(false);
     Path buildFile = scratch.file("/third_party/foo/BUILD", "exports_files(['bar'])");
-    Package pkg = packages.createPackage("third_party/foo", buildFile);
+    Package pkg =
+        packages.createPackage("third_party/foo", RootedPath.toRootedPath(root, buildFile));
     events.assertContainsError(
         "third-party file 'bar' lacks a license "
             + "declaration with one of the following types: "
@@ -429,7 +443,7 @@
             "",
             "cc_library(name = 'dup_proto',",
             "           srcs = ['dup.pb.cc', 'dup.pb.h'])");
-    Package pkg = packages.createPackage("dup", path);
+    Package pkg = packages.createPackage("dup", RootedPath.toRootedPath(root, path));
     events.assertContainsError(
         "cc_library rule 'dup_proto' in package 'dup' "
             + "conflicts with existing proto_library rule");
@@ -463,7 +477,7 @@
             "        cmd = '',",
             "        srcs = ['in3', 'in4'],",
             "        outs = ['out3', 'out2'])");
-    Package pkg = packages.createPackage("conflict", path);
+    Package pkg = packages.createPackage("conflict", RootedPath.toRootedPath(root, path));
     events.assertContainsError(
         "generated file 'out2' in rule 'rule2' "
             + "conflicts with existing generated file from rule 'rule1'");
@@ -507,7 +521,7 @@
             "genrule(name = 'rule2',",
             "        cmd = ':',",
             "        outs = list)");
-    Package pkg = packages.createPackage("error", path);
+    Package pkg = packages.createPackage("error", RootedPath.toRootedPath(root, path));
     events.assertContainsError("name 'PopulateList' is not defined");
 
     assertThat(pkg.containsErrors()).isTrue();
@@ -531,7 +545,7 @@
     scratch.file("/x/y.cc");
     scratch.file("/x/dir/dummy");
 
-    Package pkg = packages.createPackage("x", path);
+    Package pkg = packages.createPackage("x", RootedPath.toRootedPath(root, path));
 
     assertThat(pkg.getTarget("x.cc")).isNotNull(); // existing and mentioned.
 
@@ -582,7 +596,7 @@
             "test_suite(name='t2', tests=['//foo'])",
             "test_suite(name='t3', tests=['//foo'])",
             "cc_test(name='c')");
-    Package pkg = packages.createPackage("x", path);
+    Package pkg = packages.createPackage("x", RootedPath.toRootedPath(root, path));
 
     // Things to note:
     // - the t1 refers to both :j and :c, even though :c is a forward reference.
@@ -610,7 +624,7 @@
             "/fruit/BUILD",
             "cc_library(name = 'yes', srcs = glob(['data/*']))",
             "cc_library(name = 'no',  srcs = glob(['data/*'], exclude_directories=0))");
-    Package pkg = packages.eval("fruit", file);
+    Package pkg = packages.eval("fruit", RootedPath.toRootedPath(root, file));
     events.assertNoWarningsOrErrors();
     List<Label> yesFiles = attributes(pkg.getRule("yes")).get("srcs", BuildType.LABEL_LIST);
     List<Label> noFiles = attributes(pkg.getRule("no")).get("srcs", BuildType.LABEL_LIST);
@@ -641,7 +655,7 @@
             "/rg/BUILD",
             "cc_library(name = 'ri', srcs = glob(['**/*.cc']))",
             "cc_library(name = 're', srcs = glob(['*.cc'], exclude=['**/*.c']))");
-    Package pkg = packages.eval("rg", file);
+    Package pkg = packages.eval("rg", RootedPath.toRootedPath(root, file));
     events.assertNoWarningsOrErrors();
 
     assertEvaluates(
@@ -861,7 +875,9 @@
     unreadableSubdir.setReadable(false);
 
     Path file = scratch.file("/pkg/BUILD", "cc_library(name = 'c', srcs = glob(['globs/**']))");
-    MoreAsserts.assertThrows(NoSuchPackageException.class, () -> packages.eval("pkg", file));
+    MoreAsserts.assertThrows(
+        NoSuchPackageException.class,
+        () -> packages.eval("pkg", RootedPath.toRootedPath(root, file)));
     events.assertContainsError("Directory is not readable");
   }
 
@@ -963,7 +979,7 @@
             "/foo/BUILD",
             "package(default_deprecation = \"" + msg + "\")",
             "sh_library(name = 'bar', srcs=['b'])");
-    Package pkg = packages.eval("foo", file);
+    Package pkg = packages.eval("foo", RootedPath.toRootedPath(root, file));
 
     Rule fooRule = (Rule) pkg.getTarget("bar");
     String deprAttr =
@@ -979,7 +995,7 @@
             "package(default_testonly = 1)",
             "sh_library(name = 'foo', srcs=['b'])",
             "sh_library(name = 'bar', srcs=['b'], testonly = 0)");
-    Package pkg = packages.eval("foo", file);
+    Package pkg = packages.eval("foo", RootedPath.toRootedPath(root, file));
 
     Rule fooRule = (Rule) pkg.getTarget("foo");
     assertThat(
@@ -1001,7 +1017,7 @@
             "/foo/BUILD",
             "package(default_deprecation = \"" + deceive + "\")",
             "sh_library(name = 'bar', srcs=['b'], deprecation = \"" + msg + "\")");
-    Package pkg = packages.eval("foo", file);
+    Package pkg = packages.eval("foo", RootedPath.toRootedPath(root, file));
 
     Rule fooRule = (Rule) pkg.getTarget("bar");
     String deprAttr =
@@ -1017,7 +1033,7 @@
             "sh_library(name='before')",
             "package(features=['b', 'c'])",
             "sh_library(name='after')");
-    Package pkg = packages.eval("a", file);
+    Package pkg = packages.eval("a", RootedPath.toRootedPath(root, file));
 
     assertThat(pkg.getFeatures()).containsExactly("b", "c");
   }
@@ -1031,10 +1047,11 @@
     scratch.file("/e/data.txt");
     throwOnReaddir = parentDir;
     MoreAsserts.assertThrows(
-        NoSuchPackageException.class, () -> packages.createPackage("e", buildFile));
+        NoSuchPackageException.class,
+        () -> packages.createPackage("e", RootedPath.toRootedPath(root, buildFile)));
     events.setFailFast(true);
     throwOnReaddir = null;
-    Package pkg = packages.createPackage("e", buildFile);
+    Package pkg = packages.createPackage("e", RootedPath.toRootedPath(root, buildFile));
     assertThat(pkg.containsErrors()).isFalse();
     assertThat(pkg.getRule("e")).isNotNull();
     List globList = (List) pkg.getRule("e").getAttributeContainer().getAttr("data");
diff --git a/src/test/java/com/google/devtools/build/lib/packages/PackageGroupStaticInitializationTest.java b/src/test/java/com/google/devtools/build/lib/packages/PackageGroupStaticInitializationTest.java
index e778d89..151c72a 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/PackageGroupStaticInitializationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/PackageGroupStaticInitializationTest.java
@@ -22,7 +22,10 @@
 import com.google.devtools.build.lib.testutil.Scratch;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.Root;
+import com.google.devtools.build.lib.vfs.RootedPath;
 import java.util.concurrent.SynchronousQueue;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -38,6 +41,12 @@
   private Scratch scratch = new Scratch("/workspace");
   private EventCollectionApparatus events = new EventCollectionApparatus();
   private PackageFactoryApparatus packages = new PackageFactoryApparatus(events.reporter());
+  private Root root;
+
+  @Before
+  public void setUp() throws Exception {
+    root = Root.fromPath(scratch.dir(""));
+  }
 
   @Test
   public void testNoDeadlockOnPackageGroupCreation() throws Exception {
@@ -90,7 +99,7 @@
   private Package getPackage(String packageName) throws Exception {
     PathFragment buildFileFragment = PathFragment.create(packageName).getRelative("BUILD");
     Path buildFile = scratch.resolve(buildFileFragment.getPathString());
-    return packages.createPackage(packageName, buildFile);
+    return packages.createPackage(packageName, RootedPath.toRootedPath(root, buildFile));
   }
 
   private PackageGroup getPackageGroup(String pkg, String name) throws Exception {
diff --git a/src/test/java/com/google/devtools/build/lib/packages/PackageGroupTest.java b/src/test/java/com/google/devtools/build/lib/packages/PackageGroupTest.java
index 12f0eaf..a16d7cc 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/PackageGroupTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/PackageGroupTest.java
@@ -21,6 +21,9 @@
 import com.google.devtools.build.lib.testutil.Scratch;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.Root;
+import com.google.devtools.build.lib.vfs.RootedPath;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -33,6 +36,12 @@
   private Scratch scratch = new Scratch("/workspace");
   private EventCollectionApparatus events = new EventCollectionApparatus();
   private PackageFactoryApparatus packages = new PackageFactoryApparatus(events.reporter());
+  private Root root;
+
+  @Before
+  public void setUp() throws Exception {
+    root = Root.fromPath(scratch.dir(""));
+  }
 
   @Test
   public void testDoesNotFailHorribly() throws Exception {
@@ -243,7 +252,7 @@
     PathFragment buildFileFragment = PathFragment.create(packageName).getRelative("BUILD");
 
     Path buildFile = scratch.resolve(buildFileFragment.getPathString());
-    return packages.createPackage(packageName, buildFile);
+    return packages.createPackage(packageName, RootedPath.toRootedPath(root, buildFile));
   }
 
   private PackageGroup getPackageGroup(String pkg, String name) throws Exception {
diff --git a/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java b/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java
index fee974b..4244102 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java
@@ -57,6 +57,7 @@
 import com.google.devtools.build.lib.syntax.Environment;
 import com.google.devtools.build.lib.syntax.Type;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.RootedPath;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -245,15 +246,15 @@
 
   @Before
   public final void setRuleLocation() throws Exception {
-    testBuildfilePath = scratch.resolve("/home/user/workspace/testpackage/BUILD");
+    testBuildfilePath = root.getRelative("testpackage/BUILD");
     testRuleLocation = Location.fromPathAndStartColumn(
         testBuildfilePath.asFragment(), 0, 0, new LineAndColumn(TEST_RULE_DEFINED_AT_LINE, 0));
   }
 
   private Package.Builder createDummyPackageBuilder() {
-    return packageFactory.newPackageBuilder(
-        PackageIdentifier.createInMainRepo(TEST_PACKAGE_NAME), "TESTING")
-        .setFilename(testBuildfilePath);
+    return packageFactory
+        .newPackageBuilder(PackageIdentifier.createInMainRepo(TEST_PACKAGE_NAME), "TESTING")
+        .setFilename(RootedPath.toRootedPath(root, testBuildfilePath));
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/packages/RuleFactoryTest.java b/src/test/java/com/google/devtools/build/lib/packages/RuleFactoryTest.java
index bfd84c9..ab0a636 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/RuleFactoryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/RuleFactoryTest.java
@@ -31,6 +31,7 @@
 import com.google.devtools.build.lib.syntax.Type;
 import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.RootedPath;
 import java.util.HashMap;
 import java.util.Map;
 import org.junit.Test;
@@ -47,10 +48,11 @@
 
   @Test
   public void testCreateRule() throws Exception {
-    Path myPkgPath = scratch.resolve("/foo/workspace/mypkg/BUILD");
+    Path myPkgPath = scratch.resolve("/workspace/mypkg/BUILD");
     Package.Builder pkgBuilder =
-        packageFactory.newPackageBuilder(PackageIdentifier.createInMainRepo("mypkg"), "TESTING")
-            .setFilename(myPkgPath);
+        packageFactory
+            .newPackageBuilder(PackageIdentifier.createInMainRepo("mypkg"), "TESTING")
+            .setFilename(RootedPath.toRootedPath(root, myPkgPath));
 
     Map<String, Object> attributeValues = new HashMap<>();
     attributeValues.put("name", "foo");
@@ -110,8 +112,10 @@
 
   @Test
   public void testCreateWorkspaceRule() throws Exception {
-    Path myPkgPath = scratch.resolve("/foo/workspace/WORKSPACE");
-    Package.Builder pkgBuilder = packageFactory.newExternalPackageBuilder(myPkgPath, "TESTING");
+    Path myPkgPath = scratch.resolve("/workspace/WORKSPACE");
+    Package.Builder pkgBuilder =
+        packageFactory.newExternalPackageBuilder(
+            RootedPath.toRootedPath(root, myPkgPath), "TESTING");
 
     Map<String, Object> attributeValues = new HashMap<>();
     attributeValues.put("name", "foo");
@@ -133,10 +137,11 @@
 
   @Test
   public void testWorkspaceRuleFailsInBuildFile() throws Exception {
-    Path myPkgPath = scratch.resolve("/foo/workspace/mypkg/BUILD");
+    Path myPkgPath = scratch.resolve("/workspace/mypkg/BUILD");
     Package.Builder pkgBuilder =
-        packageFactory.newPackageBuilder(PackageIdentifier.createInMainRepo("mypkg"), "TESTING")
-            .setFilename(myPkgPath);
+        packageFactory
+            .newPackageBuilder(PackageIdentifier.createInMainRepo("mypkg"), "TESTING")
+            .setFilename(RootedPath.toRootedPath(root, myPkgPath));
 
     Map<String, Object> attributeValues = new HashMap<>();
     attributeValues.put("name", "foo");
@@ -161,10 +166,11 @@
 
   @Test
   public void testBuildRuleFailsInWorkspaceFile() throws Exception {
-    Path myPkgPath = scratch.resolve("/foo/workspace/WORKSPACE");
+    Path myPkgPath = scratch.resolve("/workspace/WORKSPACE");
     Package.Builder pkgBuilder =
-        packageFactory.newPackageBuilder(Label.EXTERNAL_PACKAGE_IDENTIFIER, "TESTING")
-            .setFilename(myPkgPath);
+        packageFactory
+            .newPackageBuilder(Label.EXTERNAL_PACKAGE_IDENTIFIER, "TESTING")
+            .setFilename(RootedPath.toRootedPath(root, myPkgPath));
 
     Map<String, Object> attributeValues = new HashMap<>();
     attributeValues.put("name", "foo");
@@ -202,10 +208,11 @@
 
   @Test
   public void testOutputFileNotEqualDot() throws Exception {
-    Path myPkgPath = scratch.resolve("/foo");
+    Path myPkgPath = scratch.resolve("/workspace/mypkg");
     Package.Builder pkgBuilder =
-        packageFactory.newPackageBuilder(PackageIdentifier.createInMainRepo("mypkg"), "TESTING")
-            .setFilename(myPkgPath);
+        packageFactory
+            .newPackageBuilder(PackageIdentifier.createInMainRepo("mypkg"), "TESTING")
+            .setFilename(RootedPath.toRootedPath(root, myPkgPath));
 
     Map<String, Object> attributeValues = new HashMap<>();
     attributeValues.put("outs", Lists.newArrayList("."));
@@ -236,10 +243,11 @@
   // pattern, which will always guarantee that these attributes are present.
   @Test
   public void testTestRules() throws Exception {
-    Path myPkgPath = scratch.resolve("/foo/workspace/mypkg/BUILD");
+    Path myPkgPath = scratch.resolve("/workspace/mypkg/BUILD");
     Package pkg =
-        packageFactory.newPackageBuilder(PackageIdentifier.createInMainRepo("mypkg"), "TESTING")
-            .setFilename(myPkgPath)
+        packageFactory
+            .newPackageBuilder(PackageIdentifier.createInMainRepo("mypkg"), "TESTING")
+            .setFilename(RootedPath.toRootedPath(root, myPkgPath))
             .build();
 
     for (String name : ruleFactory.getRuleClassNames()) {
diff --git a/src/test/java/com/google/devtools/build/lib/packages/RuleTest.java b/src/test/java/com/google/devtools/build/lib/packages/RuleTest.java
index 2871523..936baa6 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/RuleTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/RuleTest.java
@@ -21,6 +21,9 @@
 import com.google.devtools.build.lib.packages.util.PackageFactoryApparatus;
 import com.google.devtools.build.lib.testutil.Scratch;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.Root;
+import com.google.devtools.build.lib.vfs.RootedPath;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -33,6 +36,12 @@
   private Scratch scratch = new Scratch("/workspace");
   private EventCollectionApparatus events = new EventCollectionApparatus();
   private PackageFactoryApparatus packages = new PackageFactoryApparatus(events.reporter());
+  private Root root;
+
+  @Before
+  public void setUp() throws Exception {
+    root = Root.fromPath(scratch.dir(""));
+  }
 
   @Test
   public void testAttributeLocation() throws Exception {
@@ -40,7 +49,7 @@
         "cc_binary(name = 'x',",
         "          srcs = ['a', 'b', 'c'],",
         "          defines = ['-Da', '-Db'])");
-    Package pkg = packages.createPackage("x", buildFile);
+    Package pkg = packages.createPackage("x", RootedPath.toRootedPath(root, buildFile));
     Rule rule = pkg.getRule("x");
 
     assertThat(rule.getLocation().getStartLineAndColumn()).isEqualTo(new LineAndColumn(1, 1));
@@ -74,7 +83,7 @@
                 "outs = ['message.txt', 'hello_world'],",
                 "cmd  = 'echo \"Hello, world.\" >$(location message.txt)')");
 
-    Package pkg = packages.createPackage("namecollide", buildFile);
+    Package pkg = packages.createPackage("namecollide", RootedPath.toRootedPath(root, buildFile));
     Rule genRule = pkg.getRule("hello_world");
     assertThat(genRule.containsErrors()).isFalse(); // TODO: assertTrue
     events.assertContainsWarning("target 'hello_world' is both a rule and a file; please choose "
@@ -90,7 +99,7 @@
         "cc_test(name = 'z',",
         "          srcs = ['a'],",
         "          local = 1)");
-    Package pkg = packages.createPackage("x", buildFile);
+    Package pkg = packages.createPackage("x", RootedPath.toRootedPath(root, buildFile));
     Rule y = pkg.getRule("y");
     assertThat(TargetUtils.isLocalTestRule(y)).isFalse();
     Rule z = pkg.getRule("z");
@@ -102,7 +111,7 @@
     Path buildFile = scratch.file("x/BUILD",
         "cc_test(name = 'y')",
         "cc_test(name = 'z', deprecation = 'Foo')");
-    Package pkg = packages.createPackage("x", buildFile);
+    Package pkg = packages.createPackage("x", RootedPath.toRootedPath(root, buildFile));
     Rule y = pkg.getRule("y");
     assertThat(TargetUtils.getDeprecation(y)).isNull();
     Rule z = pkg.getRule("z");
@@ -111,13 +120,19 @@
 
   @Test
   public void testVisibilityValid() throws Exception {
-    Package pkg = packages.createPackage("x", scratch.file("x/BUILD",
-        "cc_binary(name = 'pr',",
-        "          visibility = ['//visibility:private'])",
-        "cc_binary(name = 'pu',",
-        "          visibility = ['//visibility:public'])",
-        "cc_binary(name = 'cu',",
-        "          visibility = ['//a:b'])"));
+    Package pkg =
+        packages.createPackage(
+            "x",
+            RootedPath.toRootedPath(
+                root,
+                scratch.file(
+                    "x/BUILD",
+                    "cc_binary(name = 'pr',",
+                    "          visibility = ['//visibility:private'])",
+                    "cc_binary(name = 'pu',",
+                    "          visibility = ['//visibility:public'])",
+                    "cc_binary(name = 'cu',",
+                    "          visibility = ['//a:b'])")));
 
     assertThat(pkg.getRule("pu").getVisibility()).isEqualTo(ConstantRuleVisibility.PUBLIC);
     assertThat(pkg.getRule("pr").getVisibility()).isEqualTo(ConstantRuleVisibility.PRIVATE);
diff --git a/src/test/java/com/google/devtools/build/lib/packages/WorkspaceFactoryTest.java b/src/test/java/com/google/devtools/build/lib/packages/WorkspaceFactoryTest.java
index 017ec02..6c5954b 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/WorkspaceFactoryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/WorkspaceFactoryTest.java
@@ -17,6 +17,9 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.devtools.build.lib.cmdline.RepositoryName;
+import com.google.devtools.build.lib.testutil.Scratch;
+import com.google.devtools.build.lib.vfs.Root;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -26,8 +29,15 @@
  */
 @RunWith(JUnit4.class)
 public class WorkspaceFactoryTest {
+  private Scratch scratch = new Scratch();
+  private WorkspaceFactoryTestHelper helper;
+  private Root root;
 
-  private WorkspaceFactoryTestHelper helper = new WorkspaceFactoryTestHelper();
+  @Before
+  public void setUp() throws Exception {
+    root = Root.fromPath(scratch.dir(""));
+    helper = new WorkspaceFactoryTestHelper(root);
+  }
 
   @Test
   public void testLoadError() throws Exception {
@@ -65,7 +75,7 @@
 
   @Test
   public void testIllegalWorkspaceFunctionPosition() throws Exception {
-    helper = new WorkspaceFactoryTestHelper(/*allowOverride=*/ false);
+    helper = new WorkspaceFactoryTestHelper(/*allowOverride=*/ false, root);
     helper.parse("workspace(name = 'foo')");
     assertThat(helper.getParserError()).contains(
         "workspace() function should be used only at the top of the WORKSPACE file");
diff --git a/src/test/java/com/google/devtools/build/lib/packages/WorkspaceFactoryTestHelper.java b/src/test/java/com/google/devtools/build/lib/packages/WorkspaceFactoryTestHelper.java
index c4a02bd..e50e53a 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/WorkspaceFactoryTestHelper.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/WorkspaceFactoryTestHelper.java
@@ -23,16 +23,18 @@
 import com.google.devtools.build.lib.syntax.Mutability;
 import com.google.devtools.build.lib.syntax.ParserInputSource;
 import com.google.devtools.build.lib.syntax.SkylarkSemantics;
-import com.google.devtools.build.lib.testutil.Scratch;
 import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.Root;
+import com.google.devtools.build.lib.vfs.RootedPath;
 import com.google.devtools.common.options.OptionsParser;
 import java.io.IOException;
 import java.util.List;
 
 /** Parses a WORKSPACE file with the given content. */
 class WorkspaceFactoryTestHelper {
+  private final Root root;
   private Package.Builder builder;
   private Exception exception;
   private ImmutableList<Event> events;
@@ -40,30 +42,31 @@
 
   private final boolean allowOverride;
 
-  WorkspaceFactoryTestHelper() {
-    this(true);
+  WorkspaceFactoryTestHelper(Root root) {
+    this(true, root);
   }
 
-  WorkspaceFactoryTestHelper(boolean allowOverride) {
+  WorkspaceFactoryTestHelper(boolean allowOverride, Root root) {
+    this.root = root;
     this.exception = null;
     this.events = null;
     this.allowOverride = allowOverride;
     this.skylarkSemantics = SkylarkSemantics.DEFAULT_SEMANTICS;
   }
 
-  void parse(Scratch scratch, String... args) {
-    Path root = null;
-    Path workspaceFilePath = null;
+  void parse(String... args) {
+    Path workspaceFilePath = root.getRelative("WORKSPACE");
     try {
-      root = scratch.dir("/workspace");
-      workspaceFilePath = scratch.overwriteFile("/workspace/WORKSPACE", args);
+      FileSystemUtils.writeIsoLatin1(workspaceFilePath, args);
     } catch (IOException e) {
       fail("Shouldn't happen: " + e.getMessage());
     }
     StoredEventHandler eventHandler = new StoredEventHandler();
     builder =
         Package.newExternalPackageBuilder(
-            Package.Builder.DefaultHelper.INSTANCE, workspaceFilePath, "");
+            Package.Builder.DefaultHelper.INSTANCE,
+            RootedPath.toRootedPath(root, workspaceFilePath),
+            "");
     WorkspaceFactory factory =
         new WorkspaceFactory(
             builder,
@@ -71,8 +74,8 @@
             ImmutableList.<PackageFactory.EnvironmentExtension>of(),
             Mutability.create("test"),
             allowOverride,
-            root,
-            root,
+            root.asPath(),
+            root.asPath(),
             /* defaultSystemJavabaseDir= */ null,
             /* skylarkSemantics= */ SkylarkSemantics.DEFAULT_SEMANTICS);
     Exception exception = null;
@@ -92,17 +95,13 @@
     this.exception = exception;
   }
 
-  void parse(String... args) {
-    parse(new Scratch("/"), args);
-  }
-
   public Package getPackage() throws InterruptedException, NoSuchPackageException {
     return builder.build();
   }
 
   public void assertLexingExceptionThrown() {
     assertThat(exception).isNotNull();
-    assertThat(exception).hasMessageThat().contains("Failed to parse /workspace/WORKSPACE");
+    assertThat(exception).hasMessageThat().contains("Failed to parse /WORKSPACE");
   }
 
   public String getLexerError() {
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 83def68..cf1f485 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
@@ -39,6 +39,7 @@
 import com.google.devtools.build.lib.util.Pair;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.RootedPath;
 import com.google.devtools.common.options.OptionsParser;
 import java.io.IOException;
 
@@ -78,11 +79,11 @@
   }
 
   /** Parses and evaluates {@code buildFile} and returns the resulting {@link Package} instance. */
-  public Package createPackage(String packageName, Path buildFile) throws Exception {
+  public Package createPackage(String packageName, RootedPath buildFile) throws Exception {
     return createPackage(PackageIdentifier.createInMainRepo(packageName), buildFile, eventHandler);
   }
 
-  public Package createPackage(String packageName, Path buildFile, String skylarkOption)
+  public Package createPackage(String packageName, RootedPath buildFile, String skylarkOption)
       throws Exception {
     return createPackage(
         PackageIdentifier.createInMainRepo(packageName), buildFile, eventHandler, skylarkOption);
@@ -94,7 +95,7 @@
    */
   public Package createPackage(
       PackageIdentifier packageIdentifier,
-      Path buildFile,
+      RootedPath buildFile,
       ExtendedEventHandler reporter,
       String skylarkOption)
       throws Exception {
@@ -111,7 +112,10 @@
       Package externalPkg =
           factory
               .newExternalPackageBuilder(
-                  buildFile.getRelative(Label.WORKSPACE_FILE_NAME), "TESTING")
+                  RootedPath.toRootedPath(
+                      buildFile.getRoot(),
+                      buildFile.getRootRelativePath().getRelative(Label.WORKSPACE_FILE_NAME)),
+                  "TESTING")
               .build();
       Package pkg =
           factory.createPackageForTesting(
@@ -123,7 +127,7 @@
   }
 
   public Package createPackage(
-      PackageIdentifier packageIdentifier, Path buildFile, ExtendedEventHandler reporter)
+      PackageIdentifier packageIdentifier, RootedPath buildFile, ExtendedEventHandler reporter)
       throws Exception {
     return createPackage(packageIdentifier, buildFile, reporter, null);
   }
@@ -139,12 +143,12 @@
 
   /** Evaluates the {@code buildFileAST} into a {@link Package}. */
   public Pair<Package, GlobCache> evalAndReturnGlobCache(
-      String packageName, Path buildFile, BuildFileAST buildFileAST)
+      String packageName, RootedPath buildFile, BuildFileAST buildFileAST)
       throws InterruptedException, NoSuchPackageException {
     PackageIdentifier packageId = PackageIdentifier.createInMainRepo(packageName);
     GlobCache globCache =
         new GlobCache(
-            buildFile.getParentDirectory(),
+            buildFile.asPath().getParentDirectory(),
             packageId,
             getPackageLocator(),
             null,
@@ -152,8 +156,12 @@
             -1);
     LegacyGlobber globber = PackageFactory.createLegacyGlobber(globCache);
     Package externalPkg =
-        factory.newExternalPackageBuilder(
-                buildFile.getParentDirectory().getRelative("WORKSPACE"), "TESTING")
+        factory
+            .newExternalPackageBuilder(
+                RootedPath.toRootedPath(
+                    buildFile.getRoot(),
+                    buildFile.getRootRelativePath().getParentDirectory().getRelative("WORKSPACE")),
+                "TESTING")
             .build();
     Package.Builder resultBuilder =
         factory.evaluateBuildFile(
@@ -181,15 +189,15 @@
     return Pair.of(result, globCache);
   }
 
-  public Package eval(String packageName, Path buildFile, BuildFileAST buildFileAST)
+  public Package eval(String packageName, RootedPath buildFile, BuildFileAST buildFileAST)
       throws InterruptedException, NoSuchPackageException {
     return evalAndReturnGlobCache(packageName, buildFile, buildFileAST).first;
   }
 
   /** Evaluates the {@code buildFileAST} into a {@link Package}. */
-  public Package eval(String packageName, Path buildFile)
+  public Package eval(String packageName, RootedPath buildFile)
       throws InterruptedException, IOException, NoSuchPackageException {
-    return eval(packageName, buildFile, ast(buildFile));
+    return eval(packageName, buildFile, ast(buildFile.asPath()));
   }
 
   /**
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 eb614fc..24d9eb0 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
@@ -40,6 +40,8 @@
 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.Root;
+import com.google.devtools.build.lib.vfs.RootedPath;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -59,19 +61,20 @@
   protected Scratch scratch;
   protected EventCollectionApparatus events = new EventCollectionApparatus();
   protected PackageFactoryApparatus packages = createPackageFactoryApparatus();
+  protected Root root;
 
   protected com.google.devtools.build.lib.packages.Package expectEvalSuccess(String... content)
       throws InterruptedException, IOException, NoSuchPackageException {
-    Path file = scratch.file("/pkg/BUILD", content);
-    Package pkg = packages.eval("pkg", file);
+    Path file = scratch.file("pkg/BUILD", content);
+    Package pkg = packages.eval("pkg", RootedPath.toRootedPath(root, file));
     assertThat(pkg.containsErrors()).isFalse();
     return pkg;
   }
 
   protected void expectEvalError(String expectedError, String... content) throws Exception {
     events.setFailFast(false);
-    Path file = scratch.file("/pkg/BUILD", content);
-    Package pkg = packages.eval("pkg", file);
+    Path file = scratch.file("pkg/BUILD", content);
+    Package pkg = packages.eval("pkg", RootedPath.toRootedPath(root, file));
     assertWithMessage("Expected evaluation error, but none was not reported")
         .that(pkg.containsErrors())
         .isTrue();
@@ -108,7 +111,7 @@
       throws Exception {
     GlobCache globCache =
         new GlobCache(
-            pkg.getFilename().getParentDirectory(),
+            pkg.getFilename().asPath().getParentDirectory(),
             pkg.getPackageIdentifier(),
             PackageFactoryApparatus.createEmptyLocator(),
             null,
@@ -129,8 +132,9 @@
             return super.readdir(path, followSymlinks);
           }
         };
-    Path tmpPath = fs.getPath("/tmp");
+    Path tmpPath = fs.getPath("/");
     scratch = new Scratch(tmpPath);
+    root = Root.fromPath(scratch.dir("/"));
   }
 
   protected Path emptyBuildFile(String packageName) {
@@ -149,7 +153,7 @@
     // Write a license decl just in case it's a third_party package:
     Path buildFile = scratch.file(
         getPathPrefix() + "/" + packageName + "/BUILD", "licenses(['notice'])");
-    Package pkg = packages.createPackage(packageName, buildFile);
+    Package pkg = packages.createPackage(packageName, RootedPath.toRootedPath(root, buildFile));
     return !pkg.containsErrors();
   }
 
@@ -168,7 +172,7 @@
   private Package buildPackageWithGlob(String globCallExpression) throws Exception {
     scratch.deleteFile("/dummypackage/BUILD");
     Path file = scratch.file("/dummypackage/BUILD", "x = " + globCallExpression);
-    return packages.eval("dummypackage", file);
+    return packages.eval("dummypackage", RootedPath.toRootedPath(root, file));
   }
 
   private List<Pair<String, Boolean>> createGlobCacheKeys(
@@ -242,7 +246,8 @@
                 includes, excludes, excludeDirs ? 1 : 0),
             resultAssertion);
 
-    return packages.evalAndReturnGlobCache("globs", file, packages.ast(file));
+    return packages.evalAndReturnGlobCache(
+        "globs", RootedPath.toRootedPath(root, file), packages.ast(file));
   }
 
   protected void assertGlobProducesError(String pattern, boolean errorExpected) throws Exception {
@@ -293,10 +298,12 @@
                 "java_library(name = 'mylib',",
                 "  srcs = 'java/A.java')");
         packages.createPackage(
-            PackageIdentifier.createInMainRepo("isolated"), buildFile, eventHandler);
+            PackageIdentifier.createInMainRepo("isolated"),
+            RootedPath.toRootedPath(root, buildFile),
+            eventHandler);
         parsedOK = true;
       } catch (Exception e) {
-        e.printStackTrace();
+        throw new IllegalStateException(e);
       }
     }
 
diff --git a/src/test/java/com/google/devtools/build/lib/pkgcache/PackageCacheTest.java b/src/test/java/com/google/devtools/build/lib/pkgcache/PackageCacheTest.java
index e845748..8392f3b 100644
--- a/src/test/java/com/google/devtools/build/lib/pkgcache/PackageCacheTest.java
+++ b/src/test/java/com/google/devtools/build/lib/pkgcache/PackageCacheTest.java
@@ -224,7 +224,7 @@
     createPkg1();
     Package pkg1 = getPackage("pkg1");
     assertThat(pkg1.getName()).isEqualTo("pkg1");
-    assertThat(pkg1.getFilename().toString()).isEqualTo("/workspace/pkg1/BUILD");
+    assertThat(pkg1.getFilename().asPath().getPathString()).isEqualTo("/workspace/pkg1/BUILD");
     assertThat(getPackageManager().getPackage(reporter, PackageIdentifier.createInMainRepo("pkg1")))
         .isSameAs(pkg1);
   }
@@ -349,7 +349,7 @@
 
     Package oldPkg = getPackage("pkg");
     assertThat(getPackage("pkg")).isSameAs(oldPkg); // change not yet visible
-    assertThat(oldPkg.getFilename()).isEqualTo(buildFile1);
+    assertThat(oldPkg.getFilename().asPath()).isEqualTo(buildFile1);
     assertThat(oldPkg.getSourceRoot()).isEqualTo(Root.fromPath(rootDirectory));
 
     buildFile1.delete();
@@ -357,7 +357,7 @@
 
     Package newPkg = getPackage("pkg");
     assertThat(newPkg).isNotSameAs(oldPkg);
-    assertThat(newPkg.getFilename()).isEqualTo(buildFile2);
+    assertThat(newPkg.getFilename().asPath()).isEqualTo(buildFile2);
     assertThat(newPkg.getSourceRoot()).isEqualTo(Root.fromPath(scratch.dir("/otherroot")));
 
     // TODO(bazel-team): (2009) test BUILD file moves in the other direction too.
@@ -526,7 +526,8 @@
     // root.  It's as if we've merged c and c/d in the first root.
 
     // c/d is still a subpackage--found in the second root:
-    assertThat(getPackage("c/d").getFilename()).isEqualTo(rootDir2.getRelative("c/d/BUILD"));
+    assertThat(getPackage("c/d").getFilename().asPath())
+        .isEqualTo(rootDir2.getRelative("c/d/BUILD"));
 
     // Subpackage labels are still valid...
     assertLabelValidity(true, "//c/d:foo.txt");
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TransitiveTraversalFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/TransitiveTraversalFunctionTest.java
index f338971..22a9cc2 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TransitiveTraversalFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TransitiveTraversalFunctionTest.java
@@ -28,6 +28,7 @@
 import com.google.devtools.build.lib.util.GroupedList;
 import com.google.devtools.build.lib.util.GroupedList.GroupedListHelper;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.RootedPath;
 import com.google.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyKey;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -102,12 +103,14 @@
     Path buildFile = scratch.file("" + packageId.getSourceRoot() + "/BUILD", lines);
     Package.Builder externalPkg =
         Package.newExternalPackageBuilder(
-            Package.Builder.DefaultHelper.INSTANCE, buildFile.getRelative("WORKSPACE"), "TESTING");
+            Package.Builder.DefaultHelper.INSTANCE,
+            RootedPath.toRootedPath(root, buildFile.getRelative("WORKSPACE")),
+            "TESTING");
     externalPkg.setWorkspaceName(workspaceName);
     return pkgFactory.createPackageForTesting(
         packageId,
         externalPkg.build(),
-        buildFile,
+        RootedPath.toRootedPath(root, buildFile),
         packageIdentifier -> buildFile,
         reporter,
         SkylarkSemantics.DEFAULT_SEMANTICS);
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoaderTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoaderTest.java
index d83b570..3f84377 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoaderTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoaderTest.java
@@ -31,6 +31,7 @@
 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.Root;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
 import org.junit.Before;
 import org.junit.Test;
@@ -40,6 +41,7 @@
   protected Path workspaceDir;
   protected StoredEventHandler handler;
   protected FileSystem fs;
+  protected Root root;
   private Reporter reporter;
 
   @Before
@@ -47,6 +49,7 @@
     fs = new InMemoryFileSystem();
     workspaceDir = fs.getPath("/workspace/");
     workspaceDir.createDirectoryAndParents();
+    root = Root.fromPath(workspaceDir);
     reporter = new Reporter(new EventBus());
     handler = new StoredEventHandler();
     reporter.addHandler(handler);
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoaderTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoaderTest.java
index b31f2ac..e356fc1 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoaderTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoaderTest.java
@@ -95,7 +95,7 @@
 
   @Override
   protected BazelPackageLoader.Builder newPackageLoaderBuilder(Path workspaceDir) {
-    return BazelPackageLoader.builder(workspaceDir, installBase, outputBase);
+    return BazelPackageLoader.builder(root, workspaceDir, installBase, outputBase);
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/testutil/BazelPackageBuilderHelperForTesting.java b/src/test/java/com/google/devtools/build/lib/testutil/BazelPackageBuilderHelperForTesting.java
index e27bc4d..acf374d 100644
--- a/src/test/java/com/google/devtools/build/lib/testutil/BazelPackageBuilderHelperForTesting.java
+++ b/src/test/java/com/google/devtools/build/lib/testutil/BazelPackageBuilderHelperForTesting.java
@@ -68,6 +68,7 @@
     PackageIdentifier pkgId = pkg.getPackageIdentifier();
     PackageLoader packageLoader =
         BazelPackageLoader.builder(
+                pkg.getSourceRoot(),
                 directories.getWorkspace(),
                 directories.getInstallBase(),
                 directories.getOutputBase())
diff --git a/src/test/java/com/google/devtools/build/lib/testutil/FoundationTestCase.java b/src/test/java/com/google/devtools/build/lib/testutil/FoundationTestCase.java
index 6a966a5..0a4fde5 100644
--- a/src/test/java/com/google/devtools/build/lib/testutil/FoundationTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/testutil/FoundationTestCase.java
@@ -24,6 +24,7 @@
 import com.google.devtools.build.lib.events.Reporter;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.Root;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
 import java.util.Set;
 import org.junit.After;
@@ -43,6 +44,7 @@
   protected EventCollector eventCollector;
   protected FileSystem fileSystem;
   protected Scratch scratch;
+  protected Root root;
 
   /** Returns the Scratch instance for this test case. */
   public Scratch getScratch() {
@@ -75,6 +77,7 @@
     outputBase = scratch.dir("/usr/local/google/_blaze_jrluser/FAKEMD5/");
     rootDirectory = scratch.dir("/workspace");
     scratch.file(rootDirectory.getRelative("WORKSPACE").getPathString());
+    root = Root.fromPath(rootDirectory);
   }
 
   @Before
diff --git a/src/tools/package_printer/java/com/google/devtools/build/packageprinter/BazelPackagePrinter.java b/src/tools/package_printer/java/com/google/devtools/build/packageprinter/BazelPackagePrinter.java
index b9a1c55..b9fc7b0 100644
--- a/src/tools/package_printer/java/com/google/devtools/build/packageprinter/BazelPackagePrinter.java
+++ b/src/tools/package_printer/java/com/google/devtools/build/packageprinter/BazelPackagePrinter.java
@@ -22,6 +22,7 @@
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.Root;
 import java.nio.file.Paths;
 
 /**
@@ -52,7 +53,8 @@
 
   /** newPackageLoader returns a new PackageLoader. */
   static PackageLoader newPackageLoader(Path workspaceDir, Path installBase, Path outputBase) {
-    return BazelPackageLoader.builder(workspaceDir, installBase, outputBase)
+    return BazelPackageLoader.builder(
+            Root.fromPath(workspaceDir), workspaceDir, installBase, outputBase)
         .useDefaultSkylarkSemantics()
         .setReporter(new Reporter(new EventBus(), PrintingEventHandler.ERRORS_TO_STDERR))
         .setLegacyGlobbingThreads(400)