| // 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.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.devtools.build.lib.actions.AbstractAction; |
| import com.google.devtools.build.lib.actions.ActionExecutionContext; |
| import com.google.devtools.build.lib.actions.ActionExecutionException; |
| import com.google.devtools.build.lib.actions.ActionOwner; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.actions.ArtifactResolver; |
| import com.google.devtools.build.lib.actions.PackageRootResolutionException; |
| import com.google.devtools.build.lib.actions.PackageRootResolver; |
| 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; |
| import javax.annotation.concurrent.GuardedBy; |
| |
| /** |
| * 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 { |
| // This can be read/written from multiple threads, and so accesses should be synchronized. |
| @GuardedBy("this") |
| private boolean inputsKnown; |
| |
| 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, |
| ActionOwner owner, |
| CommandLine argv, |
| Map<String, String> environment, |
| Set<String> clientEnvironmentVariables, |
| Map<String, String> executionInfo, |
| String progressMessage, |
| ImmutableMap<PathFragment, Artifact> inputManifests, |
| String mnemonic) { |
| super( |
| owner, |
| ImmutableList.<Artifact>of(), |
| inputs, |
| outputs, |
| AbstractAction.DEFAULT_RESOURCE_SET, |
| argv, |
| ImmutableMap.copyOf(environment), |
| ImmutableSet.copyOf(clientEnvironmentVariables), |
| ImmutableMap.copyOf(executionInfo), |
| progressMessage, |
| ImmutableMap.copyOf(inputManifests), |
| mnemonic, |
| false, |
| null); |
| |
| inputsKnown = false; |
| mandatoryInputs = inputs; |
| bitcodeFiles = allBitcodeFiles; |
| imports = importsFile; |
| } |
| |
| @Override |
| public boolean discoversInputs() { |
| return true; |
| } |
| |
| 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(imports.getPath())) { |
| if (!line.isEmpty()) { |
| PathFragment execPath = new PathFragment(line); |
| if (execPath.isAbsolute()) { |
| throw new ActionExecutionException( |
| "Absolute paths not allowed in imports file " + imports.getPath() + ": " + execPath, |
| this, |
| false); |
| } |
| importSet.add(new PathFragment(line)); |
| } |
| } |
| } catch (IOException e) { |
| throw new ActionExecutionException( |
| "error iterating imports file " + imports.getPath(), 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 " + imports.getPath(), this, false); |
| } |
| updateInputs(createInputs(bitcodeInputSet, getMandatoryInputs())); |
| return bitcodeInputSet; |
| } |
| |
| @Override |
| public synchronized boolean inputsKnown() { |
| return inputsKnown; |
| } |
| |
| @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 synchronized void updateInputs(Iterable<Artifact> discoveredInputs) { |
| setInputs(discoveredInputs); |
| inputsKnown = true; |
| } |
| |
| @Nullable |
| @Override |
| public Iterable<Artifact> resolveInputsFromCache( |
| ArtifactResolver artifactResolver, |
| PackageRootResolver resolver, |
| Collection<PathFragment> inputPaths) |
| throws PackageRootResolutionException { |
| Set<Artifact> bitcodeInputSet = computeBitcodeInputs(inputPaths); |
| return createInputs(bitcodeInputSet, getMandatoryInputs()); |
| } |
| |
| @Override |
| public void execute(ActionExecutionContext actionExecutionContext) |
| throws ActionExecutionException, InterruptedException { |
| super.execute(actionExecutionContext); |
| |
| synchronized (this) { |
| inputsKnown = true; |
| } |
| } |
| |
| @Override |
| protected String computeKey() { |
| Fingerprint f = new Fingerprint(); |
| f.addString(GUID); |
| f.addStrings(argv.arguments()); |
| f.addString(getMnemonic()); |
| f.addInt(inputManifests.size()); |
| for (Map.Entry<PathFragment, Artifact> input : inputManifests.entrySet()) { |
| f.addString(input.getKey().getPathString() + "/"); |
| f.addPath(input.getValue().getExecPath()); |
| } |
| for (Artifact input : getMandatoryInputs()) { |
| f.addPath(input.getExecPath()); |
| } |
| for (PathFragment bitcodePath : bitcodeFiles.keySet()) { |
| f.addPath(bitcodePath); |
| } |
| f.addPath(imports.getExecPath()); |
| f.addStringMap(getEnvironment()); |
| f.addStringMap(getExecutionInfo()); |
| return f.hexDigestAndReset(); |
| } |
| |
| /** 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 |
| SpawnAction createSpawnAction( |
| ActionOwner owner, |
| NestedSet<Artifact> tools, |
| NestedSet<Artifact> inputsAndTools, |
| ImmutableList<Artifact> outputs, |
| CommandLine actualCommandLine, |
| ImmutableMap<String, String> env, |
| ImmutableSet<String> clientEnvironmentVariables, |
| ImmutableMap<String, String> executionInfo, |
| String progressMessage, |
| ImmutableMap<PathFragment, Artifact> inputAndToolManifests, |
| String mnemonic) { |
| return new LTOBackendAction( |
| inputsAndTools.toCollection(), |
| bitcodeFiles, |
| imports, |
| outputs, |
| owner, |
| actualCommandLine, |
| env, |
| clientEnvironmentVariables, |
| executionInfo, |
| progressMessage, |
| inputAndToolManifests, |
| mnemonic); |
| } |
| } |
| } |