blob: e3f1d8e17d1cfae8920365cfb6034353687508e7 [file] [log] [blame]
// 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.rules.cpp;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.actions.AbstractAction;
import com.google.devtools.build.lib.actions.ActionEnvironment;
import com.google.devtools.build.lib.actions.ActionExecutionContext;
import com.google.devtools.build.lib.actions.ActionExecutionException;
import com.google.devtools.build.lib.actions.ActionKeyContext;
import com.google.devtools.build.lib.actions.ActionOwner;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.CommandLineExpansionException;
import com.google.devtools.build.lib.actions.CommandLines;
import com.google.devtools.build.lib.actions.CommandLines.CommandLineLimits;
import com.google.devtools.build.lib.actions.ResourceSet;
import com.google.devtools.build.lib.actions.RunfilesSupplier;
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.util.Fingerprint;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
/**
* Action used by LtoBackendArtifacts to create an LtoBackendAction. Similar to {@link SpawnAction},
* except that inputs are discovered from the imports file created by the ThinLTO indexing step for
* each backend artifact.
*
* <p>See {@link LtoBackendArtifacts} for a high level description of the ThinLTO build process. The
* LTO indexing step takes all bitcode .o files and decides which other .o file symbols can be
* imported/inlined. The additional input files for each backend action are then written to an
* imports file. Therefore these new inputs must be discovered here by subsetting the imports paths
* from the set of all bitcode artifacts, before executing the backend action.
*
* <p>For more information on ThinLTO see
* http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html.
*/
public final class LtoBackendAction extends SpawnAction {
private Collection<Artifact> mandatoryInputs;
private Map<PathFragment, Artifact> bitcodeFiles;
private Artifact imports;
private static final String GUID = "72ce1eca-4625-4e24-a0d8-bb91bb8b0e0e";
public LtoBackendAction(
Collection<Artifact> inputs,
Map<PathFragment, Artifact> allBitcodeFiles,
Artifact importsFile,
Collection<Artifact> outputs,
Artifact primaryOutput,
ActionOwner owner,
CommandLines argv,
CommandLineLimits commandLineLimits,
boolean isShellCommand,
ActionEnvironment env,
Map<String, String> executionInfo,
CharSequence progressMessage,
RunfilesSupplier runfilesSupplier,
String mnemonic) {
super(
owner,
ImmutableList.<Artifact>of(),
inputs,
outputs,
primaryOutput,
AbstractAction.DEFAULT_RESOURCE_SET,
argv,
commandLineLimits,
isShellCommand,
env,
ImmutableMap.copyOf(executionInfo),
progressMessage,
runfilesSupplier,
mnemonic,
false,
null);
mandatoryInputs = inputs;
Preconditions.checkState(
(bitcodeFiles == null) == (imports == null),
"Either both or neither bitcodeFiles and imports files should be null");
bitcodeFiles = allBitcodeFiles;
imports = importsFile;
}
@Override
public boolean mayExecuteAsync() {
return false;
}
@Override
public boolean discoversInputs() {
return imports != null;
}
private Set<Artifact> computeBitcodeInputs(Collection<PathFragment> inputPaths) {
HashSet<Artifact> bitcodeInputs = new HashSet<>();
for (PathFragment inputPath : inputPaths) {
Artifact inputArtifact = bitcodeFiles.get(inputPath);
if (inputArtifact != null) {
bitcodeInputs.add(inputArtifact);
}
}
return bitcodeInputs;
}
@Nullable
@Override
public Iterable<Artifact> discoverInputs(ActionExecutionContext actionExecutionContext)
throws ActionExecutionException, InterruptedException {
// Build set of files this LTO backend artifact will import from.
HashSet<PathFragment> importSet = new HashSet<>();
try {
for (String line :
FileSystemUtils.iterateLinesAsLatin1(actionExecutionContext.getInputPath(imports))) {
if (!line.isEmpty()) {
PathFragment execPath = PathFragment.create(line);
if (execPath.isAbsolute()) {
throw new ActionExecutionException(
"Absolute paths not allowed in imports file "
+ actionExecutionContext.getInputPath(imports)
+ ": "
+ execPath,
this,
false);
}
importSet.add(PathFragment.create(line));
}
}
} catch (IOException e) {
throw new ActionExecutionException(
"error iterating imports file " + actionExecutionContext.getInputPath(imports),
e,
this,
false);
}
// Convert the import set of paths to the set of bitcode file artifacts.
Set<Artifact> bitcodeInputSet = computeBitcodeInputs(importSet);
if (bitcodeInputSet.size() != importSet.size()) {
throw new ActionExecutionException(
"error computing inputs from imports file "
+ actionExecutionContext.getInputPath(imports),
this,
false);
}
updateInputs(createInputs(bitcodeInputSet, getMandatoryInputs()));
return bitcodeInputSet;
}
@Override
public Collection<Artifact> getMandatoryInputs() {
return mandatoryInputs;
}
private static Iterable<Artifact> createInputs(
Set<Artifact> newInputs, Collection<Artifact> curInputs) {
Set<Artifact> result = new LinkedHashSet<>(newInputs);
result.addAll(curInputs);
return result;
}
@Override
public Iterable<Artifact> getAllowedDerivedInputs() {
return bitcodeFiles.values();
}
@Override
protected void computeKey(ActionKeyContext actionKeyContext, Fingerprint fp) {
fp.addString(GUID);
try {
fp.addStrings(getArguments());
} catch (CommandLineExpansionException e) {
throw new AssertionError("LtoBackendAction command line expansion cannot fail");
}
fp.addString(getMnemonic());
fp.addPaths(getRunfilesSupplier().getRunfilesDirs());
ImmutableList<Artifact> runfilesManifests = getRunfilesSupplier().getManifests();
for (Artifact runfilesManifest : runfilesManifests) {
fp.addPath(runfilesManifest.getExecPath());
}
for (Artifact input : getMandatoryInputs()) {
fp.addPath(input.getExecPath());
}
if (imports != null) {
for (PathFragment bitcodePath : bitcodeFiles.keySet()) {
fp.addPath(bitcodePath);
}
fp.addPath(imports.getExecPath());
}
env.addTo(fp);
fp.addStringMap(getExecutionInfo());
}
/** Builder class to construct {@link LtoBackendAction} instances. */
public static class Builder extends SpawnAction.Builder {
private Map<PathFragment, Artifact> bitcodeFiles;
private Artifact imports;
public Builder addImportsInfo(
Map<PathFragment, Artifact> allBitcodeFiles, Artifact importsFile) {
this.bitcodeFiles = allBitcodeFiles;
this.imports = importsFile;
return this;
}
@Override
protected SpawnAction createSpawnAction(
ActionOwner owner,
NestedSet<Artifact> tools,
NestedSet<Artifact> inputsAndTools,
ImmutableList<Artifact> outputs,
Artifact primaryOutput,
ResourceSet resourceSet,
CommandLines commandLines,
CommandLineLimits commandLineLimits,
boolean isShellCommand,
ActionEnvironment env,
ImmutableMap<String, String> executionInfo,
CharSequence progressMessage,
RunfilesSupplier runfilesSupplier,
String mnemonic) {
return new LtoBackendAction(
inputsAndTools.toCollection(),
bitcodeFiles,
imports,
outputs,
primaryOutput,
owner,
commandLines,
commandLineLimits,
isShellCommand,
env,
executionInfo,
progressMessage,
runfilesSupplier,
mnemonic);
}
}
}