Introduce Root class.

This class represents a root (such as a package path or an output root) used for file lookups and artifacts. It is meant to be as opaque as possible in order to hide the user's environment from sky keys and sky functions.

Roots are used by RootedPaths and ArtifactRoots.

This CL attempts to make the minimum number of modifications necessary to change RootedPath and ArtifactRoot to use these fields. Deprecated methods and invasive accessors are permitted to minimise the risk of any observable changes.

RELNOTES: None
PiperOrigin-RevId: 182271759
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ActionTemplateExpansionFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ActionTemplateExpansionFunctionTest.java
index f122174..9b2e73d 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ActionTemplateExpansionFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ActionTemplateExpansionFunctionTest.java
@@ -42,6 +42,7 @@
 import com.google.devtools.build.lib.testutil.FoundationTestCase;
 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.skyframe.EvaluationResult;
 import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
 import com.google.devtools.build.skyframe.MemoizingEvaluator;
@@ -75,7 +76,7 @@
         new AtomicReference<>(
             new PathPackageLocator(
                 rootDirectory.getFileSystem().getPath("/outputbase"),
-                ImmutableList.of(rootDirectory),
+                ImmutableList.of(Root.fromPath(rootDirectory)),
                 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
     RecordingDifferencer differencer = new SequencedRecordingDifferencer();
     MemoizingEvaluator evaluator =
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java
index 339ad4e..c5abc9e 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java
@@ -41,6 +41,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.skyframe.EvaluationResult;
 import com.google.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyKey;
@@ -245,7 +246,7 @@
   }
 
   private Artifact createSourceArtifact(String path) {
-    return new Artifact(PathFragment.create(path), ArtifactRoot.asSourceRoot(root));
+    return new Artifact(PathFragment.create(path), ArtifactRoot.asSourceRoot(Root.fromPath(root)));
   }
 
   private Artifact createDerivedArtifact(String path) {
@@ -264,7 +265,7 @@
   private Artifact createMiddlemanArtifact(String path) {
     ArtifactRoot middlemanRoot =
         ArtifactRoot.middlemanRoot(middlemanPath, middlemanPath.getRelative("out"));
-    Path fullPath = middlemanRoot.getPath().getRelative(path);
+    Path fullPath = middlemanRoot.getRoot().getRelative(path);
     return new Artifact(
         fullPath, middlemanRoot, fullPath.relativeTo(middlemanRoot.getExecRoot()), ALL_OWNER);
   }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java
index 375c715..2020506 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java
@@ -30,6 +30,7 @@
 import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
 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.inmemoryfs.InMemoryFileSystem;
 import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
 import com.google.devtools.build.skyframe.MemoizingEvaluator;
@@ -73,7 +74,7 @@
         new AtomicReference<>(
             new PathPackageLocator(
                 root.getFileSystem().getPath("/outputbase"),
-                ImmutableList.of(root),
+                ImmutableList.of(Root.fromPath(root)),
                 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
     BlazeDirectories directories =
         new BlazeDirectories(new ServerDirectories(root, root), root, TestConstants.PRODUCT_NAME);
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupFunctionTest.java
index 0728c54..fe2b28c 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupFunctionTest.java
@@ -40,6 +40,7 @@
 import com.google.devtools.build.lib.testutil.FoundationTestCase;
 import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.Root;
 import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
 import com.google.devtools.build.skyframe.MemoizingEvaluator;
 import com.google.devtools.build.skyframe.RecordingDifferencer;
@@ -77,7 +78,7 @@
         new AtomicReference<>(
             new PathPackageLocator(
                 outputBase,
-                ImmutableList.of(rootDirectory),
+                ImmutableList.of(Root.fromPath(rootDirectory)),
                 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
     deletedPackages = new AtomicReference<>(ImmutableSet.<PackageIdentifier>of());
     BlazeDirectories directories =
@@ -178,7 +179,7 @@
     ContainingPackageLookupValue value = lookupContainingPackage("a/b");
     assertThat(value.hasContainingPackage()).isTrue();
     assertThat(value.getContainingPackageName()).isEqualTo(PackageIdentifier.createInMainRepo("a"));
-    assertThat(value.getContainingPackageRoot()).isEqualTo(rootDirectory);
+    assertThat(value.getContainingPackageRoot()).isEqualTo(Root.fromPath(rootDirectory));
   }
 
   @Test
@@ -188,7 +189,7 @@
     assertThat(value.hasContainingPackage()).isTrue();
     assertThat(value.getContainingPackageName())
         .isEqualTo(PackageIdentifier.createInMainRepo("a/b"));
-    assertThat(value.getContainingPackageRoot()).isEqualTo(rootDirectory);
+    assertThat(value.getContainingPackageRoot()).isEqualTo(Root.fromPath(rootDirectory));
   }
 
   @Test
@@ -227,18 +228,18 @@
     ContainingPackageLookupValue valueA2 = ContainingPackageLookupValue.NONE;
     ContainingPackageLookupValue valueB1 =
         ContainingPackageLookupValue.withContainingPackage(
-            PackageIdentifier.createInMainRepo("b"), rootDirectory);
+            PackageIdentifier.createInMainRepo("b"), Root.fromPath(rootDirectory));
     ContainingPackageLookupValue valueB2 =
         ContainingPackageLookupValue.withContainingPackage(
-            PackageIdentifier.createInMainRepo("b"), rootDirectory);
+            PackageIdentifier.createInMainRepo("b"), Root.fromPath(rootDirectory));
     PackageIdentifier cFrag = PackageIdentifier.createInMainRepo("c");
     ContainingPackageLookupValue valueC1 =
-        ContainingPackageLookupValue.withContainingPackage(cFrag, rootDirectory);
+        ContainingPackageLookupValue.withContainingPackage(cFrag, Root.fromPath(rootDirectory));
     ContainingPackageLookupValue valueC2 =
-        ContainingPackageLookupValue.withContainingPackage(cFrag, rootDirectory);
+        ContainingPackageLookupValue.withContainingPackage(cFrag, Root.fromPath(rootDirectory));
     ContainingPackageLookupValue valueCOther =
         ContainingPackageLookupValue.withContainingPackage(
-            cFrag, rootDirectory.getRelative("other_root"));
+            cFrag, Root.fromPath(rootDirectory.getRelative("other_root")));
     new EqualsTester()
         .addEqualityGroup(valueA1, valueA2)
         .addEqualityGroup(valueB1, valueB2)
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/DiffAwarenessManagerTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/DiffAwarenessManagerTest.java
index dee4a46..5dbbaba 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/DiffAwarenessManagerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/DiffAwarenessManagerTest.java
@@ -23,8 +23,8 @@
 import com.google.devtools.build.lib.skyframe.DiffAwarenessManager.ProcessableModifiedFileSet;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.ModifiedFileSet;
-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 com.google.devtools.common.options.OptionsClassProvider;
 import java.util.List;
@@ -57,7 +57,7 @@
 
   @Test
   public void testEverythingModifiedIfNoDiffAwareness() throws Exception {
-    Path pathEntry = fs.getPath("/pathEntry");
+    Root pathEntry = Root.fromPath(fs.getPath("/pathEntry"));
     DiffAwarenessFactoryStub factory = new DiffAwarenessFactoryStub();
     DiffAwarenessManager manager = new DiffAwarenessManager(ImmutableList.of(factory));
     assertWithMessage("Expected EVERYTHING_MODIFIED since there are no factories")
@@ -71,7 +71,7 @@
 
   @Test
   public void testResetAndSetPathEntriesCallClose() throws Exception {
-    Path pathEntry = fs.getPath("/pathEntry");
+    Root pathEntry = Root.fromPath(fs.getPath("/pathEntry"));
     ModifiedFileSet diff = ModifiedFileSet.NOTHING_MODIFIED;
     DiffAwarenessStub diffAwareness1 = new DiffAwarenessStub(ImmutableList.of(diff));
     DiffAwarenessStub diffAwareness2 = new DiffAwarenessStub(ImmutableList.of(diff));
@@ -96,7 +96,7 @@
 
   @Test
   public void testHandlesUnprocessedDiffs() throws Exception {
-    Path pathEntry = fs.getPath("/pathEntry");
+    Root pathEntry = Root.fromPath(fs.getPath("/pathEntry"));
     ModifiedFileSet diff1 = ModifiedFileSet.builder().modify(PathFragment.create("file1")).build();
     ModifiedFileSet diff2 = ModifiedFileSet.builder().modify(PathFragment.create("file2")).build();
     ModifiedFileSet diff3 = ModifiedFileSet.builder().modify(PathFragment.create("file3")).build();
@@ -132,7 +132,7 @@
 
   @Test
   public void testHandlesBrokenDiffs() throws Exception {
-    Path pathEntry = fs.getPath("/pathEntry");
+    Root pathEntry = Root.fromPath(fs.getPath("/pathEntry"));
     DiffAwarenessFactoryStub factory1 = new DiffAwarenessFactoryStub();
     DiffAwarenessStub diffAwareness1 =
         new DiffAwarenessStub(ImmutableList.<ModifiedFileSet>of(), 1);
@@ -196,19 +196,19 @@
 
   private static class DiffAwarenessFactoryStub implements DiffAwareness.Factory {
 
-    private Map<Path, DiffAwareness> diffAwarenesses = Maps.newHashMap();
+    private final Map<Root, DiffAwareness> diffAwarenesses = Maps.newHashMap();
 
-    public void inject(Path pathEntry, DiffAwareness diffAwareness) {
+    public void inject(Root pathEntry, DiffAwareness diffAwareness) {
       diffAwarenesses.put(pathEntry, diffAwareness);
     }
 
-    public void remove(Path pathEntry) {
+    public void remove(Root pathEntry) {
       diffAwarenesses.remove(pathEntry);
     }
 
     @Override
     @Nullable
-    public DiffAwareness maybeCreate(Path pathEntry) {
+    public DiffAwareness maybeCreate(Root pathEntry) {
       return diffAwarenesses.get(pathEntry);
     }
   }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java
index 0a0b460..d0e33b5 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java
@@ -54,6 +54,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.RootedPath;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
 import com.google.devtools.build.lib.vfs.util.FileSystems;
@@ -94,7 +95,7 @@
 @RunWith(JUnit4.class)
 public class FileFunctionTest {
   private CustomInMemoryFs fs;
-  private Path pkgRoot;
+  private Root pkgRoot;
   private Path outputBase;
   private PathPackageLocator pkgLocator;
   private boolean fastDigest;
@@ -110,14 +111,14 @@
 
   private void createFsAndRoot(CustomInMemoryFs fs) throws IOException {
     this.fs = fs;
-    pkgRoot = fs.getPath("/root");
+    pkgRoot = Root.fromPath(fs.getPath("/root"));
     outputBase = fs.getPath("/output_base");
     pkgLocator =
         new PathPackageLocator(
             outputBase,
             ImmutableList.of(pkgRoot),
             BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY);
-    FileSystemUtils.createDirectoryAndParents(pkgRoot);
+    FileSystemUtils.createDirectoryAndParents(pkgRoot.asPath());
   }
 
   private SequentialBuildDriver makeDriver() {
@@ -128,7 +129,9 @@
     AtomicReference<PathPackageLocator> pkgLocatorRef = new AtomicReference<>(pkgLocator);
     BlazeDirectories directories =
         new BlazeDirectories(
-            new ServerDirectories(pkgRoot, outputBase), pkgRoot, TestConstants.PRODUCT_NAME);
+            new ServerDirectories(pkgRoot.asPath(), outputBase),
+            pkgRoot.asPath(),
+            TestConstants.PRODUCT_NAME);
     ExternalFilesHelper externalFilesHelper =
         new ExternalFilesHelper(pkgLocatorRef, externalFileAction, directories);
     differencer = new SequencedRecordingDifferencer();
@@ -183,11 +186,11 @@
   }
 
   private FileValue valueForPathOutsidePkgRoot(Path path) throws InterruptedException {
-    return valueForPathHelper(fs.getRootDirectory(), path);
+    return valueForPathHelper(Root.fromFileSystemRoot(fs), path);
   }
 
-  private FileValue valueForPathHelper(Path root, Path path) throws InterruptedException {
-    PathFragment pathFragment = path.relativeTo(root);
+  private FileValue valueForPathHelper(Root root, Path path) throws InterruptedException {
+    PathFragment pathFragment = root.relativize(path);
     RootedPath rootedPath = RootedPath.toRootedPath(root, pathFragment);
     SequentialBuildDriver driver = makeDriver();
     SkyKey key = FileValue.key(rootedPath);
@@ -312,8 +315,8 @@
         .containsExactly(
             rootedPath("a"),
             rootedPath(""),
-            RootedPath.toRootedPath(fs.getRootDirectory(), PathFragment.EMPTY_FRAGMENT),
-            RootedPath.toRootedPath(fs.getRootDirectory(), PathFragment.create("outside")));
+            RootedPath.toRootedPath(Root.fromFileSystemRoot(fs), PathFragment.EMPTY_FRAGMENT),
+            RootedPath.toRootedPath(Root.fromFileSystemRoot(fs), PathFragment.create("outside")));
   }
 
   @Test
@@ -329,8 +332,8 @@
         .containsExactly(
             rootedPath("a"),
             rootedPath(""),
-            RootedPath.toRootedPath(fs.getRootDirectory(), PathFragment.EMPTY_FRAGMENT),
-            RootedPath.toRootedPath(fs.getRootDirectory(), PathFragment.create("absolute")));
+            RootedPath.toRootedPath(Root.fromFileSystemRoot(fs), PathFragment.EMPTY_FRAGMENT),
+            RootedPath.toRootedPath(Root.fromFileSystemRoot(fs), PathFragment.create("absolute")));
   }
 
   @Test
@@ -344,7 +347,7 @@
     seenFiles.addAll(getFilesSeenAndAssertValueChangesIfContentsOfFileChanges("b", false, "a"));
     seenFiles.addAll(
         getFilesSeenAndAssertValueChangesIfContentsOfFileChanges(externalPath, true, "a"));
-    Path root = fs.getRootDirectory();
+    Root root = Root.fromFileSystemRoot(fs);
     assertThat(seenFiles)
         .containsExactly(
             rootedPath("WORKSPACE"),
@@ -626,7 +629,7 @@
     pkgLocator =
         new PathPackageLocator(
             outputBase,
-            ImmutableList.of(pkgRoot, otherPkgRoot),
+            ImmutableList.of(pkgRoot, Root.fromPath(otherPkgRoot)),
             BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY);
     symlink("a", "/other_root/b");
     assertValueChangesIfContentsOfFileChanges("/other_root/b", true, "a");
@@ -823,7 +826,7 @@
             return super.getDigest(path, hf);
           }
         };
-    pkgRoot = fs.getPath("/root");
+    pkgRoot = Root.fromPath(fs.getPath("/root"));
     Path file = file("file");
     FileSystemUtils.writeContentAsLatin1(file, Strings.repeat("a", 20));
     byte[] digest = file.getDigest();
@@ -1108,7 +1111,7 @@
       // InMemoryFS is not supported for serialization.
       FileSystem fs = FileSystems.getJavaIoFileSystem();
       Path.setFileSystemForSerialization(fs);
-      pkgRoot = fs.getRootDirectory();
+      pkgRoot = Root.fromFileSystemRoot(fs);
 
       FileValue a = valueForPath(fs.getPath("/"));
 
@@ -1175,24 +1178,24 @@
   @Test
   public void testSymlinkToPackagePathBoundary() throws Exception {
     Path path = path("this/is/a/path");
-    FileSystemUtils.ensureSymbolicLink(path, pkgRoot);
+    FileSystemUtils.ensureSymbolicLink(path, pkgRoot.asPath());
     assertError("this/is/a/path");
   }
 
   private void runTestInfiniteSymlinkExpansion(boolean symlinkToAncestor, boolean absoluteSymlink)
       throws Exception {
     Path otherPath = path("other");
-    RootedPath otherRootedPath = RootedPath.toRootedPath(pkgRoot, otherPath.relativeTo(pkgRoot));
+    RootedPath otherRootedPath = RootedPath.toRootedPath(pkgRoot, pkgRoot.relativize(otherPath));
     Path ancestorPath = path("a");
     RootedPath ancestorRootedPath =
-        RootedPath.toRootedPath(pkgRoot, ancestorPath.relativeTo(pkgRoot));
+        RootedPath.toRootedPath(pkgRoot, pkgRoot.relativize(ancestorPath));
     FileSystemUtils.ensureSymbolicLink(otherPath, ancestorPath);
     Path intermediatePath = path("inter");
     RootedPath intermediateRootedPath =
-        RootedPath.toRootedPath(pkgRoot, intermediatePath.relativeTo(pkgRoot));
+        RootedPath.toRootedPath(pkgRoot, pkgRoot.relativize(intermediatePath));
     Path descendantPath = path("a/b/c/d/e");
     RootedPath descendantRootedPath =
-        RootedPath.toRootedPath(pkgRoot, descendantPath.relativeTo(pkgRoot));
+        RootedPath.toRootedPath(pkgRoot, pkgRoot.relativize(descendantPath));
     if (symlinkToAncestor) {
       FileSystemUtils.ensureSymbolicLink(descendantPath, intermediatePath);
       if (absoluteSymlink) {
@@ -1312,7 +1315,7 @@
 
   private void checkRealPath(String pathString) throws Exception {
     Path realPath = pkgRoot.getRelative(pathString).resolveSymbolicLinks();
-    assertRealPath(pathString, realPath.relativeTo(pkgRoot).toString());
+    assertRealPath(pathString, pkgRoot.relativize(realPath).toString());
   }
 
   private void assertRealPath(String pathString, String expectedRealPathString) throws Exception {
@@ -1646,12 +1649,12 @@
 
   private RootedPath rootedPath(String pathString) {
     Path path = path(pathString);
-    for (Path root : pkgLocator.getPathEntries()) {
-      if (path.startsWith(root)) {
+    for (Root root : pkgLocator.getPathEntries()) {
+      if (root.contains(path)) {
         return RootedPath.toRootedPath(root, path);
       }
     }
-    return RootedPath.toRootedPath(fs.getRootDirectory(), path);
+    return RootedPath.toRootedPath(Root.fromFileSystemRoot(fs), path);
   }
 
   private SkyKey skyKey(String pathString) {
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FileSymlinkCycleUniquenessFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FileSymlinkCycleUniquenessFunctionTest.java
index 811afd1..38bffd7 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/FileSymlinkCycleUniquenessFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/FileSymlinkCycleUniquenessFunctionTest.java
@@ -15,8 +15,8 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.testing.EqualsTester;
-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.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
 import org.junit.Test;
@@ -28,7 +28,7 @@
 
   @Test
   public void testHashCodeAndEqualsContract() throws Exception {
-    Path root = new InMemoryFileSystem().getPath("/root");
+    Root root = Root.fromPath(new InMemoryFileSystem().getPath("/root"));
     RootedPath p1 = RootedPath.toRootedPath(root, PathFragment.create("p1"));
     RootedPath p2 = RootedPath.toRootedPath(root, PathFragment.create("p2"));
     RootedPath p3 = RootedPath.toRootedPath(root, PathFragment.create("p3"));
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FilesetEntryFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FilesetEntryFunctionTest.java
index 2671226..20fadee 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/FilesetEntryFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/FilesetEntryFunctionTest.java
@@ -45,6 +45,7 @@
 import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
 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.devtools.build.skyframe.EvaluationResult;
 import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
@@ -84,7 +85,7 @@
         new AtomicReference<>(
             new PathPackageLocator(
                 outputBase,
-                ImmutableList.of(rootDirectory),
+                ImmutableList.of(Root.fromPath(rootDirectory)),
                 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
     AtomicReference<ImmutableSet<PackageIdentifier>> deletedPackages =
         new AtomicReference<>(ImmutableSet.<PackageIdentifier>of());
@@ -129,7 +130,8 @@
   }
 
   private Artifact getSourceArtifact(String path) throws Exception {
-    return new Artifact(PathFragment.create(path), ArtifactRoot.asSourceRoot(rootDirectory));
+    return new Artifact(
+        PathFragment.create(path), ArtifactRoot.asSourceRoot(Root.fromPath(rootDirectory)));
   }
 
   private Artifact createSourceArtifact(String path) throws Exception {
@@ -139,18 +141,18 @@
   }
 
   private static RootedPath rootedPath(Artifact artifact) {
-    return RootedPath.toRootedPath(artifact.getRoot().getPath(), artifact.getRootRelativePath());
+    return RootedPath.toRootedPath(artifact.getRoot().getRoot(), artifact.getRootRelativePath());
   }
 
   private static RootedPath childOf(Artifact artifact, String relative) {
     return RootedPath.toRootedPath(
-        artifact.getRoot().getPath(), artifact.getRootRelativePath().getRelative(relative));
+        artifact.getRoot().getRoot(), artifact.getRootRelativePath().getRelative(relative));
   }
 
   private static RootedPath siblingOf(Artifact artifact, String relative) {
     PathFragment parent =
         Preconditions.checkNotNull(artifact.getRootRelativePath().getParentDirectory());
-    return RootedPath.toRootedPath(artifact.getRoot().getPath(), parent.getRelative(relative));
+    return RootedPath.toRootedPath(artifact.getRoot().getRoot(), parent.getRelative(relative));
   }
 
   private void createFile(Path path, String... contents) throws Exception {
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
index 01ed4fd..851b7a8 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
@@ -50,6 +50,7 @@
 import com.google.devtools.build.lib.vfs.ModifiedFileSet;
 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.devtools.build.lib.vfs.Symlinks;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
@@ -104,7 +105,7 @@
         new AtomicReference<>(
             new PathPackageLocator(
                 fs.getPath("/output_base"),
-                ImmutableList.of(pkgRoot),
+                ImmutableList.of(Root.fromPath(pkgRoot)),
                 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
     BlazeDirectories directories =
         new BlazeDirectories(
@@ -157,8 +158,9 @@
     FileSystemUtils.createEmptyFile(path);
     assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
 
-    SkyKey skyKey = FileStateValue.key(
-        RootedPath.toRootedPath(fs.getRootDirectory(), PathFragment.create("foo")));
+    SkyKey skyKey =
+        FileStateValue.key(
+            RootedPath.toRootedPath(Root.fromFileSystemRoot(fs), PathFragment.create("foo")));
     EvaluationResult<SkyValue> result =
         driver.evaluate(
             ImmutableList.of(skyKey),
@@ -209,13 +211,14 @@
     FileSystemUtils.ensureSymbolicLink(sym1, path);
     FileSystemUtils.ensureSymbolicLink(sym2, path);
     SkyKey fooKey =
-        FileValue.key(RootedPath.toRootedPath(fs.getRootDirectory(), PathFragment.create("foo")));
+        FileValue.key(
+            RootedPath.toRootedPath(Root.fromFileSystemRoot(fs), PathFragment.create("foo")));
     RootedPath symlinkRootedPath =
-        RootedPath.toRootedPath(fs.getRootDirectory(), PathFragment.create("bar"));
+        RootedPath.toRootedPath(Root.fromFileSystemRoot(fs), PathFragment.create("bar"));
     SkyKey symlinkKey = FileValue.key(symlinkRootedPath);
     SkyKey symlinkFileStateKey = FileStateValue.key(symlinkRootedPath);
     RootedPath sym1RootedPath =
-        RootedPath.toRootedPath(fs.getRootDirectory(), PathFragment.create("sym1"));
+        RootedPath.toRootedPath(Root.fromFileSystemRoot(fs), PathFragment.create("sym1"));
     SkyKey sym1FileStateKey = FileStateValue.key(sym1RootedPath);
     Iterable<SkyKey> allKeys = ImmutableList.of(symlinkKey, fooKey);
 
@@ -277,10 +280,10 @@
 
     SkyKey key1 =
         FileStateValue.key(
-            RootedPath.toRootedPath(fs.getRootDirectory(), PathFragment.create("foo1")));
+            RootedPath.toRootedPath(Root.fromFileSystemRoot(fs), PathFragment.create("foo1")));
     SkyKey key2 =
         FileStateValue.key(
-            RootedPath.toRootedPath(fs.getRootDirectory(), PathFragment.create("foo2")));
+            RootedPath.toRootedPath(Root.fromFileSystemRoot(fs), PathFragment.create("foo2")));
     Iterable<SkyKey> skyKeys = ImmutableList.of(key1, key2);
     EvaluationResult<SkyValue> result =
         driver.evaluate(
@@ -310,8 +313,9 @@
     path.createSymbolicLink(PathFragment.create("bar"));
 
     fs.readlinkThrowsIoException = true;
-    SkyKey fileKey = FileStateValue.key(
-        RootedPath.toRootedPath(pkgRoot, PathFragment.create("foo")));
+    SkyKey fileKey =
+        FileStateValue.key(
+            RootedPath.toRootedPath(Root.fromPath(pkgRoot), PathFragment.create("foo")));
     EvaluationResult<SkyValue> result =
         driver.evaluate(
             ImmutableList.of(fileKey),
@@ -335,7 +339,7 @@
     FileSystemUtils.ensureSymbolicLink(path1, path2);
     FileSystemUtils.ensureSymbolicLink(path2, path3);
     FileSystemUtils.ensureSymbolicLink(path3, path1);
-    SkyKey fileKey1 = FileValue.key(RootedPath.toRootedPath(pkgRoot, path1));
+    SkyKey fileKey1 = FileValue.key(RootedPath.toRootedPath(Root.fromPath(pkgRoot), path1));
 
     EvaluationResult<SkyValue> result =
         driver.evaluate(
@@ -616,9 +620,12 @@
     Path outputPath = outputDir.getRelative(relPath);
     outputDir.createDirectory();
     ArtifactRoot derivedRoot = ArtifactRoot.asDerivedRoot(fs.getPath("/"), outputDir);
-    return new SpecialArtifact(outputPath, derivedRoot,
-        derivedRoot.getExecPath().getRelative(outputPath.relativeTo(derivedRoot.getPath())),
-        ArtifactOwner.NULL_OWNER, SpecialArtifactType.TREE);
+    return new SpecialArtifact(
+        outputPath,
+        derivedRoot,
+        derivedRoot.getExecPath().getRelative(derivedRoot.getRoot().relativize(outputPath)),
+        ArtifactOwner.NULL_OWNER,
+        SpecialArtifactType.TREE);
   }
 
   @Test
@@ -767,8 +774,10 @@
 
   @Test
   public void testPropagatesRuntimeExceptions() throws Exception {
-    Collection<SkyKey> values = ImmutableList.of(
-        FileValue.key(RootedPath.toRootedPath(pkgRoot, PathFragment.create("foo"))));
+    Collection<SkyKey> values =
+        ImmutableList.of(
+            FileValue.key(
+                RootedPath.toRootedPath(Root.fromPath(pkgRoot), PathFragment.create("foo"))));
     driver.evaluate(
         values, false, SkyframeExecutor.DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
     FilesystemValueChecker checker = new FilesystemValueChecker(null, null);
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/GlobDescriptorTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/GlobDescriptorTest.java
index 3601eaa..e9605e3 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/GlobDescriptorTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/GlobDescriptorTest.java
@@ -21,6 +21,7 @@
 import com.google.devtools.build.lib.skyframe.serialization.testutils.ObjectCodecTester;
 import com.google.devtools.build.lib.vfs.PathCodec;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.Root;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -31,33 +32,34 @@
 
   @Test
   public void testSerialization() throws Exception {
-    ObjectCodecTester.newBuilder(GlobDescriptor.getCodec(new PathCodec(FsUtils.TEST_FILESYSTEM)))
+    ObjectCodecTester.newBuilder(
+            GlobDescriptor.getCodec(Root.getCodec(new PathCodec(FsUtils.TEST_FILESYSTEM))))
         .addSubjects(
             GlobDescriptor.create(
                 PackageIdentifier.create("@foo", PathFragment.create("//bar")),
-                FsUtils.TEST_FILESYSTEM.getPath("/packageRoot"),
+                Root.fromPath(FsUtils.TEST_FILESYSTEM.getPath("/packageRoot")),
                 PathFragment.create("subdir"),
                 "pattern",
                 /*excludeDirs=*/ false),
             GlobDescriptor.create(
                 PackageIdentifier.create("@bar", PathFragment.create("//foo")),
-                FsUtils.TEST_FILESYSTEM.getPath("/anotherPackageRoot"),
+                Root.fromPath(FsUtils.TEST_FILESYSTEM.getPath("/anotherPackageRoot")),
                 PathFragment.create("anotherSubdir"),
                 "pattern",
                 /*excludeDirs=*/ true))
-        .verificationFunction(
-            (orig, deserialized) -> assertThat(deserialized).isSameAs(orig))
+        .verificationFunction((orig, deserialized) -> assertThat(deserialized).isSameAs(orig))
         .buildAndRunTests();
   }
 
   @Test
   public void testCreateReturnsInternedInstances() throws LabelSyntaxException {
-    GlobDescriptor original = GlobDescriptor.create(
-        PackageIdentifier.create("@foo", PathFragment.create("//bar")),
-        FsUtils.TEST_FILESYSTEM.getPath("/packageRoot"),
-        PathFragment.create("subdir"),
-        "pattern",
-        /*excludeDirs=*/ false);
+    GlobDescriptor original =
+        GlobDescriptor.create(
+            PackageIdentifier.create("@foo", PathFragment.create("//bar")),
+            Root.fromPath(FsUtils.TEST_FILESYSTEM.getPath("/packageRoot")),
+            PathFragment.create("subdir"),
+            "pattern",
+            /*excludeDirs=*/ false);
 
     GlobDescriptor sameCopy = GlobDescriptor.create(
         original.getPackageId(),
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java
index 51cf1cf..ed8a74d 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java
@@ -45,6 +45,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.RootedPath;
 import com.google.devtools.build.lib.vfs.UnixGlob;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
@@ -114,7 +115,7 @@
         new AtomicReference<>(
             new PathPackageLocator(
                 outputBase,
-                ImmutableList.of(writableRoot, root),
+                ImmutableList.of(Root.fromPath(writableRoot), Root.fromPath(root)),
                 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
 
     differencer = new SequencedRecordingDifferencer();
@@ -411,7 +412,9 @@
   }
 
   private GlobValue runGlob(boolean excludeDirs, String pattern) throws Exception {
-    SkyKey skyKey = GlobValue.key(PKG_ID, root, pattern, excludeDirs, PathFragment.EMPTY_FRAGMENT);
+    SkyKey skyKey =
+        GlobValue.key(
+            PKG_ID, Root.fromPath(root), pattern, excludeDirs, PathFragment.EMPTY_FRAGMENT);
     EvaluationResult<SkyValue> result =
         driver.evaluate(
             ImmutableList.of(skyKey),
@@ -438,28 +441,32 @@
       differencer.invalidate(
           ImmutableList.of(
               FileStateValue.key(
-                  RootedPath.toRootedPath(root, pkgPath.getRelative("foo/bar/wiz/file")))));
+                  RootedPath.toRootedPath(
+                      Root.fromPath(root), pkgPath.getRelative("foo/bar/wiz/file")))));
       // The result should not rely on the FileStateValue, so it's still a cache hit.
       assertGlobMatches(pattern, "foo/bar/wiz/file");
 
       differencer.invalidate(
           ImmutableList.of(
               DirectoryListingStateValue.key(
-                  RootedPath.toRootedPath(root, pkgPath.getRelative("foo/bar/wiz")))));
+                  RootedPath.toRootedPath(
+                      Root.fromPath(root), pkgPath.getRelative("foo/bar/wiz")))));
       // This should have invalidated the glob result.
       assertGlobMatches(pattern /* => nothing */);
     } else {
       differencer.invalidate(
           ImmutableList.of(
               DirectoryListingStateValue.key(
-                  RootedPath.toRootedPath(root, pkgPath.getRelative("foo/bar/wiz")))));
+                  RootedPath.toRootedPath(
+                      Root.fromPath(root), pkgPath.getRelative("foo/bar/wiz")))));
       // The result should not rely on the DirectoryListingValue, so it's still a cache hit.
       assertGlobMatches(pattern, "foo/bar/wiz/file");
 
       differencer.invalidate(
           ImmutableList.of(
               FileStateValue.key(
-                  RootedPath.toRootedPath(root, pkgPath.getRelative("foo/bar/wiz/file")))));
+                  RootedPath.toRootedPath(
+                      Root.fromPath(root), pkgPath.getRelative("foo/bar/wiz/file")))));
       // This should have invalidated the glob result.
       assertGlobMatches(pattern /* => nothing */);
     }
@@ -492,7 +499,7 @@
 
   private void assertIllegalPattern(String pattern) {
     try {
-      GlobValue.key(PKG_ID, root, pattern, false, PathFragment.EMPTY_FRAGMENT);
+      GlobValue.key(PKG_ID, Root.fromPath(root), pattern, false, PathFragment.EMPTY_FRAGMENT);
       fail("invalid pattern not detected: " + pattern);
     } catch (InvalidGlobPatternException e) {
       // Expected.
@@ -630,13 +637,14 @@
   public void testResilienceToFilesystemInconsistencies_DirectoryExistence() throws Exception {
     // Our custom filesystem says "pkgPath/BUILD" exists but "pkgPath" does not exist.
     fs.stubStat(pkgPath, null);
-    RootedPath pkgRootedPath = RootedPath.toRootedPath(root, pkgPath);
+    RootedPath pkgRootedPath = RootedPath.toRootedPath(Root.fromPath(root), pkgPath);
     FileStateValue pkgDirFileStateValue = FileStateValue.create(pkgRootedPath, null);
     FileValue pkgDirValue =
         FileValue.value(pkgRootedPath, pkgDirFileStateValue, pkgRootedPath, pkgDirFileStateValue);
     differencer.inject(ImmutableMap.of(FileValue.key(pkgRootedPath), pkgDirValue));
     String expectedMessage = "/root/workspace/pkg is no longer an existing directory";
-    SkyKey skyKey = GlobValue.key(PKG_ID, root, "*/foo", false, PathFragment.EMPTY_FRAGMENT);
+    SkyKey skyKey =
+        GlobValue.key(PKG_ID, Root.fromPath(root), "*/foo", false, PathFragment.EMPTY_FRAGMENT);
     EvaluationResult<GlobValue> result =
         driver.evaluate(
             ImmutableList.of(skyKey),
@@ -655,7 +663,7 @@
     // direct stat on "pkgPath/foo/bar/wiz" says it does not exist.
     Path fooBarDir = pkgPath.getRelative("foo/bar");
     fs.stubStat(fooBarDir.getRelative("wiz"), null);
-    RootedPath fooBarDirRootedPath = RootedPath.toRootedPath(root, fooBarDir);
+    RootedPath fooBarDirRootedPath = RootedPath.toRootedPath(Root.fromPath(root), fooBarDir);
     SkyValue fooBarDirListingValue =
         DirectoryListingStateValue.create(
             ImmutableList.of(new Dirent("wiz", Dirent.Type.DIRECTORY)));
@@ -663,7 +671,8 @@
         ImmutableMap.of(
             DirectoryListingStateValue.key(fooBarDirRootedPath), fooBarDirListingValue));
     String expectedMessage = "/root/workspace/pkg/foo/bar/wiz is no longer an existing directory.";
-    SkyKey skyKey = GlobValue.key(PKG_ID, root, "**/wiz", false, PathFragment.EMPTY_FRAGMENT);
+    SkyKey skyKey =
+        GlobValue.key(PKG_ID, Root.fromPath(root), "**/wiz", false, PathFragment.EMPTY_FRAGMENT);
     EvaluationResult<GlobValue> result =
         driver.evaluate(
             ImmutableList.of(skyKey),
@@ -678,9 +687,10 @@
 
   @Test
   public void testResilienceToFilesystemInconsistencies_SymlinkType() throws Exception {
-    RootedPath wizRootedPath = RootedPath.toRootedPath(root, pkgPath.getRelative("foo/bar/wiz"));
+    RootedPath wizRootedPath =
+        RootedPath.toRootedPath(Root.fromPath(root), pkgPath.getRelative("foo/bar/wiz"));
     RootedPath fileRootedPath =
-        RootedPath.toRootedPath(root, pkgPath.getRelative("foo/bar/wiz/file"));
+        RootedPath.toRootedPath(Root.fromPath(root), pkgPath.getRelative("foo/bar/wiz/file"));
     final FileStatus realStat = fileRootedPath.asPath().stat();
     fs.stubStat(
         fileRootedPath.asPath(),
@@ -735,8 +745,9 @@
         ImmutableMap.of(DirectoryListingStateValue.key(wizRootedPath), wizDirListingValue));
     String expectedMessage =
         "readdir and stat disagree about whether " + fileRootedPath.asPath() + " is a symlink";
-    SkyKey skyKey = GlobValue.key(PKG_ID, root, "foo/bar/wiz/*", false,
-        PathFragment.EMPTY_FRAGMENT);
+    SkyKey skyKey =
+        GlobValue.key(
+            PKG_ID, Root.fromPath(root), "foo/bar/wiz/*", false, PathFragment.EMPTY_FRAGMENT);
     EvaluationResult<GlobValue> result =
         driver.evaluate(
             ImmutableList.of(skyKey),
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/LocalRepositoryLookupFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/LocalRepositoryLookupFunctionTest.java
index a854d2e..2445058 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/LocalRepositoryLookupFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/LocalRepositoryLookupFunctionTest.java
@@ -36,6 +36,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.RootedPath;
 import com.google.devtools.build.skyframe.EvaluationResult;
 import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
@@ -69,7 +70,7 @@
         new AtomicReference<>(
             new PathPackageLocator(
                 outputBase,
-                ImmutableList.of(rootDirectory),
+                ImmutableList.of(Root.fromPath(rootDirectory)),
                 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
     deletedPackages = new AtomicReference<>(ImmutableSet.<PackageIdentifier>of());
     BlazeDirectories directories =
@@ -143,7 +144,8 @@
   @Test
   public void testNoPath() throws Exception {
     LocalRepositoryLookupValue repositoryLookupValue =
-        lookupDirectory(RootedPath.toRootedPath(rootDirectory, PathFragment.EMPTY_FRAGMENT));
+        lookupDirectory(
+            RootedPath.toRootedPath(Root.fromPath(rootDirectory), PathFragment.EMPTY_FRAGMENT));
     assertThat(repositoryLookupValue).isNotNull();
     assertThat(repositoryLookupValue.getRepository()).isEqualTo(RepositoryName.MAIN);
     assertThat(repositoryLookupValue.getPath()).isEqualTo(PathFragment.EMPTY_FRAGMENT);
@@ -154,7 +156,9 @@
     scratch.file("some/path/BUILD");
 
     LocalRepositoryLookupValue repositoryLookupValue =
-        lookupDirectory(RootedPath.toRootedPath(rootDirectory, PathFragment.create("some/path")));
+        lookupDirectory(
+            RootedPath.toRootedPath(
+                Root.fromPath(rootDirectory), PathFragment.create("some/path")));
     assertThat(repositoryLookupValue).isNotNull();
     assertThat(repositoryLookupValue.getRepository()).isEqualTo(RepositoryName.MAIN);
     assertThat(repositoryLookupValue.getPath()).isEqualTo(PathFragment.EMPTY_FRAGMENT);
@@ -167,7 +171,9 @@
     scratch.file("local/repo/BUILD");
 
     LocalRepositoryLookupValue repositoryLookupValue =
-        lookupDirectory(RootedPath.toRootedPath(rootDirectory, PathFragment.create("local/repo")));
+        lookupDirectory(
+            RootedPath.toRootedPath(
+                Root.fromPath(rootDirectory), PathFragment.create("local/repo")));
     assertThat(repositoryLookupValue).isNotNull();
     assertThat(repositoryLookupValue.getRepository().getName()).isEqualTo("@local");
     assertThat(repositoryLookupValue.getPath()).isEqualTo(PathFragment.create("local/repo"));
@@ -182,7 +188,8 @@
     LocalRepositoryLookupValue repositoryLookupValue =
         lookupDirectory(
             RootedPath.toRootedPath(
-                rootDirectory.getRelative("/abs"), PathFragment.create("local/repo")));
+                Root.fromPath(rootDirectory.getRelative("/abs")),
+                PathFragment.create("local/repo")));
     assertThat(repositoryLookupValue).isNotNull();
     assertThat(repositoryLookupValue.getRepository().getName()).isEqualTo("@local");
     assertThat(repositoryLookupValue.getPath()).isEqualTo(PathFragment.create("/abs/local/repo"));
@@ -195,7 +202,9 @@
     scratch.file("local/repo/BUILD");
 
     LocalRepositoryLookupValue repositoryLookupValue =
-        lookupDirectory(RootedPath.toRootedPath(rootDirectory, PathFragment.create("local/repo")));
+        lookupDirectory(
+            RootedPath.toRootedPath(
+                Root.fromPath(rootDirectory), PathFragment.create("local/repo")));
     assertThat(repositoryLookupValue).isNotNull();
     assertThat(repositoryLookupValue.getRepository().getName()).isEqualTo("@local");
     assertThat(repositoryLookupValue.getPath()).isEqualTo(PathFragment.create("local/repo"));
@@ -210,7 +219,8 @@
     LocalRepositoryLookupValue repositoryLookupValue =
         lookupDirectory(
             RootedPath.toRootedPath(
-                rootDirectory.getRelative("/abs"), PathFragment.create("local/repo")));
+                Root.fromPath(rootDirectory.getRelative("/abs")),
+                PathFragment.create("local/repo")));
     assertThat(repositoryLookupValue).isNotNull();
     assertThat(repositoryLookupValue.getRepository().getName()).isEqualTo("@local");
     assertThat(repositoryLookupValue.getPath()).isEqualTo(PathFragment.create("/abs/local/repo"));
@@ -225,7 +235,8 @@
 
     LocalRepositoryLookupValue repositoryLookupValue =
         lookupDirectory(
-            RootedPath.toRootedPath(rootDirectory, PathFragment.create("local/repo/sub/package")));
+            RootedPath.toRootedPath(
+                Root.fromPath(rootDirectory), PathFragment.create("local/repo/sub/package")));
     assertThat(repositoryLookupValue).isNotNull();
     assertThat(repositoryLookupValue.getRepository().getName()).isEqualTo("@local");
     assertThat(repositoryLookupValue.getPath()).isEqualTo(PathFragment.create("local/repo"));
@@ -238,7 +249,9 @@
     scratch.file("local/repo/BUILD");
 
     LocalRepositoryLookupValue repositoryLookupValue =
-        lookupDirectory(RootedPath.toRootedPath(rootDirectory, PathFragment.create("local/repo")));
+        lookupDirectory(
+            RootedPath.toRootedPath(
+                Root.fromPath(rootDirectory), PathFragment.create("local/repo")));
     assertThat(repositoryLookupValue).isNotNull();
     assertThat(repositoryLookupValue.getRepository()).isEqualTo(RepositoryName.MAIN);
     assertThat(repositoryLookupValue.getPath()).isEqualTo(PathFragment.EMPTY_FRAGMENT);
@@ -256,7 +269,9 @@
     scratch.file("local/repo/BUILD");
 
     SkyKey localRepositoryKey =
-        createKey(RootedPath.toRootedPath(rootDirectory, PathFragment.create("local/repo")));
+        createKey(
+            RootedPath.toRootedPath(
+                Root.fromPath(rootDirectory), PathFragment.create("local/repo")));
     EvaluationResult<LocalRepositoryLookupValue> result = lookupDirectory(localRepositoryKey);
 
     assertThatEvaluationResult(result)
@@ -276,7 +291,9 @@
     scratch.file("local/repo/BUILD");
 
     LocalRepositoryLookupValue repositoryLookupValue =
-        lookupDirectory(RootedPath.toRootedPath(rootDirectory, PathFragment.create("local/repo")));
+        lookupDirectory(
+            RootedPath.toRootedPath(
+                Root.fromPath(rootDirectory), PathFragment.create("local/repo")));
     assertThat(repositoryLookupValue).isNotNull();
     // In this case, the repository should be MAIN as we can't find any local_repository rules.
     assertThat(repositoryLookupValue.getRepository()).isEqualTo(RepositoryName.MAIN);
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java
index 8773d54..07c7062 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java
@@ -42,6 +42,7 @@
 import com.google.devtools.build.lib.vfs.ModifiedFileSet;
 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.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
 import com.google.devtools.build.skyframe.ErrorInfo;
@@ -53,6 +54,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -81,7 +83,7 @@
         .preparePackageLoading(
             new PathPackageLocator(
                 outputBase,
-                ImmutableList.copyOf(roots),
+                Arrays.stream(roots).map(Root::fromPath).collect(ImmutableList.toImmutableList()),
                 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY),
             packageCacheOptions,
             Options.getDefaults(SkylarkSemanticsOptions.class),
@@ -118,7 +120,7 @@
   public void testPropagatesFilesystemInconsistencies() throws Exception {
     reporter.removeHandler(failFastHandler);
     RecordingDifferencer differencer = getSkyframeExecutor().getDifferencerForTesting();
-    Path pkgRoot = getSkyframeExecutor().getPathEntries().get(0);
+    Root pkgRoot = getSkyframeExecutor().getPathEntries().get(0);
     Path fooBuildFile = scratch.file("foo/BUILD");
     Path fooDir = fooBuildFile.getParentDirectory();
 
@@ -184,7 +186,7 @@
   public void testPropagatesFilesystemInconsistencies_Globbing() throws Exception {
     reporter.removeHandler(failFastHandler);
     RecordingDifferencer differencer = getSkyframeExecutor().getDifferencerForTesting();
-    Path pkgRoot = getSkyframeExecutor().getPathEntries().get(0);
+    Root pkgRoot = getSkyframeExecutor().getPathEntries().get(0);
     scratch.file("foo/BUILD",
         "subinclude('//a:a')",
         "sh_library(name = 'foo', srcs = glob(['bar/**/baz.sh']))");
@@ -264,7 +266,7 @@
         .invalidateFilesUnderPathForTesting(
             reporter,
             ModifiedFileSet.builder().modify(PathFragment.create("foo/d.txt")).build(),
-            rootDirectory);
+            Root.fromPath(rootDirectory));
     value = validPackage(skyKey);
     assertThat(
             (Iterable<Label>)
@@ -295,7 +297,7 @@
         .invalidateFilesUnderPathForTesting(
             reporter,
             ModifiedFileSet.builder().modify(PathFragment.create("foo/BUILD")).build(),
-            rootDirectory);
+            Root.fromPath(rootDirectory));
     assertSrcs(validPackage(skyKey), "foo", "//foo:a.config", "//foo:b.txt");
     scratch.overwriteFile(
         "foo/BUILD", "sh_library(name = 'foo', srcs = glob(['*.txt', '*.config'])) # comment");
@@ -303,7 +305,7 @@
         .invalidateFilesUnderPathForTesting(
             reporter,
             ModifiedFileSet.builder().modify(PathFragment.create("foo/BUILD")).build(),
-            rootDirectory);
+            Root.fromPath(rootDirectory));
     assertSrcs(validPackage(skyKey), "foo", "//foo:a.config", "//foo:b.txt");
     getSkyframeExecutor().resetEvaluator();
     PackageCacheOptions packageCacheOptions = Options.getDefaults(PackageCacheOptions.class);
@@ -314,7 +316,7 @@
         .preparePackageLoading(
             new PathPackageLocator(
                 outputBase,
-                ImmutableList.<Path>of(rootDirectory),
+                ImmutableList.of(Root.fromPath(rootDirectory)),
                 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY),
             packageCacheOptions,
             Options.getDefaults(SkylarkSemanticsOptions.class),
@@ -361,7 +363,7 @@
         .invalidateFilesUnderPathForTesting(
             reporter,
             ModifiedFileSet.builder().modify(PathFragment.create("foo/BUILD")).build(),
-            rootDirectory);
+            Root.fromPath(rootDirectory));
     PackageValue fooValue2 = validPackage(fooKey);
     assertThat(fooValue2).isNotEqualTo(fooValue);
     assertSrcs(fooValue2, "foo", "//foo:link.sh", "//foo:ordinary.sh");
@@ -403,7 +405,7 @@
         .invalidateFilesUnderPathForTesting(
             reporter,
             ModifiedFileSet.builder().modify(PathFragment.create("foo/irrelevant")).build(),
-            rootDirectory);
+            Root.fromPath(rootDirectory));
     assertThat(validPackage(skyKey)).isSameAs(value);
   }
 
@@ -421,7 +423,7 @@
         .invalidateFilesUnderPathForTesting(
             reporter,
             ModifiedFileSet.builder().modify(PathFragment.create("foo/irrelevant")).build(),
-            rootDirectory);
+            Root.fromPath(rootDirectory));
     assertThat(validPackage(skyKey)).isSameAs(value);
   }
 
@@ -450,10 +452,11 @@
     scratch.overwriteFile("bar/ext.bzl",
         "load('//qux:ext.bzl', 'c')",
         "a = c");
-    getSkyframeExecutor().invalidateFilesUnderPathForTesting(
-        reporter,
-        ModifiedFileSet.builder().modify(PathFragment.create("bar/ext.bzl")).build(),
-        rootDirectory);
+    getSkyframeExecutor()
+        .invalidateFilesUnderPathForTesting(
+            reporter,
+            ModifiedFileSet.builder().modify(PathFragment.create("bar/ext.bzl")).build(),
+            Root.fromPath(rootDirectory));
 
     value = validPackage(skyKey);
     assertThat(value.getPackage().getSkylarkFileDependencies()).containsExactly(
@@ -569,7 +572,7 @@
             Predicates.equalTo(
                 com.google.devtools.build.lib.skyframe.FileStateValue.key(
                     RootedPath.toRootedPath(
-                        workspacePath.getParentDirectory(),
+                        Root.fromPath(workspacePath.getParentDirectory()),
                         PathFragment.create(workspacePath.getBaseName())))));
 
     reporter.removeHandler(failFastHandler);
@@ -605,8 +608,11 @@
         "exports_files(glob(['*.txt']))",
         "#some-irrelevant-comment");
 
-    getSkyframeExecutor().invalidateFilesUnderPathForTesting(reporter,
-        ModifiedFileSet.builder().modify(PathFragment.create("foo/BUILD")).build(), rootDirectory);
+    getSkyframeExecutor()
+        .invalidateFilesUnderPathForTesting(
+            reporter,
+            ModifiedFileSet.builder().modify(PathFragment.create("foo/BUILD")).build(),
+            Root.fromPath(rootDirectory));
 
     value = validPackage(skyKey);
     assertThat(value.getPackage().containsErrors()).isFalse();
@@ -621,8 +627,11 @@
     }
 
     scratch.file("foo/nope");
-    getSkyframeExecutor().invalidateFilesUnderPathForTesting(reporter,
-        ModifiedFileSet.builder().modify(PathFragment.create("foo/nope")).build(), rootDirectory);
+    getSkyframeExecutor()
+        .invalidateFilesUnderPathForTesting(
+            reporter,
+            ModifiedFileSet.builder().modify(PathFragment.create("foo/nope")).build(),
+            Root.fromPath(rootDirectory));
 
     PackageValue newValue = validPackage(skyKey);
     assertThat(newValue.getPackage().containsErrors()).isFalse();
@@ -657,8 +666,11 @@
     scratch.overwriteFile("foo/BUILD",
         "[sh_library(name = x + '-matched') for x in glob(['**'], exclude_directories = 0)]",
         "#some-irrelevant-comment");
-    getSkyframeExecutor().invalidateFilesUnderPathForTesting(reporter,
-        ModifiedFileSet.builder().modify(PathFragment.create("foo/BUILD")).build(), rootDirectory);
+    getSkyframeExecutor()
+        .invalidateFilesUnderPathForTesting(
+            reporter,
+            ModifiedFileSet.builder().modify(PathFragment.create("foo/BUILD")).build(),
+            Root.fromPath(rootDirectory));
 
     value = validPackage(skyKey);
     assertThat(value.getPackage().containsErrors()).isFalse();
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/PackageLookupFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/PackageLookupFunctionTest.java
index d9a3f5a..6f34ab6 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/PackageLookupFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/PackageLookupFunctionTest.java
@@ -48,6 +48,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.RootedPath;
 import com.google.devtools.build.skyframe.EvaluationResult;
 import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
@@ -94,7 +95,7 @@
         new AtomicReference<>(
             new PathPackageLocator(
                 outputBase,
-                ImmutableList.of(emptyPackagePath, rootDirectory),
+                ImmutableList.of(Root.fromPath(emptyPackagePath), Root.fromPath(rootDirectory)),
                 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
     deletedPackages = new AtomicReference<>(ImmutableSet.<PackageIdentifier>of());
     BlazeDirectories directories =
@@ -228,9 +229,10 @@
 
     scratch.overwriteFile(
         ADDITIONAL_BLACKLISTED_PACKAGE_PREFIXES_FILE_PATH_STRING, "not_blacklisted");
-    RootedPath rootedBlacklist = RootedPath.toRootedPath(
-        blacklist.getParentDirectory().getParentDirectory(),
-        PathFragment.create("config/blacklisted.txt"));
+    RootedPath rootedBlacklist =
+        RootedPath.toRootedPath(
+            Root.fromPath(blacklist.getParentDirectory().getParentDirectory()),
+            PathFragment.create("config/blacklisted.txt"));
     differencer.invalidate(ImmutableSet.of(FileStateValue.key(rootedBlacklist)));
     for (String pkg : pkgs) {
       PackageLookupValue packageLookupValue = lookupPackage(pkg);
@@ -261,7 +263,7 @@
     scratch.file("parentpackage/everythinggood/BUILD");
     PackageLookupValue packageLookupValue = lookupPackage("parentpackage/everythinggood");
     assertThat(packageLookupValue.packageExists()).isTrue();
-    assertThat(packageLookupValue.getRoot()).isEqualTo(rootDirectory);
+    assertThat(packageLookupValue.getRoot()).isEqualTo(Root.fromPath(rootDirectory));
     assertThat(packageLookupValue.getBuildFileName()).isEqualTo(BuildFileName.BUILD);
   }
 
@@ -270,7 +272,7 @@
     scratch.file("parentpackage/everythinggood/BUILD.bazel");
     PackageLookupValue packageLookupValue = lookupPackage("parentpackage/everythinggood");
     assertThat(packageLookupValue.packageExists()).isTrue();
-    assertThat(packageLookupValue.getRoot()).isEqualTo(rootDirectory);
+    assertThat(packageLookupValue.getRoot()).isEqualTo(Root.fromPath(rootDirectory));
     assertThat(packageLookupValue.getBuildFileName()).isEqualTo(BuildFileName.BUILD_DOT_BAZEL);
   }
 
@@ -280,7 +282,7 @@
     scratch.file("parentpackage/everythinggood/BUILD.bazel");
     PackageLookupValue packageLookupValue = lookupPackage("parentpackage/everythinggood");
     assertThat(packageLookupValue.packageExists()).isTrue();
-    assertThat(packageLookupValue.getRoot()).isEqualTo(rootDirectory);
+    assertThat(packageLookupValue.getRoot()).isEqualTo(Root.fromPath(rootDirectory));
     assertThat(packageLookupValue.getBuildFileName()).isEqualTo(BuildFileName.BUILD_DOT_BAZEL);
   }
 
@@ -292,7 +294,7 @@
     // BUILD file in the first package path should be preferred to BUILD.bazel in the second.
     PackageLookupValue packageLookupValue = lookupPackage("foo");
     assertThat(packageLookupValue.packageExists()).isTrue();
-    assertThat(packageLookupValue.getRoot()).isEqualTo(emptyPackagePath);
+    assertThat(packageLookupValue.getRoot()).isEqualTo(Root.fromPath(emptyPackagePath));
     assertThat(packageLookupValue.getBuildFileName()).isEqualTo(BuildFileName.BUILD);
   }
 
@@ -301,7 +303,7 @@
     scratch.file("BUILD");
     PackageLookupValue packageLookupValue = lookupPackage("");
     assertThat(packageLookupValue.packageExists()).isTrue();
-    assertThat(packageLookupValue.getRoot()).isEqualTo(rootDirectory);
+    assertThat(packageLookupValue.getRoot()).isEqualTo(Root.fromPath(rootDirectory));
     assertThat(packageLookupValue.getBuildFileName()).isEqualTo(BuildFileName.BUILD);
   }
 
@@ -311,13 +313,13 @@
     PackageLookupValue packageLookupValue = lookupPackage(
         PackageIdentifier.createInMainRepo("external"));
     assertThat(packageLookupValue.packageExists()).isTrue();
-    assertThat(packageLookupValue.getRoot()).isEqualTo(rootDirectory);
+    assertThat(packageLookupValue.getRoot()).isEqualTo(Root.fromPath(rootDirectory));
   }
 
   @Test
   public void testPackageLookupValueHashCodeAndEqualsContract() throws Exception {
-    Path root1 = rootDirectory.getRelative("root1");
-    Path root2 = rootDirectory.getRelative("root2");
+    Root root1 = Root.fromPath(rootDirectory.getRelative("root1"));
+    Root root2 = Root.fromPath(rootDirectory.getRelative("root2"));
     // Our (seeming) duplication of parameters here is intentional. Some of the subclasses of
     // PackageLookupValue are supposed to have reference equality semantics, and some are supposed
     // to have logical equality semantics.
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/PrepareDepsOfPatternsFunctionSmartNegationTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/PrepareDepsOfPatternsFunctionSmartNegationTest.java
index 2f43b7c0..fa745c6 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/PrepareDepsOfPatternsFunctionSmartNegationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/PrepareDepsOfPatternsFunctionSmartNegationTest.java
@@ -36,6 +36,7 @@
 import com.google.devtools.build.lib.testutil.TestConstants;
 import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.Root;
 import com.google.devtools.build.skyframe.EvaluationResult;
 import com.google.devtools.build.skyframe.SkyKey;
 import com.google.devtools.build.skyframe.SkyValue;
@@ -91,7 +92,7 @@
     skyframeExecutor.preparePackageLoading(
         new PathPackageLocator(
             outputBase,
-            ImmutableList.of(rootDirectory),
+            ImmutableList.of(Root.fromPath(rootDirectory)),
             BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY),
         Options.getDefaults(PackageCacheOptions.class),
         Options.getDefaults(SkylarkSemanticsOptions.class),
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/PrepareDepsOfTargetsUnderDirectoryFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/PrepareDepsOfTargetsUnderDirectoryFunctionTest.java
index be3b163..49d502b 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/PrepareDepsOfTargetsUnderDirectoryFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/PrepareDepsOfTargetsUnderDirectoryFunctionTest.java
@@ -28,6 +28,7 @@
 import com.google.devtools.build.lib.pkgcache.FilteringPolicy;
 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.devtools.build.skyframe.BuildDriver;
 import com.google.devtools.build.skyframe.EvaluationResult;
@@ -54,7 +55,7 @@
 
   private SkyKey createCollectPackagesKey(
       Path root, PathFragment rootRelativePath, ImmutableSet<PathFragment> excludedPaths) {
-    RootedPath rootedPath = RootedPath.toRootedPath(root, rootRelativePath);
+    RootedPath rootedPath = RootedPath.toRootedPath(Root.fromPath(root), rootRelativePath);
     return CollectPackagesUnderDirectoryValue.key(
         RepositoryName.MAIN, rootedPath, excludedPaths);
   }
@@ -65,14 +66,14 @@
 
   private SkyKey createPrepDepsKey(Path root, PathFragment rootRelativePath,
       ImmutableSet<PathFragment> excludedPaths) {
-    RootedPath rootedPath = RootedPath.toRootedPath(root, rootRelativePath);
+    RootedPath rootedPath = RootedPath.toRootedPath(Root.fromPath(root), rootRelativePath);
     return PrepareDepsOfTargetsUnderDirectoryValue.key(
         RepositoryName.MAIN, rootedPath, excludedPaths);
   }
 
   private SkyKey createPrepDepsKey(Path root, PathFragment rootRelativePath,
       ImmutableSet<PathFragment> excludedPaths, FilteringPolicy filteringPolicy) {
-    RootedPath rootedPath = RootedPath.toRootedPath(root, rootRelativePath);
+    RootedPath rootedPath = RootedPath.toRootedPath(Root.fromPath(root), rootRelativePath);
     return PrepareDepsOfTargetsUnderDirectoryValue.key(
         RepositoryName.MAIN, rootedPath, excludedPaths, filteringPolicy);
   }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunctionTest.java
index 1779162..96fa7e4 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunctionTest.java
@@ -46,6 +46,7 @@
 import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
 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.devtools.build.skyframe.ErrorInfo;
 import com.google.devtools.build.skyframe.EvaluationProgressReceiver;
@@ -89,7 +90,7 @@
         new AtomicReference<>(
             new PathPackageLocator(
                 outputBase,
-                ImmutableList.of(rootDirectory),
+                ImmutableList.of(Root.fromPath(rootDirectory)),
                 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
     AtomicReference<ImmutableSet<PackageIdentifier>> deletedPackages =
         new AtomicReference<>(ImmutableSet.<PackageIdentifier>of());
@@ -150,13 +151,14 @@
   }
 
   private Artifact sourceArtifact(String path) {
-    return new Artifact(PathFragment.create(path), ArtifactRoot.asSourceRoot(rootDirectory));
+    return new Artifact(
+        PathFragment.create(path), ArtifactRoot.asSourceRoot(Root.fromPath(rootDirectory)));
   }
 
   private Artifact sourceArtifactUnderPackagePath(String path, String packagePath) {
     return new Artifact(
         PathFragment.create(path),
-        ArtifactRoot.asSourceRoot(rootDirectory.getRelative(packagePath)));
+        ArtifactRoot.asSourceRoot(Root.fromPath(rootDirectory.getRelative(packagePath))));
   }
 
   private Artifact derivedArtifact(String path) {
@@ -171,17 +173,17 @@
   }
 
   private static RootedPath rootedPath(Artifact artifact) {
-    return RootedPath.toRootedPath(artifact.getRoot().getPath(), artifact.getRootRelativePath());
+    return RootedPath.toRootedPath(artifact.getRoot().getRoot(), artifact.getRootRelativePath());
   }
 
   private RootedPath rootedPath(String path, String packagePath) {
     return RootedPath.toRootedPath(
-        rootDirectory.getRelative(packagePath), PathFragment.create(path));
+        Root.fromPath(rootDirectory.getRelative(packagePath)), PathFragment.create(path));
   }
 
   private static RootedPath childOf(Artifact artifact, String relative) {
     return RootedPath.toRootedPath(
-        artifact.getRoot().getPath(), artifact.getRootRelativePath().getRelative(relative));
+        artifact.getRoot().getRoot(), artifact.getRootRelativePath().getRelative(relative));
   }
 
   private static RootedPath childOf(RootedPath path, String relative) {
@@ -201,7 +203,7 @@
   private static RootedPath siblingOf(Artifact artifact, String relative) {
     PathFragment parent =
         Preconditions.checkNotNull(artifact.getRootRelativePath().getParentDirectory());
-    return RootedPath.toRootedPath(artifact.getRoot().getPath(), parent.getRelative(relative));
+    return RootedPath.toRootedPath(artifact.getRoot().getRoot(), parent.getRelative(relative));
   }
 
   private void createFile(Path path, String... contents) throws Exception {
@@ -702,7 +704,9 @@
     pkgLocator.set(
         new PathPackageLocator(
             outputBase,
-            ImmutableList.of(rootDirectory.getRelative("pp1"), rootDirectory.getRelative("pp2")),
+            ImmutableList.of(
+                Root.fromPath(rootDirectory.getRelative("pp1")),
+                Root.fromPath(rootDirectory.getRelative("pp2"))),
             BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
     PrecomputedValue.PATH_PACKAGE_LOCATOR.set(differencer, pkgLocator.get());
 
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/RecursivePkgFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/RecursivePkgFunctionTest.java
index f07b6ab..ac45a7e 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/RecursivePkgFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/RecursivePkgFunctionTest.java
@@ -24,6 +24,7 @@
 import com.google.devtools.build.lib.cmdline.RepositoryName;
 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.devtools.build.skyframe.BuildDriver;
 import com.google.devtools.build.skyframe.EvaluationResult;
@@ -55,7 +56,7 @@
 
   private SkyKey buildRecursivePkgKey(
       Path root, PathFragment rootRelativePath, ImmutableSet<PathFragment> excludedPaths) {
-    RootedPath rootedPath = RootedPath.toRootedPath(root, rootRelativePath);
+    RootedPath rootedPath = RootedPath.toRootedPath(Root.fromPath(root), rootRelativePath);
     return RecursivePkgValue.key(
         RepositoryName.MAIN, rootedPath, excludedPaths);
   }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/RecursivePkgKeyTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/RecursivePkgKeyTest.java
index e205cab..b6bd394 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/RecursivePkgKeyTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/RecursivePkgKeyTest.java
@@ -20,9 +20,9 @@
 import com.google.devtools.build.lib.cmdline.RepositoryName;
 import com.google.devtools.build.lib.skyframe.RecursivePkgValue.RecursivePkgKey;
 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.devtools.build.skyframe.SkyKey;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -35,7 +35,7 @@
       RepositoryName repository,
       PathFragment rootRelativePath,
       ImmutableSet<PathFragment> excludedPaths) {
-    RootedPath rootedPath = RootedPath.toRootedPath(rootDirectory, rootRelativePath);
+    RootedPath rootedPath = RootedPath.toRootedPath(Root.fromPath(rootDirectory), rootRelativePath);
     return RecursivePkgValue.key(repository, rootedPath, excludedPaths);
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeAwareActionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeAwareActionTest.java
index e497ab7..10e9674 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeAwareActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeAwareActionTest.java
@@ -35,6 +35,7 @@
 import com.google.devtools.build.lib.util.Fingerprint;
 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.devtools.build.skyframe.EvaluationProgressReceiver;
 import com.google.devtools.build.skyframe.EvaluationProgressReceiver.EvaluationState;
@@ -352,7 +353,7 @@
     differencer.invalidate(
         ImmutableList.of(
             FileStateValue.key(
-                RootedPath.toRootedPath(file.getRoot().getPath(), file.getRootRelativePath()))));
+                RootedPath.toRootedPath(file.getRoot().getRoot(), file.getRootRelativePath()))));
   }
 
   private void assertActionExecutions(
@@ -445,7 +446,7 @@
 
   private RootedPath createSkyframeDepOfAction() throws Exception {
     scratch.file(rootDirectory.getRelative("action.dep").getPathString(), "blah");
-    return RootedPath.toRootedPath(rootDirectory, PathFragment.create("action.dep"));
+    return RootedPath.toRootedPath(Root.fromPath(rootDirectory), PathFragment.create("action.dep"));
   }
 
   private void appendToFile(Path path) throws Exception {
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTest.java
index 63e900f..b13b879 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTest.java
@@ -34,6 +34,7 @@
 import com.google.devtools.build.lib.vfs.ModifiedFileSet;
 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.common.options.Options;
 import java.io.IOException;
 import java.util.Collection;
@@ -407,7 +408,7 @@
         .preparePackageLoading(
             new PathPackageLocator(
                 outputBase,
-                ImmutableList.of(rootDirectory),
+                ImmutableList.of(Root.fromPath(rootDirectory)),
                 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY),
             packageCacheOptions,
             Options.getDefaults(SkylarkSemanticsOptions.class),
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTestCase.java b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTestCase.java
index f20cea8..9084371 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTestCase.java
@@ -38,6 +38,7 @@
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.ModifiedFileSet;
 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 com.google.devtools.build.skyframe.DelegatingWalkableGraph;
 import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
@@ -219,7 +220,8 @@
 
   protected void syncPackages(ModifiedFileSet modifiedFileSet) throws InterruptedException {
     getSkyframeExecutor()
-        .invalidateFilesUnderPathForTesting(reporter, modifiedFileSet, rootDirectory);
+        .invalidateFilesUnderPathForTesting(
+            reporter, modifiedFileSet, Root.fromPath(rootDirectory));
   }
 
   protected Set<Target> asTargetSet(Iterable<String> strLabels)
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkFileContentHashTests.java b/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkFileContentHashTests.java
index bbf3489..172ec39 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkFileContentHashTests.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkFileContentHashTests.java
@@ -28,6 +28,7 @@
 import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
 import com.google.devtools.build.lib.skyframe.util.SkyframeExecutorTestUtils;
 import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
+import com.google.devtools.build.lib.vfs.Root;
 import com.google.devtools.build.skyframe.EvaluationResult;
 import com.google.devtools.build.skyframe.SkyKey;
 import com.google.devtools.common.options.Options;
@@ -165,7 +166,7 @@
         .preparePackageLoading(
             new PathPackageLocator(
                 outputBase,
-                ImmutableList.of(rootDirectory),
+                ImmutableList.of(Root.fromPath(rootDirectory)),
                 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY),
             packageCacheOptions,
             Options.getDefaults(SkylarkSemanticsOptions.class),
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunctionTest.java
index 9d73fc8..03ec708 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunctionTest.java
@@ -28,6 +28,7 @@
 import com.google.devtools.build.lib.skyframe.util.SkyframeExecutorTestUtils;
 import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.Root;
 import com.google.devtools.build.skyframe.ErrorInfo;
 import com.google.devtools.build.skyframe.EvaluationResult;
 import com.google.devtools.build.skyframe.SkyKey;
@@ -55,7 +56,7 @@
         .preparePackageLoading(
             new PathPackageLocator(
                 outputBase,
-                ImmutableList.of(rootDirectory, alternativeRoot),
+                ImmutableList.of(Root.fromPath(rootDirectory), Root.fromPath(alternativeRoot)),
                 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY),
             packageCacheOptions,
             Options.getDefaults(SkylarkSemanticsOptions.class),
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TargetMarkerFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/TargetMarkerFunctionTest.java
index 1e944d2..75a4888 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TargetMarkerFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TargetMarkerFunctionTest.java
@@ -29,6 +29,7 @@
 import com.google.devtools.build.lib.vfs.ModifiedFileSet;
 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 com.google.devtools.build.skyframe.ErrorInfo;
 import com.google.devtools.build.skyframe.EvaluationResult;
@@ -90,7 +91,7 @@
     ModifiedFileSet subpackageBuildFile =
         ModifiedFileSet.builder().modify(PathFragment.create("a/b/BUILD")).build();
     skyframeExecutor.invalidateFilesUnderPathForTesting(
-        reporter, subpackageBuildFile, rootDirectory);
+        reporter, subpackageBuildFile, Root.fromPath(rootDirectory));
 
     NoSuchTargetException exn = (NoSuchTargetException) getErrorFromTargetValue(labelName);
     // In the presence of b/12545745, the error message is different and comes from the
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TimestampBuilderTestCase.java b/src/test/java/com/google/devtools/build/lib/skyframe/TimestampBuilderTestCase.java
index 8595811..27e95b5 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TimestampBuilderTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TimestampBuilderTestCase.java
@@ -75,6 +75,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.skyframe.CycleInfo;
 import com.google.devtools.build.skyframe.ErrorInfo;
 import com.google.devtools.build.skyframe.EvaluationProgressReceiver;
@@ -160,7 +161,7 @@
         new AtomicReference<>(
             new PathPackageLocator(
                 outputBase,
-                ImmutableList.of(rootDirectory),
+                ImmutableList.of(Root.fromPath(rootDirectory)),
                 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
     AtomicReference<TimestampGranularityMonitor> tsgmRef = new AtomicReference<>(tsgm);
     BlazeDirectories directories =
@@ -324,7 +325,7 @@
 
   Artifact createSourceArtifact(FileSystem fs, String name) {
     Path root = fs.getPath(TestUtils.tmpDir());
-    return new Artifact(PathFragment.create(name), ArtifactRoot.asSourceRoot(root));
+    return new Artifact(PathFragment.create(name), ArtifactRoot.asSourceRoot(Root.fromPath(root)));
   }
 
   protected Artifact createDerivedArtifact(String name) {
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/WorkspaceASTFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/WorkspaceASTFunctionTest.java
index 95e1a62..ece3f59 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/WorkspaceASTFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/WorkspaceASTFunctionTest.java
@@ -24,6 +24,7 @@
 import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
 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.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyFunctionException;
@@ -60,7 +61,8 @@
     Path workspacePath = scratch.overwriteFile("WORKSPACE", contents);
     fakeWorkspaceFileValue.setSize(workspacePath.getFileSize());
     return RootedPath.toRootedPath(
-        workspacePath.getParentDirectory(), PathFragment.create(workspacePath.getBaseName()));
+        Root.fromPath(workspacePath.getParentDirectory()),
+        PathFragment.create(workspacePath.getBaseName()));
   }
 
   private SkyFunction.Environment getEnv() throws InterruptedException {
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/WorkspaceFileFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/WorkspaceFileFunctionTest.java
index d5e37ce..95e9e88 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/WorkspaceFileFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/WorkspaceFileFunctionTest.java
@@ -30,6 +30,7 @@
 import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
 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.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyFunctionName;
@@ -123,7 +124,8 @@
     Path workspacePath = scratch.overwriteFile("WORKSPACE", contents);
     fakeWorkspaceFileValue.setSize(workspacePath.getFileSize());
     return RootedPath.toRootedPath(
-        workspacePath.getParentDirectory(), PathFragment.create(workspacePath.getBaseName()));
+        Root.fromPath(workspacePath.getParentDirectory()),
+        PathFragment.create(workspacePath.getBaseName()));
   }
 
   // Dummy harmcrest matcher that match the function name of a skykey
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/WorkspaceNameFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/WorkspaceNameFunctionTest.java
index 63483f5..ec17377 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/WorkspaceNameFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/WorkspaceNameFunctionTest.java
@@ -22,6 +22,7 @@
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.ModifiedFileSet;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.Root;
 import com.google.devtools.build.skyframe.EvaluationResult;
 import com.google.devtools.build.skyframe.SkyKey;
 import org.junit.Test;
@@ -34,10 +35,11 @@
   private final SkyKey key = WorkspaceNameValue.key();
 
   private EvaluationResult<WorkspaceNameValue> eval() throws InterruptedException {
-    getSkyframeExecutor().invalidateFilesUnderPathForTesting(
-        reporter,
-        ModifiedFileSet.builder().modify(PathFragment.create("WORKSPACE")).build(),
-        rootDirectory);
+    getSkyframeExecutor()
+        .invalidateFilesUnderPathForTesting(
+            reporter,
+            ModifiedFileSet.builder().modify(PathFragment.create("WORKSPACE")).build(),
+            Root.fromPath(rootDirectory));
     return SkyframeExecutorTestUtils.evaluate(
         getSkyframeExecutor(), key, /*keepGoing=*/ false, reporter);
   }