blob: 5b20c499137be3bde2987004356be14dd00d6680 [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 com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
import com.google.devtools.build.lib.actions.Artifact.ArtifactExpanderImpl;
import com.google.devtools.build.lib.actions.FilesetManifest.RelativeSymlinkBehavior;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/**
* {@link CompletionContext} contains an {@link ArtifactExpander} and {@link ArtifactPathResolver}
* used to resolve output files during a {@link
* com.google.devtools.build.lib.skyframe.CompletionFunction} evaluation.
*
* <p>Note that output Artifacts may in fact refer to aggregations, namely tree artifacts and
* Filesets. We expand these aggregations when visiting artifacts.
*/
@AutoValue
public abstract class CompletionContext {
public static final CompletionContext FAILED_COMPLETION_CTX = createNull();
public abstract ArtifactExpander expander();
public abstract ArtifactPathResolver pathResolver();
public abstract boolean expandFilesets();
@Nullable
public abstract Path execRoot();
public static CompletionContext create(
Map<Artifact, Collection<Artifact>> expandedArtifacts,
Map<Artifact, ImmutableList<FilesetOutputSymlink>> expandedFilesets,
boolean expandFilesets,
ActionInputMap inputMap,
PathResolverFactory pathResolverFactory,
Path execRoot,
String workspaceName)
throws IOException {
ArtifactExpander expander = new ArtifactExpanderImpl(expandedArtifacts, expandedFilesets);
ArtifactPathResolver pathResolver =
pathResolverFactory.shouldCreatePathResolverForArtifactValues()
? pathResolverFactory.createPathResolverForArtifactValues(
inputMap, expandedArtifacts, expandedFilesets, workspaceName)
: ArtifactPathResolver.IDENTITY;
return new AutoValue_CompletionContext(expander, pathResolver, expandFilesets, execRoot);
}
private static CompletionContext createNull() {
return new AutoValue_CompletionContext(
(artifact, output) -> {}, ArtifactPathResolver.IDENTITY, false, null);
}
public void visitArtifacts(Iterable<Artifact> artifacts, ArtifactReceiver receiver) {
for (Artifact artifact : artifacts) {
if (artifact.isMiddlemanArtifact()) {
continue;
} else if (artifact.isFileset()) {
if (expandFilesets()) {
visitFileset(artifact, receiver);
}
} else if (artifact.isTreeArtifact()) {
List<Artifact> expandedArtifacts = new ArrayList<>();
expander().expand(artifact, expandedArtifacts);
for (Artifact expandedArtifact : expandedArtifacts) {
receiver.accept(expandedArtifact);
}
} else {
receiver.accept(artifact);
}
}
}
private void visitFileset(Artifact filesetArtifact, ArtifactReceiver receiver) {
ImmutableList<FilesetOutputSymlink> links = expander().getFileset(filesetArtifact);
FilesetManifest filesetManifest;
try {
filesetManifest =
FilesetManifest.constructFilesetManifest(
links, PathFragment.EMPTY_FRAGMENT, RelativeSymlinkBehavior.RESOLVE);
} catch (IOException e) {
// Unexpected: RelativeSymlinkBehavior.RESOLVE should not throw.
throw new IllegalStateException(e);
}
for (Map.Entry<PathFragment, String> mapping : filesetManifest.getEntries().entrySet()) {
String targetFile = mapping.getValue();
PathFragment locationInFileset = mapping.getKey();
receiver.acceptFilesetMapping(
filesetArtifact, locationInFileset, execRoot().getRelative(targetFile));
}
}
/** 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, Collection<Artifact>> expandedArtifacts,
Map<Artifact, ImmutableList<FilesetOutputSymlink>> filesets,
String workspaceName)
throws IOException;
boolean shouldCreatePathResolverForArtifactValues();
}
}