ArtifactRoot correctly switches output base when serializing.
PiperOrigin-RevId: 190779535
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ArtifactRoot.java b/src/main/java/com/google/devtools/build/lib/actions/ArtifactRoot.java
index a3abeb5..4f08d6b 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/ArtifactRoot.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/ArtifactRoot.java
@@ -17,8 +17,10 @@
import com.google.common.base.Preconditions;
import com.google.devtools.build.lib.cmdline.RepositoryName;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
-import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
-import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
+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.devtools.build.lib.skylarkinterface.SkylarkCallable;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
@@ -27,6 +29,9 @@
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.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import java.io.IOException;
import java.io.Serializable;
import java.util.Objects;
@@ -54,7 +59,6 @@
+ "together into a single directory tree to form the execution environment."
)
@Immutable
-@AutoCodec
public final class ArtifactRoot implements Comparable<ArtifactRoot>, Serializable, SkylarkValue {
// This must always be consistent with Package.getSourceRoot; otherwise computing source roots
// from exec paths does not work, which can break the action cache for input-discovering actions.
@@ -98,8 +102,7 @@
return new ArtifactRoot(Root.fromPath(root), execPath, RootType.Middleman);
}
- @VisibleForSerialization
- enum RootType {
+ private enum RootType {
Source,
Output,
Middleman
@@ -109,7 +112,6 @@
private final PathFragment execPath;
private final RootType rootType;
- @AutoCodec.Instantiator
ArtifactRoot(Root root, PathFragment execPath, RootType rootType) {
this.root = Preconditions.checkNotNull(root);
this.execPath = execPath;
@@ -174,4 +176,49 @@
public void repr(SkylarkPrinter printer) {
printer.append(isSourceRoot() ? "<source root>" : "<derived root>");
}
+
+ /** Custom codec that replaces output base with local output base on deserialization. */
+ private static class ArtifactRootCodec implements ObjectCodec<ArtifactRoot> {
+ @Override
+ public Class<ArtifactRoot> getEncodedClass() {
+ return ArtifactRoot.class;
+ }
+
+ @Override
+ public void serialize(
+ SerializationContext context, ArtifactRoot input, CodedOutputStream codedOut)
+ throws SerializationException, IOException {
+ context.serialize(input.rootType, codedOut);
+ switch (input.rootType) {
+ case Source:
+ context.serialize(input.root, codedOut);
+ break;
+ case Output: // fall-through, same behavior as Middleman
+ case Middleman:
+ Path outputBase = context.getDependency(OutputBaseSupplier.class).get();
+ context.serialize(input.root.asPath().relativeTo(outputBase), codedOut);
+ break;
+ }
+ context.serialize(input.execPath, codedOut);
+ }
+
+ @Override
+ public ArtifactRoot deserialize(DeserializationContext context, CodedInputStream codedIn)
+ throws SerializationException, IOException {
+ ArtifactRoot.RootType rootType = context.deserialize(codedIn);
+ Root root = null;
+ switch (rootType) {
+ case Source:
+ root = context.deserialize(codedIn);
+ break;
+ case Output: // fall-through, same behavior as Middleman
+ case Middleman:
+ Path outputBase = context.getDependency(OutputBaseSupplier.class).get();
+ PathFragment relativeRoot = context.deserialize(codedIn);
+ root = Root.fromPath(outputBase.getRelative(relativeRoot));
+ break;
+ }
+ return new ArtifactRoot(root, context.deserialize(codedIn), rootType);
+ }
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/actions/OutputBaseSupplier.java b/src/main/java/com/google/devtools/build/lib/actions/OutputBaseSupplier.java
new file mode 100644
index 0000000..f915869
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/actions/OutputBaseSupplier.java
@@ -0,0 +1,23 @@
+// 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.actions;
+
+import com.google.devtools.build.lib.vfs.Path;
+
+/** Provides this Blaze server's output base used for serialization. */
+@FunctionalInterface
+public interface OutputBaseSupplier {
+ Path get();
+}
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
index 71714c3..1ce0ac1 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
@@ -397,6 +397,10 @@
return projectFileProvider;
}
+ public Path getOutputBase() {
+ return getWorkspace().getDirectories().getOutputBase();
+ }
+
/**
* Hook method called by the BlazeCommandDispatcher prior to the dispatch of
* each command.
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
index 2f35acc..a089b0b 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
@@ -2034,6 +2034,11 @@
return pkgFactory.getPackageBuilderHelperForTesting();
}
+ @VisibleForTesting
+ public Path getOutputBaseForTesting() {
+ return directories.getOutputBase();
+ }
+
/**
* Initializes and syncs the graph with the given options, readying it for the next evaluation.
*/
diff --git a/src/test/java/com/google/devtools/build/lib/actions/ArtifactTest.java b/src/test/java/com/google/devtools/build/lib/actions/ArtifactTest.java
index 26efc19..e7ead54 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/ArtifactTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/ArtifactTest.java
@@ -346,6 +346,7 @@
PathFragment.create("src/c"),
new LabelArtifactOwner(Label.parseAbsoluteUnchecked("//foo:bar"))))
.addDependency(FileSystem.class, scratch.getFileSystem())
+ .addDependency(OutputBaseSupplier.class, () -> scratch.getFileSystem().getPath("/"))
.runTests();
}
diff --git a/src/test/java/com/google/devtools/build/lib/actions/ExecutableSymlinkActionTest.java b/src/test/java/com/google/devtools/build/lib/actions/ExecutableSymlinkActionTest.java
index fedc601..83e3f31 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/ExecutableSymlinkActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/ExecutableSymlinkActionTest.java
@@ -37,6 +37,7 @@
@RunWith(JUnit4.class)
public class ExecutableSymlinkActionTest {
private Scratch scratch = new Scratch();
+ private Path execRoot;
private ArtifactRoot inputRoot;
private ArtifactRoot outputRoot;
TestFileOutErr outErr;
@@ -46,7 +47,7 @@
@Before
public final void createExecutor() throws Exception {
final Path inputDir = scratch.dir("/in");
- Path execRoot = scratch.getFileSystem().getPath("/");
+ execRoot = scratch.getFileSystem().getPath("/");
inputRoot = ArtifactRoot.asDerivedRoot(execRoot, inputDir);
outputRoot = ArtifactRoot.asDerivedRoot(execRoot, scratch.dir("/out"));
outErr = new TestFileOutErr();
@@ -125,6 +126,7 @@
ExecutableSymlinkAction action = new ExecutableSymlinkAction(NULL_ACTION_OWNER, input, output);
new SerializationTester(action)
.addDependency(FileSystem.class, scratch.getFileSystem())
+ .addDependency(OutputBaseSupplier.class, () -> execRoot)
.setVerificationFunction(
(in, out) -> {
ExecutableSymlinkAction inAction = (ExecutableSymlinkAction) in;
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/actions/SymlinkActionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/actions/SymlinkActionTest.java
index b304558..cdd4760 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/actions/SymlinkActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/actions/SymlinkActionTest.java
@@ -22,6 +22,7 @@
import com.google.devtools.build.lib.actions.ActionResult;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Executor;
+import com.google.devtools.build.lib.actions.OutputBaseSupplier;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
import com.google.devtools.build.lib.exec.util.TestExecutorBuilder;
import com.google.devtools.build.lib.skyframe.serialization.testutils.SerializationTester;
@@ -47,7 +48,7 @@
private SymlinkAction action;
@Before
- public final void setUp() throws Exception {
+ public final void setUp() throws Exception {
input = scratch.file("input.txt", "Hello, world.");
inputArtifact = getSourceArtifact("input.txt");
Path linkedInput =
@@ -98,6 +99,7 @@
public void testCodec() throws Exception {
new SerializationTester(action)
.addDependency(FileSystem.class, scratch.getFileSystem())
+ .addDependency(OutputBaseSupplier.class, () -> outputBase)
.setVerificationFunction(
(in, out) -> {
SymlinkAction inAction = (SymlinkAction) in;
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 7580624..637bd39 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
@@ -1999,8 +1999,7 @@
Artifact inputManifest = Iterables.getOnlyElement(symlinkTreeAction.getInputs());
SourceManifestAction inputManifestAction =
(SourceManifestAction) getGeneratingAction(inputManifest);
- // Ask the manifest to write itself to a byte array so that we can
- // read its contents.
+ // Ask the manifest to write itself to a byte array so that we can read its contents.
ByteArrayOutputStream stream = new ByteArrayOutputStream();
inputManifestAction.writeOutputFile(stream, reporter);
String contents = stream.toString();
@@ -2053,4 +2052,8 @@
return view.getArtifactFactory()
.getDerivedArtifact(target.getLabel().getPackageFragment().getRelative(path), root, owner);
}
+
+ public Path getExecRoot() {
+ return directories.getExecRoot(ruleClassProvider.getRunfilesPrefix());
+ }
}
diff --git a/src/test/java/com/google/devtools/build/lib/exec/MiddlemanActionTest.java b/src/test/java/com/google/devtools/build/lib/exec/MiddlemanActionTest.java
index 5c176fe..c49d5e5 100644
--- a/src/test/java/com/google/devtools/build/lib/exec/MiddlemanActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/exec/MiddlemanActionTest.java
@@ -23,6 +23,7 @@
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.MiddlemanAction;
import com.google.devtools.build.lib.actions.MiddlemanFactory;
+import com.google.devtools.build.lib.actions.OutputBaseSupplier;
import com.google.devtools.build.lib.analysis.util.AnalysisTestUtil;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
import com.google.devtools.build.lib.cmdline.RepositoryName;
@@ -142,6 +143,7 @@
public void testCodec() throws Exception {
new SerializationTester(getGeneratingAction(middle))
.addDependency(FileSystem.class, scratch.getFileSystem())
+ .addDependency(OutputBaseSupplier.class, () -> outputBase)
.setVerificationFunction(MiddlemanActionTest::verifyEquivalent)
.runTests();
}
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java
index a7b4602..2905030 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java
@@ -42,6 +42,7 @@
import com.google.devtools.build.lib.actions.ArtifactRoot;
import com.google.devtools.build.lib.actions.BuildFailedException;
import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
+import com.google.devtools.build.lib.actions.OutputBaseSupplier;
import com.google.devtools.build.lib.actions.cache.MetadataHandler;
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
import com.google.devtools.build.lib.actions.util.TestAction;
@@ -117,6 +118,8 @@
public void testCodec() throws Exception {
new SerializationTester(outOne, outOneFileOne)
.addDependency(FileSystem.class, scratch.getFileSystem())
+ .addDependency(
+ OutputBaseSupplier.class, () -> scratch.getFileSystem().getPath(TestUtils.tmpDir()))
.runTests();
}