// Copyright 2018 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.vfs; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext; import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec; import com.google.devtools.build.lib.skyframe.serialization.SerializationContext; import com.google.devtools.build.lib.skyframe.serialization.SerializationException; import com.google.protobuf.CodedInputStream; import com.google.protobuf.CodedOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import javax.annotation.Nullable; /** * A root path used in {@link RootedPath} and in artifact roots. * *

A typical root could be the exec path, a package root, or an output root specific to some * configuration. We also support absolute roots for non-hermetic paths outside the user workspace. */ public interface Root extends Comparable, Serializable { ObjectCodec CODEC = new RootCodec(); /** Constructs a root from a path. */ static Root fromPath(Path path) { return new PathRoot(path); } /** Returns an absolute root. Can only be used with absolute path fragments. */ static Root absoluteRoot(FileSystem fileSystem) { return fileSystem.getAbsoluteRoot(); } /** Returns a path by concatenating the root and the root-relative path. */ Path getRelative(PathFragment rootRelativePath); /** Returns a path by concatenating the root and the root-relative path. */ Path getRelative(String rootRelativePath); /** Returns the relative path between the root and the given path. */ PathFragment relativize(Path path); /** Returns the relative path between the root and the given absolute path fragment. */ PathFragment relativize(PathFragment absolutePathFragment); /** Returns whether the given path is under this root. */ boolean contains(Path path); /** Returns whether the given absolute path fragment is under this root. */ boolean contains(PathFragment absolutePathFragment); /** * Returns the underlying path. Please avoid using this method. * *

Not all roots are backed by paths, so this may return null. */ @Nullable Path asPath(); boolean isAbsolute(); /** Implementation of Root that is backed by a {@link Path}. */ final class PathRoot implements Root { private final Path path; PathRoot(Path path) { this.path = path; } @Override public Path getRelative(PathFragment rootRelativePath) { return path.getRelative(rootRelativePath); } @Override public Path getRelative(String rootRelativePath) { return path.getRelative(rootRelativePath); } @Override public PathFragment relativize(Path path) { return path.relativeTo(this.path); } @Override public PathFragment relativize(PathFragment absolutePathFragment) { Preconditions.checkArgument(absolutePathFragment.isAbsolute()); return absolutePathFragment.relativeTo(path.asFragment()); } @Override public boolean contains(Path path) { return path.startsWith(this.path); } @Override public boolean contains(PathFragment absolutePathFragment) { return absolutePathFragment.isAbsolute() && absolutePathFragment.startsWith(path.asFragment()); } @Override public Path asPath() { return path; } @Override public boolean isAbsolute() { return false; } @Override public String toString() { return path.toString(); } @Override public int compareTo(Root o) { if (o instanceof AbsoluteRoot) { return 1; } else if (o instanceof PathRoot) { return path.compareTo(((PathRoot) o).path); } else { throw new AssertionError("Unknown Root subclass: " + o.getClass().getName()); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } PathRoot pathRoot = (PathRoot) o; return path.equals(pathRoot.path); } @Override public int hashCode() { return path.hashCode(); } } /** An absolute root of a file system. Can only resolve absolute path fragments. */ final class AbsoluteRoot implements Root { private FileSystem fileSystem; // Non-final for serialization AbsoluteRoot(FileSystem fileSystem) { this.fileSystem = fileSystem; } @Override public Path getRelative(PathFragment rootRelativePath) { Preconditions.checkArgument(rootRelativePath.isAbsolute()); return fileSystem.getPath(rootRelativePath); } @Override public Path getRelative(String rootRelativePath) { return getRelative(PathFragment.create(rootRelativePath)); } @Override public PathFragment relativize(Path path) { return path.asFragment(); } @Override public PathFragment relativize(PathFragment absolutePathFragment) { Preconditions.checkArgument(absolutePathFragment.isAbsolute()); return absolutePathFragment; } @Override public boolean contains(Path path) { return true; } @Override public boolean contains(PathFragment absolutePathFragment) { return absolutePathFragment.isAbsolute(); } @Override public boolean isAbsolute() { return true; } @Override public Path asPath() { return null; } @Override public String toString() { return ""; } @Override public int compareTo(Root o) { if (o instanceof AbsoluteRoot) { return Integer.compare(fileSystem.hashCode(), ((AbsoluteRoot) o).fileSystem.hashCode()); } else if (o instanceof PathRoot) { return -1; } else { throw new AssertionError("Unknown Root subclass: " + o.getClass().getName()); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } AbsoluteRoot that = (AbsoluteRoot) o; return Objects.equal(fileSystem, that.fileSystem); } @Override public int hashCode() { return Objects.hashCode(fileSystem); } @SuppressWarnings("unused") private void readObject(ObjectInputStream in) throws IOException { fileSystem = Path.getFileSystemForSerialization(); } @SuppressWarnings("unused") private void writeObject(ObjectOutputStream out) throws IOException { Preconditions.checkState( fileSystem == Path.getFileSystemForSerialization(), "%s %s", fileSystem, Path.getFileSystemForSerialization()); } } /** Codec to serialize {@link Root}s. */ class RootCodec implements ObjectCodec { @Override public Class getEncodedClass() { return Root.class; } @Override public void serialize( SerializationContext context, Root obj, CodedOutputStream codedOut) throws SerializationException, IOException { if (obj instanceof PathRoot) { codedOut.writeBoolNoTag(false); Path.CODEC.serialize(context, ((PathRoot) obj).path, codedOut); } else if (obj instanceof AbsoluteRoot) { Preconditions.checkArgument( ((AbsoluteRoot) obj).fileSystem == context.getDependency(FileSystem.class)); codedOut.writeBoolNoTag(true); } else { throw new AssertionError("Unknown Root subclass: " + obj.getClass().getName()); } } @Override public Root deserialize(DeserializationContext context, CodedInputStream codedIn) throws SerializationException, IOException { boolean isAbsolute = codedIn.readBool(); if (isAbsolute) { return context.getDependency(FileSystem.class).getAbsoluteRoot(); } else { Path path = Path.CODEC.deserialize(context, codedIn); return new PathRoot(path); } } } }