// 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.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.util.Preconditions; 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); } @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()); } private static final Function<String, ActionInput> FROM_PATH = new Function<String, ActionInput>() { @Override public ActionInput apply(String path) { return fromPath(path); } }; /** * 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, FROM_PATH); } /** * 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, new Function<PathFragment, TreeFileArtifact>() { @Override public TreeFileArtifact apply(PathFragment pathFragment) { return 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 = new Function<ActionInput, String>() { @Override public String apply(ActionInput input) { return input.getExecPathString(); } }; public static Iterable<String> toExecPaths(Iterable<? extends ActionInput> artifacts) { return Iterables.transform(artifacts, EXEC_PATH_STRING_FORMATTER); } }