// Copyright 2016 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.analysis.actions; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.actions.ActionAnalysisMetadata; import com.google.devtools.build.lib.actions.ActionExecutionContext; import com.google.devtools.build.lib.actions.ActionInputHelper; import com.google.devtools.build.lib.actions.ActionOwner; import com.google.devtools.build.lib.actions.ActionTemplate; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact; import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact; import com.google.devtools.build.lib.actions.ArtifactOwner; import com.google.devtools.build.lib.actions.CommandLine; import com.google.devtools.build.lib.analysis.FilesToRunProvider; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.Map; /** * An {@link ActionTemplate} that expands into {@link SpawnAction}s at execution time. */ public final class SpawnActionTemplate implements ActionTemplate { private final SpecialArtifact inputTreeArtifact; private final SpecialArtifact outputTreeArtifact; private final NestedSet commonInputs; private final NestedSet allInputs; private final NestedSet commonTools; private final ActionOwner actionOwner; private final String mnemonic; private final OutputPathMapper outputPathMapper; private final SpawnAction.Builder spawnActionBuilder; private final CustomCommandLine commandLineTemplate; /** * Interface providing mapping between expanded input files under the input TreeArtifact and * parent-relative paths of their associated output file under the output TreeArtifact. * *

Users of SpawnActionTemplate must provide a mapper object implementing this interface. * SpawnActionTemplate uses the mapper to query for the path of output artifact associated with * each input {@link TreeFileArtifact} resolved at execution time. */ public interface OutputPathMapper { /** * Given the input {@link TreeFileArtifact}, returns the parent-relative path of the associated * output {@link TreeFileArtifact}. * * @param input the input {@link TreeFileArtifact} */ PathFragment parentRelativeOutputPath(TreeFileArtifact input); } private SpawnActionTemplate( ActionOwner actionOwner, SpecialArtifact inputTreeArtifact, SpecialArtifact outputTreeArtifact, NestedSet commonInputs, NestedSet commonTools, OutputPathMapper outputPathMapper, CustomCommandLine commandLineTemplate, String mnemonic, SpawnAction.Builder spawnActionBuilder) { this.inputTreeArtifact = inputTreeArtifact; this.outputTreeArtifact = outputTreeArtifact; this.commonTools = commonTools; this.commonInputs = NestedSetBuilder.stableOrder() .addTransitive(commonInputs) .addTransitive(commonTools) .build(); this.allInputs = NestedSetBuilder.stableOrder() .add(inputTreeArtifact) .addTransitive(this.commonInputs) .build(); this.outputPathMapper = outputPathMapper; this.actionOwner = actionOwner; this.mnemonic = mnemonic; this.spawnActionBuilder = spawnActionBuilder; this.commandLineTemplate = commandLineTemplate; } @Override public Iterable generateActionForInputArtifacts( Iterable inputTreeFileArtifacts, ArtifactOwner artifactOwner) { ImmutableList.Builder expandedActions = new ImmutableList.Builder<>(); for (TreeFileArtifact inputTreeFileArtifact : inputTreeFileArtifacts) { PathFragment parentRelativeOutputPath = outputPathMapper.parentRelativeOutputPath(inputTreeFileArtifact); TreeFileArtifact outputTreeFileArtifact = ActionInputHelper.treeFileArtifact( outputTreeArtifact, parentRelativeOutputPath, artifactOwner); expandedActions.add(createAction(inputTreeFileArtifact, outputTreeFileArtifact)); } return expandedActions.build(); } /** * Returns a SpawnAction that takes inputTreeFileArtifact as input and generates * outputTreeFileArtifact. */ private SpawnAction createAction( TreeFileArtifact inputTreeFileArtifact, TreeFileArtifact outputTreeFileArtifact) { SpawnAction.Builder actionBuilder = new SpawnAction.Builder(spawnActionBuilder); actionBuilder.addInput(inputTreeFileArtifact); actionBuilder.addOutput(outputTreeFileArtifact); CommandLine commandLine = commandLineTemplate.evaluateTreeFileArtifacts( ImmutableList.of(inputTreeFileArtifact, outputTreeFileArtifact)); actionBuilder.addCommandLine(commandLine); // Note that we pass in nulls below because SpawnActionTemplate does not support param file, and // it does not use any default value for executable or shell environment. They must be set // explicitly via builder method #setExecutable and #setEnvironment. return actionBuilder.buildForActionTemplate(getOwner()); } /** * Returns the input TreeArtifact. * *

This method is called by Skyframe to expand the input TreeArtifact into child * TreeFileArtifacts. Skyframe then expands this SpawnActionTemplate with the TreeFileArtifacts * through {@link #generateActionForInputArtifacts}. */ @Override public Artifact getInputTreeArtifact() { return inputTreeArtifact; } /** Returns the output TreeArtifact. */ @Override public Artifact getOutputTreeArtifact() { return outputTreeArtifact; } @Override public ActionOwner getOwner() { return actionOwner; } @Override public final String getMnemonic() { return mnemonic; } @Override public Iterable getTools() { return commonTools; } @Override public Iterable getInputs() { return allInputs; } @Override public ImmutableSet getOutputs() { return ImmutableSet.of(outputTreeArtifact); } @Override public Iterable getMandatoryInputs() { return getInputs(); } @Override public Iterable getInputFilesForExtraAction( ActionExecutionContext actionExecutionContext) { return ImmutableList.of(); } @Override public ImmutableSet getMandatoryOutputs() { return ImmutableSet.of(); } @Override public Artifact getPrimaryInput() { return inputTreeArtifact; } @Override public Artifact getPrimaryOutput() { return outputTreeArtifact; } @Override public Iterable getClientEnvironmentVariables() { return spawnActionBuilder.buildForActionTemplate(getOwner()).getClientEnvironmentVariables(); } @Override public boolean shouldReportPathPrefixConflict(ActionAnalysisMetadata action) { return this != action; } @Override public MiddlemanType getActionType() { return MiddlemanType.NORMAL; } @Override public String prettyPrint() { return String.format("action template with output TreeArtifact %s", outputTreeArtifact.prettyPrint()); } /** Builder class to construct {@link SpawnActionTemplate} instances. */ public static class Builder { private String actionTemplateMnemonic = "Unknown"; private OutputPathMapper outputPathMapper; private CustomCommandLine commandLineTemplate; private PathFragment executable; private final SpecialArtifact inputTreeArtifact; private final SpecialArtifact outputTreeArtifact; private final NestedSetBuilder inputsBuilder = NestedSetBuilder.stableOrder(); private final NestedSetBuilder toolsBuilder = NestedSetBuilder.stableOrder(); private final SpawnAction.Builder spawnActionBuilder; /** * Creates a {@link SpawnActionTemplate} builder. * * @param inputTreeArtifact the required input TreeArtifact. * @param outputTreeArtifact the required output TreeArtifact. */ public Builder(SpecialArtifact inputTreeArtifact, SpecialArtifact outputTreeArtifact) { Preconditions.checkState( inputTreeArtifact.isTreeArtifact() && outputTreeArtifact.isTreeArtifact(), "Either %s or %s is not a TreeArtifact", inputTreeArtifact, outputTreeArtifact); this.inputTreeArtifact = inputTreeArtifact; this.outputTreeArtifact = outputTreeArtifact; this.spawnActionBuilder = new SpawnAction.Builder(); } /** * Sets the mnemonics for both the {@link SpawnActionTemplate} and expanded {@link SpawnAction}. */ public Builder setMnemonics(String actionTemplateMnemonic, String expandedActionMnemonic) { this.actionTemplateMnemonic = actionTemplateMnemonic; spawnActionBuilder.setMnemonic(expandedActionMnemonic); return this; } /** * Adds common tool artifacts. All common tool artifacts will be added as tool artifacts for * expanded actions. */ public Builder addCommonTools(Iterable artifacts) { toolsBuilder.addAll(artifacts); spawnActionBuilder.addTools(artifacts); return this; } /** * Adds common tool artifacts. All common tool artifacts will be added as input tool artifacts * for expanded actions. */ public Builder addCommonTool(FilesToRunProvider tool) { toolsBuilder.addTransitive(tool.getFilesToRun()); spawnActionBuilder.addTool(tool); return this; } /** * Adds common input artifacts. All common input artifacts will be added as input artifacts for * expanded actions. */ public Builder addCommonInputs(Iterable artifacts) { inputsBuilder.addAll(artifacts); spawnActionBuilder.addInputs(artifacts); return this; } /** * Adds transitive common input artifacts. All common input artifacts will be added as input * artifacts for expanded actions. */ public Builder addCommonTransitiveInputs(NestedSet artifacts) { inputsBuilder.addTransitive(artifacts); spawnActionBuilder.addTransitiveInputs(artifacts); return this; } /** Sets the map of environment variables for expanded actions. */ @Deprecated // TODO(ulfjack): Add env variables to the common environment, rather than replacing // it wholesale, which ignores --action_env (unless the client code explicitly handles it). public Builder setEnvironment(Map environment) { spawnActionBuilder.setEnvironment(environment); return this; } /** * Sets the map of execution info for expanded actions. */ public Builder setExecutionInfo(Map executionInfo) { spawnActionBuilder.setExecutionInfo(executionInfo); return this; } /** * Sets the executable used by expanded actions as a configured target. Automatically adds the * files to run to the tools and uses the executable of the target as the executable. * *

Calling this method overrides any previous values set via calls to * {@link #setExecutable(Artifact)} and {@link #setExecutable(PathFragment)}. */ public Builder setExecutable(FilesToRunProvider executableProvider) { Preconditions.checkArgument( executableProvider.getExecutable() != null, "The target does not have an executable"); spawnActionBuilder.setExecutable(executableProvider); addCommonTool(executableProvider); this.executable = executableProvider.getExecutable().getExecPath(); return this; } /** * Sets the executable path used by expanded actions. The path is interpreted relative to the * execution root. * *

Calling this method overrides any previous values set via calls to * {@link #setExecutable(Artifact)} and {@link #setExecutable(FilesToRunProvider)}. */ public Builder setExecutable(PathFragment executable) { spawnActionBuilder.setExecutable(executable); this.executable = executable; return this; } /** * Sets the executable artifact used by expanded actions. The path is interpreted relative to * the execution root. * *

Calling this method overrides any previous values set via calls to * {@link #setExecutable(FilesToRunProvider)} and {@link #setExecutable(PathFragment)}. */ public Builder setExecutable(Artifact artifact) { spawnActionBuilder.setExecutable(artifact); addCommonTools(ImmutableList.of(artifact)); this.executable = artifact.getExecPath(); return this; } /** * Sets the command line template used to expand actions. */ public Builder setCommandLineTemplate(CustomCommandLine commandLineTemplate) { this.commandLineTemplate = commandLineTemplate; return this; } /** * Sets the {@link OutputPathMapper} object used to get the parent-relative paths of output * {@link TreeFileArtifact}. */ public Builder setOutputPathMapper(OutputPathMapper outputPathMapper) { this.outputPathMapper = outputPathMapper; return this; } /** * Builds and returns the {@link SpawnActionTemplate} using the accumulated builder information. * * @param actionOwner the action owner of the SpawnActionTemplate to be built. */ public SpawnActionTemplate build(ActionOwner actionOwner) { Preconditions.checkNotNull(executable); return new SpawnActionTemplate( actionOwner, Preconditions.checkNotNull(inputTreeArtifact), Preconditions.checkNotNull(outputTreeArtifact), inputsBuilder.build(), toolsBuilder.build(), Preconditions.checkNotNull(outputPathMapper), Preconditions.checkNotNull(commandLineTemplate), actionTemplateMnemonic, spawnActionBuilder); } } }