blob: 1b0bf6136adb8e65195702796acac06a0967581a [file] [log] [blame]
// Copyright 2014 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.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Preconditions;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* Helper utility to create ActionInput instances.
*/
public final class ActionInputHelper {
private ActionInputHelper() {
}
@VisibleForTesting
public static ArtifactExpander actionGraphArtifactExpander(
final ActionGraph actionGraph) {
return new ArtifactExpander() {
@Override
public void expand(Artifact mm, Collection<? super Artifact> output) {
// Skyframe is stricter in that it checks that "mm" is a input of the action, because
// it cannot expand arbitrary middlemen without access to a global action graph.
// We could check this constraint here too, but it seems unnecessary. This code is
// going away anyway.
Preconditions.checkArgument(mm.isMiddlemanArtifact(),
"%s is not a middleman artifact", mm);
ActionAnalysisMetadata middlemanAction = actionGraph.getGeneratingAction(mm);
Preconditions.checkState(middlemanAction != null, mm);
// TODO(bazel-team): Consider expanding recursively or throwing an exception here.
// Most likely, this code will cause silent errors if we ever have a middleman that
// contains a middleman.
if (middlemanAction.getActionType() == Action.MiddlemanType.AGGREGATING_MIDDLEMAN) {
Artifact.addNonMiddlemanArtifacts(middlemanAction.getInputs(), output,
Functions.<Artifact>identity());
}
}
};
}
/**
* Most ActionInputs are created and never used again. On the off chance that one is, however, we
* implement equality via path comparison. Since file caches are keyed by ActionInput, equality
* checking does come up.
*/
private static class BasicActionInput implements ActionInput {
private final String path;
public BasicActionInput(String path) {
this.path = Preconditions.checkNotNull(path);
Preconditions.checkArgument(!path.isEmpty());
}
@Override
public String getExecPathString() {
return path;
}
@Override
public PathFragment getExecPath() {
return PathFragment.create(path);
}
@Override
public int hashCode() {
return path.hashCode();
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null) {
return false;
}
if (!this.getClass().equals(other.getClass())) {
return false;
}
return this.path.equals(((BasicActionInput) other).path);
}
@Override
public String toString() {
return "BasicActionInput: " + path;
}
}
/**
* Creates an ActionInput with just the given relative path and no digest.
*
* @param path the relative path of the input.
* @return a ActionInput.
*/
public static ActionInput fromPath(String path) {
return new BasicActionInput(path);
}
/**
* Creates an ActionInput with just the given relative path and no digest.
*
* @param path the relative path of the input.
* @return a ActionInput.
*/
public static ActionInput fromPath(PathFragment path) {
return fromPath(path.getPathString());
}
/**
* Creates a sequence of {@link ActionInput}s from a sequence of string paths.
*/
public static Collection<ActionInput> fromPaths(Collection<String> paths) {
return Collections2.transform(paths, ActionInputHelper::fromPath);
}
/**
* Instantiates a concrete TreeFileArtifact with the given parent Artifact and path relative to
* that Artifact.
*/
public static TreeFileArtifact treeFileArtifact(Artifact parent, PathFragment relativePath) {
Preconditions.checkState(parent.isTreeArtifact(),
"Given parent %s must be a TreeArtifact", parent);
return new TreeFileArtifact(parent, relativePath);
}
public static TreeFileArtifact treeFileArtifact(
Artifact parent, PathFragment relativePath, ArtifactOwner artifactOwner) {
Preconditions.checkState(parent.isTreeArtifact(),
"Given parent %s must be a TreeArtifact", parent);
return new TreeFileArtifact(
parent,
relativePath,
artifactOwner);
}
/**
* Instantiates a concrete TreeFileArtifact with the given parent Artifact and path relative to
* that Artifact.
*/
public static TreeFileArtifact treeFileArtifact(Artifact parent, String relativePath) {
return treeFileArtifact(parent, PathFragment.create(relativePath));
}
/** Returns an Iterable of TreeFileArtifacts with the given parent and parent relative paths. */
public static Iterable<TreeFileArtifact> asTreeFileArtifacts(
final Artifact parent, Iterable<? extends PathFragment> parentRelativePaths) {
Preconditions.checkState(parent.isTreeArtifact(),
"Given parent %s must be a TreeArtifact", parent);
return Iterables.transform(
parentRelativePaths, pathFragment -> treeFileArtifact(parent, pathFragment));
}
/** Returns a Set of TreeFileArtifacts with the given parent and parent-relative paths. */
public static Set<TreeFileArtifact> asTreeFileArtifacts(
final Artifact parent, Set<? extends PathFragment> parentRelativePaths) {
Preconditions.checkState(parent.isTreeArtifact(),
"Given parent %s must be a TreeArtifact", parent);
ImmutableSet.Builder<TreeFileArtifact> builder = ImmutableSet.builder();
for (PathFragment path : parentRelativePaths) {
builder.add(treeFileArtifact(parent, path));
}
return builder.build();
}
/**
* Expands middleman artifacts in a sequence of {@link ActionInput}s.
*
* <p>Non-middleman artifacts are returned untouched.
*/
public static List<ActionInput> expandArtifacts(Iterable<? extends ActionInput> inputs,
ArtifactExpander artifactExpander) {
List<ActionInput> result = new ArrayList<>();
List<Artifact> containedArtifacts = new ArrayList<>();
for (ActionInput input : inputs) {
if (!(input instanceof Artifact)) {
result.add(input);
continue;
}
containedArtifacts.add((Artifact) input);
}
Artifact.addExpandedArtifacts(containedArtifacts, result, artifactExpander);
return result;
}
/** Formatter for execPath String output. Public because {@link Artifact} uses it directly. */
public static final Function<ActionInput, String> EXEC_PATH_STRING_FORMATTER =
ActionInput::getExecPathString;
public static Iterable<String> toExecPaths(Iterable<? extends ActionInput> artifacts) {
return Iterables.transform(artifacts, EXEC_PATH_STRING_FORMATTER);
}
/** Returns the {@link Path} for an {@link ActionInput}. */
public static Path toInputPath(ActionInput input, Path execRoot) {
Preconditions.checkNotNull(input, "input");
Preconditions.checkNotNull(execRoot, "execRoot");
return (input instanceof Artifact)
? ((Artifact) input).getPath()
: execRoot.getRelative(input.getExecPath());
}
}