| // 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.ActionLookupValue.ActionLookupKey; | |
| import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander; | |
| import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact; | |
| import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact; | |
| import com.google.devtools.build.lib.collect.nestedset.NestedSet; | |
| 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().toList(), 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; | |
| BasicActionInput(String path) { | |
| this.path = Preconditions.checkNotNull(path); | |
| } | |
| // TODO(lberki): Plumb this flag from InputTree.build() somehow. | |
| @Override | |
| public boolean isSymlink() { | |
| return false; | |
| } | |
| @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.SpecialArtifact parent, PathFragment relativePath) { | |
| TreeFileArtifact result = | |
| treeFileArtifactWithNoGeneratingActionSet(parent, relativePath, parent.getArtifactOwner()); | |
| result.setGeneratingActionKey(parent.getGeneratingActionKey()); | |
| return result; | |
| } | |
| /** | |
| * Instantiates a concrete TreeFileArtifact with the given parent Artifact and path relative to | |
| * that Artifact. | |
| */ | |
| public static TreeFileArtifact treeFileArtifact( | |
| Artifact.SpecialArtifact parent, String relativePath) { | |
| return treeFileArtifact(parent, PathFragment.create(relativePath)); | |
| } | |
| public static TreeFileArtifact treeFileArtifactWithNoGeneratingActionSet( | |
| SpecialArtifact parent, PathFragment relativePath, ActionLookupKey artifactOwner) { | |
| Preconditions.checkState( | |
| parent.isTreeArtifact(), "Given parent %s must be a TreeArtifact", parent); | |
| return new TreeFileArtifact(parent, relativePath, artifactOwner); | |
| } | |
| /** Returns an Iterable of TreeFileArtifacts with the given parent and parent relative paths. */ | |
| public static Iterable<TreeFileArtifact> asTreeFileArtifacts( | |
| final Artifact.SpecialArtifact 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.SpecialArtifact 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( | |
| NestedSet<? extends ActionInput> inputs, ArtifactExpander artifactExpander) { | |
| List<ActionInput> result = new ArrayList<>(); | |
| List<Artifact> containedArtifacts = new ArrayList<>(); | |
| for (ActionInput input : inputs.toList()) { | |
| 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()); | |
| } | |
| } |