Annotate BEP output files with "prefix" information sufficient to reconstruct the full output path.
This should be backwards-compatible, as we are just adding a new field to the File proto.
RELNOTES: None
PiperOrigin-RevId: 250485470
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 ad3399f..272513e 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
@@ -14,7 +14,9 @@
package com.google.devtools.build.lib.actions;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
@@ -26,6 +28,7 @@
import com.google.devtools.build.lib.vfs.Root;
import java.io.Serializable;
import java.util.Objects;
+import javax.annotation.Nullable;
/**
* A root for an artifact. The roots are the directories containing artifacts, and they are mapped
@@ -47,7 +50,6 @@
@Immutable
public final class ArtifactRoot implements Comparable<ArtifactRoot>, Serializable, FileRootApi {
private static final Interner<ArtifactRoot> INTERNER = Interners.newWeakInterner();
-
/**
* Do not use except in tests and in {@link
* com.google.devtools.build.lib.skyframe.SkyframeExecutor}.
@@ -59,17 +61,35 @@
}
/**
+ * Constructs an ArtifactRoot given the output prefixes. (eg, "bin"), and (eg, "testlogs")
+ * relative to the execRoot.
+ *
+ * <p>Be careful with this method - all derived roots must be registered with the artifact factory
+ * before the analysis phase.
+ */
+ public static ArtifactRoot asDerivedRoot(Path execRoot, PathFragment... prefixes) {
+ Path root = execRoot;
+ for (PathFragment prefix : prefixes) {
+ root = root.getRelative(prefix);
+ }
+ Preconditions.checkArgument(root.startsWith(execRoot));
+ Preconditions.checkArgument(!root.equals(execRoot));
+ PathFragment execPath = root.relativeTo(execRoot);
+ return INTERNER.intern(
+ new ArtifactRoot(
+ Root.fromPath(root), execPath, RootType.Output, ImmutableList.copyOf(prefixes)));
+ }
+
+ /**
* Returns the given path as a derived root, relative to the given exec root. The root must be a
* proper sub-directory of the exec root (i.e. not equal). Neither may be {@code null}.
*
* <p>Be careful with this method - all derived roots must be registered with the artifact factory
* before the analysis phase.
*/
+ @VisibleForTesting
public static ArtifactRoot asDerivedRoot(Path execRoot, Path root) {
- Preconditions.checkArgument(root.startsWith(execRoot));
- Preconditions.checkArgument(!root.equals(execRoot));
- PathFragment execPath = root.relativeTo(execRoot);
- return INTERNER.intern(new ArtifactRoot(Root.fromPath(root), execPath, RootType.Output));
+ return asDerivedRoot(execRoot, root.relativeTo(execRoot));
}
public static ArtifactRoot middlemanRoot(Path execRoot, Path outputDir) {
@@ -82,8 +102,9 @@
@AutoCodec.VisibleForSerialization
@AutoCodec.Instantiator
- static ArtifactRoot createForSerialization(Root root, PathFragment execPath, RootType rootType) {
- return INTERNER.intern(new ArtifactRoot(root, execPath, rootType));
+ static ArtifactRoot createForSerialization(
+ Root root, PathFragment execPath, RootType rootType, ImmutableList<PathFragment> components) {
+ return INTERNER.intern(new ArtifactRoot(root, execPath, rootType, components));
}
@AutoCodec.VisibleForSerialization
@@ -96,11 +117,18 @@
private final Root root;
private final PathFragment execPath;
private final RootType rootType;
+ @Nullable private final ImmutableList<PathFragment> components;
- private ArtifactRoot(Root root, PathFragment execPath, RootType rootType) {
+ private ArtifactRoot(
+ Root root, PathFragment execPath, RootType rootType, ImmutableList<PathFragment> components) {
this.root = Preconditions.checkNotNull(root);
this.execPath = execPath;
this.rootType = rootType;
+ this.components = components;
+ }
+
+ private ArtifactRoot(Root root, PathFragment execPath, RootType rootType) {
+ this(root, execPath, rootType, /* components= */ null);
}
public Root getRoot() {
@@ -120,6 +148,9 @@
return getExecPath().getPathString();
}
+ public ImmutableList<PathFragment> getComponents() {
+ return components;
+ }
public boolean isSourceRoot() {
return rootType == RootType.Source;
@@ -136,7 +167,7 @@
@Override
public int hashCode() {
- return Objects.hash(root, execPath, rootType);
+ return Objects.hash(root, execPath, rootType, components);
}
@Override
@@ -148,7 +179,10 @@
return false;
}
ArtifactRoot r = (ArtifactRoot) o;
- return root.equals(r.root) && execPath.equals(r.execPath) && rootType == r.rootType;
+ return root.equals(r.root)
+ && execPath.equals(r.execPath)
+ && rootType == r.rootType
+ && Objects.equals(components, r.components);
}
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BlazeDirectories.java b/src/main/java/com/google/devtools/build/lib/analysis/BlazeDirectories.java
index a1b4c3e..27fce36 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BlazeDirectories.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BlazeDirectories.java
@@ -21,6 +21,7 @@
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.util.StringCanonicalizer;
import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.Objects;
/**
@@ -185,7 +186,8 @@
* {@link BlazeDirectories} of this server instance. Nothing else should be placed here.
*/
public ArtifactRoot getBuildDataDirectory(String workspaceName) {
- return ArtifactRoot.asDerivedRoot(getExecRoot(workspaceName), getOutputPath(workspaceName));
+ return ArtifactRoot.asDerivedRoot(
+ getExecRoot(workspaceName), PathFragment.create(getRelativeOutputPath(productName)));
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/TargetCompleteEvent.java b/src/main/java/com/google/devtools/build/lib/analysis/TargetCompleteEvent.java
index fb9ebe3..028c033 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/TargetCompleteEvent.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/TargetCompleteEvent.java
@@ -56,6 +56,7 @@
import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.skyframe.SkyValue;
import java.util.Collection;
import java.util.function.Function;
@@ -117,6 +118,11 @@
private final ExecutableTargetData executableTargetData;
private final boolean bepReportOnlyImportantArtifacts;
+ private static final String DISABLE_PREFIX_NAME =
+ "com.google.devtools.build.lib.analysis.TargetCompleteEvent.disable_path_prefix";
+ private static final boolean INCLUDE_PATH_PREFIX =
+ !"1".equals(System.getProperty(DISABLE_PREFIX_NAME));
+
private TargetCompleteEvent(
ConfiguredTargetAndData targetAndData,
NestedSet<Cause> rootCauses,
@@ -305,11 +311,26 @@
String name = artifactNameFunction.apply(artifact);
String uri = converters.pathConverter().apply(pathResolver.toPath(artifact));
if (uri != null) {
- builder.addImportantOutput(File.newBuilder().setName(name).setUri(uri).build());
+ builder.addImportantOutput(newFileFromArtifact(name, artifact).setUri(uri).build());
}
}
}
+ public static BuildEventStreamProtos.File.Builder newFileFromArtifact(
+ String name, Artifact artifact) {
+ File.Builder builder =
+ File.newBuilder().setName(name == null ? artifact.getRootRelativePathString() : name);
+ if (INCLUDE_PATH_PREFIX && artifact.getRoot().getComponents() != null) {
+ builder.addAllPathPrefix(
+ Iterables.transform(artifact.getRoot().getComponents(), PathFragment::getPathString));
+ }
+ return builder;
+ }
+
+ public static BuildEventStreamProtos.File.Builder newFileFromArtifact(Artifact artifact) {
+ return newFileFromArtifact(/* name= */ null, artifact);
+ }
+
@Override
public Collection<LocalFile> referencedLocalFiles() {
ImmutableList.Builder<LocalFile> builder = ImmutableList.builder();
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/OutputDirectories.java b/src/main/java/com/google/devtools/build/lib/analysis/config/OutputDirectories.java
index 17d47c1..713ad72 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/OutputDirectories.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/OutputDirectories.java
@@ -104,13 +104,17 @@
// e.g., execroot/repo1
Path execRoot = directories.getExecRoot(mainRepositoryName.strippedName());
// e.g., execroot/repo1/bazel-out/config/bin
- Path outputDir =
- execRoot.getRelative(directories.getRelativeOutputPath()).getRelative(outputDirName);
if (middleman) {
+ Path outputDir =
+ execRoot.getRelative(directories.getRelativeOutputPath()).getRelative(outputDirName);
return ArtifactRoot.middlemanRoot(execRoot, outputDir);
}
// e.g., [[execroot/repo1]/bazel-out/config/bin]
- return ArtifactRoot.asDerivedRoot(execRoot, outputDir.getRelative(nameFragment));
+ return ArtifactRoot.asDerivedRoot(
+ execRoot,
+ PathFragment.create(directories.getRelativeOutputPath()),
+ PathFragment.create(outputDirName),
+ nameFragment);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto b/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto
index 00d2592..79f4882 100644
--- a/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto
+++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto
@@ -384,6 +384,13 @@
}
message File {
+ // A sequence of prefixes to apply to the file name to construct a full path.
+ // In most but not all cases, there will be 3 entries:
+ // 1. A root output directory, eg "bazel-out"
+ // 2. A configuration mnemonic, eg "k8-fastbuild"
+ // 3. An output category, eg "genfiles"
+ repeated string path_prefix = 4;
+
// identifier indicating the nature of the file (e.g., "stdout", "stderr")
string name = 1;
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/NamedArtifactGroup.java b/src/main/java/com/google/devtools/build/lib/runtime/NamedArtifactGroup.java
index 2ec940d..320519a 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/NamedArtifactGroup.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/NamedArtifactGroup.java
@@ -14,6 +14,8 @@
package com.google.devtools.build.lib.runtime;
+import static com.google.devtools.build.lib.analysis.TargetCompleteEvent.newFileFromArtifact;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.Artifact;
@@ -81,10 +83,9 @@
if (artifact.isMiddlemanArtifact()) {
continue;
}
- String name = artifact.getRootRelativePathString();
String uri = pathConverter.apply(pathResolver.toPath(artifact));
if (uri != null) {
- builder.addFiles(BuildEventStreamProtos.File.newBuilder().setName(name).setUri(uri));
+ builder.addFiles(newFileFromArtifact(artifact).setUri(uri));
}
}
for (NestedSetView<Artifact> child : view.transitives()) {
diff --git a/src/test/java/com/google/devtools/build/lib/actions/ArtifactRootTest.java b/src/test/java/com/google/devtools/build/lib/actions/ArtifactRootTest.java
index f486ba9..18be4a5 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/ArtifactRootTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/ArtifactRootTest.java
@@ -73,9 +73,9 @@
}
@Test
- public void testBadAsDerivedRootNullDir() throws IOException {
+ public void testBadAsDerivedRootIsExecRoot() throws IOException {
Path execRoot = scratch.dir("/exec");
- assertThrows(NullPointerException.class, () -> ArtifactRoot.asDerivedRoot(execRoot, null));
+ assertThrows(IllegalArgumentException.class, () -> ArtifactRoot.asDerivedRoot(execRoot));
}
@Test