blob: d4d51f64fbbc83192fe61d2cc8f7f5348f692a8b [file] [log] [blame]
// Copyright 2019 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 static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Maps;
import com.google.devtools.build.lib.actions.Artifact.ArchivedTreeArtifact;
import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.actions.FilesetOutputTree.RelativeSymlinkBehaviorWithoutError;
import com.google.devtools.build.lib.bugreport.BugReport;
import com.google.devtools.build.lib.bugreport.BugReporter;
import com.google.devtools.build.lib.skyframe.TreeArtifactValue;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Container for the data one needs to resolve aggregate artifacts from events signaling the
* completion of a target or an aspect ({@code TargetCompleteEvent} and {@code
* AspectCompleteEvent}).
*
* <p>This is needed because some artifacts (tree artifacts and Filesets) are in fact aggregations
* of multiple files.
*/
public final class CompletionContext implements ArtifactExpander {
public static final CompletionContext FAILED_COMPLETION_CTX =
new CompletionContext(
null,
ImmutableMap.of(),
ImmutableMap.of(),
null,
ArtifactPathResolver.IDENTITY,
new ActionInputMap(BugReporter.defaultInstance(), 0),
false,
false);
private final Path execRoot;
private final ArtifactPathResolver pathResolver;
private final Map<Artifact, TreeArtifactValue> treeArtifacts;
private final Map<Artifact, FilesetOutputTree> filesets;
@Nullable private final FileArtifactValue baselineCoverageValue;
// Only contains the metadata for 'important' artifacts of the Target/Aspect that completed. Any
// 'unimportant' artifacts produced by internal output groups (most importantly, _validation) will
// not be included to avoid retaining many GB on the heap. This ActionInputMap must only be
// consulted with respect to known-important artifacts (e.g. artifacts referenced in BEP).
private final ActionInputMap importantInputMap;
private final boolean expandFilesets;
private final boolean fullyResolveFilesetLinks;
@VisibleForTesting
public CompletionContext(
Path execRoot,
Map<Artifact, TreeArtifactValue> treeArtifacts,
Map<Artifact, FilesetOutputTree> filesets,
@Nullable FileArtifactValue baselineCoverageValue,
ArtifactPathResolver pathResolver,
ActionInputMap importantInputMap,
boolean expandFilesets,
boolean fullyResolveFilesetLinks) {
this.execRoot = execRoot;
this.treeArtifacts = treeArtifacts;
this.filesets = filesets;
this.baselineCoverageValue = baselineCoverageValue;
this.pathResolver = pathResolver;
this.importantInputMap = importantInputMap;
this.expandFilesets = expandFilesets;
this.fullyResolveFilesetLinks = fullyResolveFilesetLinks;
}
public static CompletionContext create(
Map<Artifact, TreeArtifactValue> treeArtifacts,
Map<Artifact, FilesetOutputTree> filesets,
@Nullable FileArtifactValue baselineCoverageValue,
boolean expandFilesets,
boolean fullyResolveFilesetSymlinks,
ActionInputMap inputMap,
ActionInputMap importantInputMap,
PathResolverFactory pathResolverFactory,
Path execRoot,
String workspaceName) {
ArtifactPathResolver pathResolver =
pathResolverFactory.shouldCreatePathResolverForArtifactValues()
? pathResolverFactory.createPathResolverForArtifactValues(
inputMap,
Maps.transformValues(treeArtifacts, TreeArtifactValue::getChildren),
filesets,
workspaceName)
: ArtifactPathResolver.IDENTITY;
return new CompletionContext(
execRoot,
treeArtifacts,
filesets,
baselineCoverageValue,
pathResolver,
importantInputMap,
expandFilesets,
fullyResolveFilesetSymlinks);
}
public ArtifactPathResolver pathResolver() {
return pathResolver;
}
public ActionInputMap getImportantInputMap() {
return importantInputMap;
}
public Map<Artifact, FilesetOutputTree> getExpandedFilesets() {
return filesets;
}
@Nullable
public FileArtifactValue getFileArtifactValue(Artifact artifact) {
return importantInputMap.getInputMetadata(artifact);
}
@Nullable
public FileArtifactValue getBaselineCoverageValue() {
return baselineCoverageValue;
}
/** Visits the expansion of the given artifacts. */
public void visitArtifacts(Iterable<Artifact> artifacts, ArtifactReceiver receiver) {
for (Artifact artifact : artifacts) {
if (artifact.isMiddlemanArtifact()) {
continue;
}
if (artifact.isFileset()) {
if (expandFilesets) {
visitFileset(artifact, receiver);
}
} else if (artifact.isTreeArtifact()) {
FileArtifactValue treeArtifactMetadata = importantInputMap.getInputMetadata(artifact);
if (treeArtifactMetadata == null) {
BugReport.sendBugReport(
new IllegalStateException(
String.format(
"missing artifact metadata for tree artifact: %s",
artifact.toDebugString())));
}
if (FileArtifactValue.OMITTED_FILE_MARKER.equals(treeArtifactMetadata)) {
// Expansion can be missing for omitted tree artifacts -- skip the whole tree.
continue;
}
TreeArtifactValue treeValue =
checkNotNull(
treeArtifacts.get(artifact), "Missing expansion for tree artifact: %s", artifact);
for (Artifact child : treeValue.getChildren()) {
receiver.accept(child);
}
} else {
receiver.accept(artifact);
}
}
}
private void visitFileset(Artifact filesetArtifact, ArtifactReceiver receiver) {
FilesetOutputTree filesetOutput = filesets.get(filesetArtifact);
filesetOutput.visitSymlinks(
fullyResolveFilesetLinks
? RelativeSymlinkBehaviorWithoutError.RESOLVE_FULLY
: RelativeSymlinkBehaviorWithoutError.RESOLVE,
(name, target, metadata) ->
receiver.acceptFilesetMapping(filesetArtifact, name, execRoot.getRelative(target)));
}
@Override
public ImmutableSortedSet<TreeFileArtifact> expandTreeArtifact(Artifact treeArtifact) {
checkArgument(treeArtifact.isTreeArtifact(), treeArtifact);
TreeArtifactValue treeValue = treeArtifacts.get(treeArtifact);
return treeValue != null ? treeValue.getChildren() : ImmutableSortedSet.of();
}
@Nullable
@Override
public ArchivedTreeArtifact getArchivedTreeArtifact(Artifact treeArtifact) {
checkArgument(treeArtifact.isTreeArtifact(), treeArtifact);
TreeArtifactValue treeValue = treeArtifacts.get(treeArtifact);
return treeValue != null ? treeValue.getArchivedArtifact() : null;
}
@Override
public FilesetOutputTree expandFileset(Artifact fileset) throws MissingExpansionException {
checkArgument(fileset.isFileset(), fileset);
checkState(expandFilesets, "Fileset expansion disabled, cannot expand %s", fileset);
FilesetOutputTree filesetOutput = filesets.get(fileset);
if (filesetOutput == null) {
throw new MissingExpansionException("Missing expansion for fileset: " + fileset);
}
return filesetOutput;
}
/** A function that accepts an {@link Artifact}. */
public interface ArtifactReceiver {
void accept(Artifact artifact);
void acceptFilesetMapping(Artifact fileset, PathFragment relName, Path targetFile);
}
/** A factory for {@link ArtifactPathResolver}. */
public interface PathResolverFactory {
ArtifactPathResolver createPathResolverForArtifactValues(
ActionInputMap actionInputMap,
Map<Artifact, ImmutableSortedSet<TreeFileArtifact>> treeArtifacts,
Map<Artifact, FilesetOutputTree> filesets,
String workspaceName);
boolean shouldCreatePathResolverForArtifactValues();
}
}