blob: c55bcd0da98178fb281974d531ef59a9f4a7b7a8 [file] [log] [blame]
// Copyright 2016 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.runtime;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.CompletionContext;
import com.google.devtools.build.lib.actions.CompletionContext.ArtifactReceiver;
import com.google.devtools.build.lib.actions.FileArtifactValue;
import com.google.devtools.build.lib.actions.FilesetOutputSymlink;
import com.google.devtools.build.lib.analysis.TargetCompleteEvent;
import com.google.devtools.build.lib.buildeventstream.ArtifactGroupNamer;
import com.google.devtools.build.lib.buildeventstream.BuildEvent;
import com.google.devtools.build.lib.buildeventstream.BuildEvent.LocalFile.LocalFileType;
import com.google.devtools.build.lib.buildeventstream.BuildEventContext;
import com.google.devtools.build.lib.buildeventstream.BuildEventIdUtil;
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos;
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildEventId;
import com.google.devtools.build.lib.buildeventstream.GenericBuildEvent;
import com.google.devtools.build.lib.buildeventstream.PathConverter;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import java.util.Collection;
/**
* A {@link BuildEvent} introducing a set of artifacts to be referred to later by its name. Those
* events are generated by the {@link BuildEventStreamer} upon seeing an {@link
* com.google.devtools.build.lib.actions.EventReportingArtifacts}, if necessary.
*/
class NamedArtifactGroup implements BuildEvent {
private final String name;
private final CompletionContext completionContext;
private final NestedSet<?> set; // of Artifact or ExpandedArtifact
/**
* Create a {@link NamedArtifactGroup}. Although the set may contain a mixture of Artifacts and
* ExpandedArtifacts, all its leaf successors ("direct elements") are ExpandedArtifacts.
*/
NamedArtifactGroup(String name, CompletionContext completionContext, NestedSet<?> set) {
this.name = name;
this.completionContext = completionContext;
this.set = set;
}
@Override
public BuildEventId getEventId() {
return BuildEventIdUtil.fromArtifactGroupName(name);
}
@Override
public Collection<BuildEventId> getChildrenEvents() {
return ImmutableSet.of();
}
@Override
public Collection<LocalFile> referencedLocalFiles() {
ImmutableList.Builder<LocalFile> artifacts = ImmutableList.builder();
for (Object elem : set.getLeaves()) {
switch ((ExpandedArtifact) elem) {
case NormalExpandedArtifact(Artifact artifact, FileArtifactValue metadata) -> {
artifacts.add(
new LocalFile(
completionContext.pathResolver().toPath(artifact),
LocalFileType.forArtifact(artifact, metadata),
metadata));
}
case FilesetExpandedArtifact(Artifact fileset, FilesetOutputSymlink link) -> {
artifacts.add(
new LocalFile(
completionContext.pathResolver().toPath(link.target()),
LocalFileType.forArtifact(link.target(), link.metadata()),
link.metadata()));
}
}
}
return artifacts.build();
}
@Override
public BuildEventStreamProtos.BuildEvent asStreamProto(BuildEventContext converters) {
PathConverter pathConverter = converters.pathConverter();
ArtifactGroupNamer namer = converters.artifactGroupNamer();
BuildEventStreamProtos.NamedSetOfFiles.Builder builder =
BuildEventStreamProtos.NamedSetOfFiles.newBuilder();
for (Object elem : set.getLeaves()) {
BuildEventStreamProtos.File file =
switch ((ExpandedArtifact) elem) {
case NormalExpandedArtifact(Artifact artifact, FileArtifactValue metadata) -> {
String uri = pathConverter.apply(completionContext.pathResolver().toPath(artifact));
yield TargetCompleteEvent.newFile(artifact, metadata, uri);
}
case FilesetExpandedArtifact(Artifact fileset, FilesetOutputSymlink link) -> {
String uri =
pathConverter.apply(completionContext.pathResolver().toPath(link.target()));
yield TargetCompleteEvent.newFile(
fileset.getRoot(),
fileset.getRootRelativePath().getRelative(link.name()),
link.metadata(),
uri);
}
};
// Omit files with unknown contents (e.g. if uploading failed).
if (file.getFileCase() != BuildEventStreamProtos.File.FileCase.FILE_NOT_SET) {
builder.addFiles(file);
}
}
for (NestedSet<?> succ : set.getNonLeaves()) {
builder.addFileSets(namer.apply(succ.toNode()));
}
return GenericBuildEvent.protoChaining(this).setNamedSetOfFiles(builder.build()).build();
}
/**
* Given a set whose leaf successors are {@link Artifact} and {@link ExpandedArtifact}, returns a
* new NestedSet whose leaf successors are all ExpandedArtifact. Non-leaf successors are
* unaltered.
*/
static NestedSet<?> expandSet(CompletionContext ctx, NestedSet<?> artifacts) {
NestedSetBuilder<Object> res = NestedSetBuilder.newBuilder(Order.STABLE_ORDER);
for (Object elem : artifacts.getLeaves()) {
switch (elem) {
case ExpandedArtifact expandedArtifact -> {
res.add(expandedArtifact);
}
case Artifact artifact -> {
ctx.visitArtifacts(
ImmutableList.of(artifact),
new ArtifactReceiver() {
@Override
public void accept(Artifact artifact, FileArtifactValue metadata) {
res.add(new NormalExpandedArtifact(artifact, metadata));
}
@Override
public void acceptFilesetMapping(Artifact fileset, FilesetOutputSymlink link) {
res.add(new FilesetExpandedArtifact(fileset, link));
}
});
}
default -> {
throw new IllegalStateException("Unexpected type in artifact set: %s".formatted(elem));
}
}
}
boolean noDirects = res.isEmpty();
ImmutableList<? extends NestedSet<?>> nonLeaves = artifacts.getNonLeaves();
for (NestedSet<?> succ : nonLeaves) {
res.addTransitive(succ);
}
NestedSet<?> result = res.build();
// If we have executed one call to res.addTransitive(x)
// and none to res.add, then res.build() simply returns x
// ("inlining"), which may violate our postcondition on elements.
// (This can happen if 'artifacts' contains one non-leaf successor
// and one or more leaf successors for which visitArtifacts is a no-op.)
//
// In that case we need to recursively apply the expansion. Ugh.
if (noDirects && nonLeaves.size() == 1) {
return expandSet(ctx, result);
}
return result;
}
private sealed interface ExpandedArtifact
permits NormalExpandedArtifact, FilesetExpandedArtifact {}
private record NormalExpandedArtifact(Artifact artifact, FileArtifactValue metadata)
implements ExpandedArtifact {
private NormalExpandedArtifact {
checkState(!artifact.isDirectory(), "artifact must be expanded: %s", artifact);
}
}
private record FilesetExpandedArtifact(Artifact fileset, FilesetOutputSymlink link)
implements ExpandedArtifact {
private FilesetExpandedArtifact {
checkState(fileset.isFileset(), "artifact must be a fileset: %s", fileset);
}
}
}