com.google.devtools.build.lib.rules.cpp.CcCompilationHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.build.lib.rules.cpp.CcCompilationHelper.java

Source

// 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.rules.cpp;

import static java.util.stream.Collectors.toCollection;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
import com.google.devtools.build.lib.analysis.AnalysisEnvironment;
import com.google.devtools.build.lib.analysis.AnalysisUtils;
import com.google.devtools.build.lib.analysis.LanguageDependentFragment;
import com.google.devtools.build.lib.analysis.OutputGroupInfo;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.TransitiveInfoProviderMap;
import com.google.devtools.build.lib.analysis.TransitiveInfoProviderMapBuilder;
import com.google.devtools.build.lib.analysis.actions.SymlinkAction;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.PerLabelOptions;
import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.analysis.test.InstrumentedFilesCollector;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
import com.google.devtools.build.lib.rules.cpp.CcCommon.CoptsFilter;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
import com.google.devtools.build.lib.rules.cpp.CcToolchainVariables.VariablesExtension;
import com.google.devtools.build.lib.rules.cpp.CppConfiguration.HeadersCheckingMode;
import com.google.devtools.build.lib.skylarkbuildapi.cpp.CompilationInfoApi;
import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.util.FileTypeSet;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;
import javax.annotation.Nullable;

/**
 * A class to create C/C++ compile actions in a way that is consistent with cc_library. Rules that
 * generate source files and emulate cc_library on top of that should use this class instead of the
 * lower-level APIs in CppHelper and CppCompileActionBuilder.
 *
 * <p>Rules that want to use this class are required to have implicit dependencies on the toolchain,
 * the STL, the lipo context, and so on. Optionally, they can also have copts, and malloc
 * attributes, but note that these require explicit calls to the corresponding setter methods.
 */
public final class CcCompilationHelper {
    /** Similar to {@code OutputGroupInfo.HIDDEN_TOP_LEVEL}, but specific to header token files. */
    public static final String HIDDEN_HEADER_TOKENS = OutputGroupInfo.HIDDEN_OUTPUT_GROUP_PREFIX
            + "hidden_header_tokens" + OutputGroupInfo.INTERNAL_SUFFIX;

    /**
     * A group of source file types and action names for builds controlled by CcCompilationHelper.
     * Determines what file types CcCompilationHelper considers sources and what action configs are
     * configured in the CROSSTOOL.
     */
    public enum SourceCategory {
        CC(FileTypeSet.of(CppFileTypes.CPP_SOURCE, CppFileTypes.CPP_HEADER, CppFileTypes.C_SOURCE,
                CppFileTypes.ASSEMBLER, CppFileTypes.ASSEMBLER_WITH_C_PREPROCESSOR,
                CppFileTypes.CLIF_INPUT_PROTO)), CC_AND_OBJC(
                        FileTypeSet.of(CppFileTypes.CPP_SOURCE, CppFileTypes.CPP_HEADER, CppFileTypes.OBJC_SOURCE,
                                CppFileTypes.OBJCPP_SOURCE, CppFileTypes.C_SOURCE, CppFileTypes.ASSEMBLER,
                                CppFileTypes.ASSEMBLER_WITH_C_PREPROCESSOR));

        private final FileTypeSet sourceTypeSet;

        private SourceCategory(FileTypeSet sourceTypeSet) {
            this.sourceTypeSet = sourceTypeSet;
        }

        /** Returns the set of file types that are valid for this category. */
        public FileTypeSet getSourceTypes() {
            return sourceTypeSet;
        }
    }

    /** Function for extracting module maps from CppCompilationDependencies. */
    private static final Function<TransitiveInfoCollection, CppModuleMap> CPP_DEPS_TO_MODULES = dep -> {
        CcCompilationInfo ccCompilationInfo = dep.get(CcCompilationInfo.PROVIDER);
        CcCompilationContext ccCompilationContext = null;
        if (ccCompilationInfo != null) {
            ccCompilationContext = ccCompilationInfo.getCcCompilationContext();
        }
        return ccCompilationContext == null ? null : ccCompilationContext.getCppModuleMap();
    };

    /**
     * Contains the providers as well as the {@code CcCompilationOutputs} and the {@code
     * CcCompilationContext}.
     */
    // TODO(plf): Rename so that it's not confused with CcCompilationContext and also consider
    // merging
    // this class with {@code CcCompilationOutputs}.
    public static final class CompilationInfo implements CompilationInfoApi {
        private final TransitiveInfoProviderMap providers;
        private final Map<String, NestedSet<Artifact>> outputGroups;
        private final CcCompilationOutputs compilationOutputs;

        private CompilationInfo(TransitiveInfoProviderMap providers, Map<String, NestedSet<Artifact>> outputGroups,
                CcCompilationOutputs compilationOutputs) {
            this.providers = providers;
            this.outputGroups = outputGroups;
            this.compilationOutputs = compilationOutputs;
        }

        public TransitiveInfoProviderMap getProviders() {
            return providers;
        }

        public Map<String, NestedSet<Artifact>> getOutputGroups() {
            return outputGroups;
        }

        @Override
        public Map<String, SkylarkNestedSet> getSkylarkOutputGroups() {
            Map<String, SkylarkNestedSet> skylarkOutputGroups = new TreeMap<>();
            for (Map.Entry<String, NestedSet<Artifact>> entry : outputGroups.entrySet()) {
                skylarkOutputGroups.put(entry.getKey(), SkylarkNestedSet.of(Artifact.class, entry.getValue()));
            }
            return skylarkOutputGroups;
        }

        @Override
        public CcCompilationOutputs getCcCompilationOutputs() {
            return compilationOutputs;
        }

        @Override
        public CcCompilationInfo getCcCompilationInfo() {
            return (CcCompilationInfo) providers.getProvider(CcCompilationInfo.PROVIDER.getKey());
        }

        public CcCompilationContext getCcCompilationContext() {
            return ((CcCompilationInfo) providers.getProvider(CcCompilationInfo.PROVIDER.getKey()))
                    .getCcCompilationContext();
        }
    }

    private final RuleContext ruleContext;
    private final CppSemantics semantics;
    private final BuildConfiguration configuration;
    private final CppConfiguration cppConfiguration;

    private final List<Artifact> publicHeaders = new ArrayList<>();
    private final List<Artifact> nonModuleMapHeaders = new ArrayList<>();
    private final List<Artifact> publicTextualHeaders = new ArrayList<>();
    private final List<Artifact> privateHeaders = new ArrayList<>();
    private final List<Artifact> additionalInputs = new ArrayList<>();
    private final List<Artifact> additionalCompilationInputs = new ArrayList<>();
    private final List<Artifact> additionalIncludeScanningRoots = new ArrayList<>();
    private final List<PathFragment> additionalExportedHeaders = new ArrayList<>();
    private final List<CppModuleMap> additionalCppModuleMaps = new ArrayList<>();
    private final Set<CppSource> compilationUnitSources = new LinkedHashSet<>();
    private final List<Artifact> objectFiles = new ArrayList<>();
    private final List<Artifact> picObjectFiles = new ArrayList<>();
    private ImmutableList<String> copts = ImmutableList.of();
    private CoptsFilter coptsFilter = CoptsFilter.alwaysPasses();
    private final Set<String> defines = new LinkedHashSet<>();
    private final List<TransitiveInfoCollection> deps = new ArrayList<>();
    private final List<CcCompilationContext> ccCompilationContexts = new ArrayList<>();
    private final List<PathFragment> looseIncludeDirs = new ArrayList<>();
    private final List<PathFragment> systemIncludeDirs = new ArrayList<>();
    private final List<PathFragment> includeDirs = new ArrayList<>();

    private HeadersCheckingMode headersCheckingMode = HeadersCheckingMode.LOOSE;
    private boolean fake;

    private boolean checkDepsGenerateCpp = true;
    private boolean emitCompileProviders;
    private final SourceCategory sourceCategory;
    private final List<VariablesExtension> variablesExtensions = new ArrayList<>();
    @Nullable
    private CppModuleMap cppModuleMap;
    private boolean propagateModuleMapToCompileAction = true;

    private final FeatureConfiguration featureConfiguration;
    private final CcToolchainProvider ccToolchain;
    private final FdoSupportProvider fdoSupport;
    private boolean useDeps = true;
    private boolean generateModuleMap = true;
    private String purpose = null;
    private boolean generateNoPicAction;
    private boolean generatePicAction;

    // TODO(plf): Pull out of class.
    private CcCompilationContext ccCompilationContext;

    /**
     * Creates a CcCompilationHelper.
     *
     * @param ruleContext the RuleContext for the rule being built
     * @param semantics CppSemantics for the build
     * @param featureConfiguration activated features and action configs for the build
     * @param sourceCatagory the candidate source types for the build
     * @param ccToolchain the C++ toolchain provider for the build
     * @param fdoSupport the C++ FDO optimization support provider for the build
     */
    public CcCompilationHelper(RuleContext ruleContext, CppSemantics semantics,
            FeatureConfiguration featureConfiguration, SourceCategory sourceCatagory,
            CcToolchainProvider ccToolchain, FdoSupportProvider fdoSupport) {
        this(ruleContext, semantics, featureConfiguration, sourceCatagory, ccToolchain, fdoSupport,
                ruleContext.getConfiguration());
    }

    /**
     * Creates a CcCompilationHelper that outputs artifacts in a given configuration.
     *
     * @param ruleContext the RuleContext for the rule being built
     * @param semantics CppSemantics for the build
     * @param featureConfiguration activated features and action configs for the build
     * @param sourceCatagory the candidate source types for the build
     * @param ccToolchain the C++ toolchain provider for the build
     * @param fdoSupport the C++ FDO optimization support provider for the build
     * @param configuration the configuration that gives the directory of output artifacts
     */
    public CcCompilationHelper(RuleContext ruleContext, CppSemantics semantics,
            FeatureConfiguration featureConfiguration, SourceCategory sourceCatagory,
            CcToolchainProvider ccToolchain, FdoSupportProvider fdoSupport, BuildConfiguration configuration) {
        this.ruleContext = Preconditions.checkNotNull(ruleContext);
        this.semantics = Preconditions.checkNotNull(semantics);
        this.featureConfiguration = Preconditions.checkNotNull(featureConfiguration);
        this.sourceCategory = Preconditions.checkNotNull(sourceCatagory);
        this.ccToolchain = Preconditions.checkNotNull(ccToolchain);
        this.fdoSupport = Preconditions.checkNotNull(fdoSupport);
        this.configuration = Preconditions.checkNotNull(configuration);
        this.cppConfiguration = Preconditions.checkNotNull(ruleContext.getFragment(CppConfiguration.class));
        setGenerateNoPicAction(!CppHelper.usePicForDynamicLibraries(ruleContext, ccToolchain)
                || !CppHelper.usePicForBinaries(ruleContext, ccToolchain));
        setGeneratePicAction(CppHelper.usePicForDynamicLibraries(ruleContext, ccToolchain)
                || CppHelper.usePicForBinaries(ruleContext, ccToolchain));
    }

    /**
     * Creates a CcCompilationHelper for cpp source files.
     *
     * @param ruleContext the RuleContext for the rule being built
     * @param semantics CppSemantics for the build
     * @param featureConfiguration activated features and action configs for the build
     * @param ccToolchain the C++ toolchain provider for the build
     * @param fdoSupport the C++ FDO optimization support provider for the build
     */
    public CcCompilationHelper(RuleContext ruleContext, CppSemantics semantics,
            FeatureConfiguration featureConfiguration, CcToolchainProvider ccToolchain,
            FdoSupportProvider fdoSupport) {
        this(ruleContext, semantics, featureConfiguration, SourceCategory.CC, ccToolchain, fdoSupport);
    }

    /** Sets fields that overlap for cc_library and cc_binary rules. */
    public CcCompilationHelper fromCommon(CcCommon common, ImmutableList<String> additionalCopts) {
        Preconditions.checkNotNull(additionalCopts);

        setCopts(Iterables.concat(common.getCopts(), additionalCopts));
        addDefines(common.getDefines());
        addDeps(ruleContext.getPrerequisites("deps", Mode.TARGET));
        addLooseIncludeDirs(common.getLooseIncludeDirs());
        addSystemIncludeDirs(common.getSystemIncludeDirs());
        setCoptsFilter(common.getCoptsFilter());
        setHeadersCheckingMode(semantics.determineHeadersCheckingMode(ruleContext));
        return this;
    }

    /**
     * Adds {@code headers} as public header files. These files will be made visible to dependent
     * rules. They may be parsed/preprocessed or compiled into a header module depending on the
     * configuration.
     */
    public CcCompilationHelper addPublicHeaders(Collection<Artifact> headers) {
        for (Artifact header : headers) {
            addHeader(header, ruleContext.getLabel());
        }
        return this;
    }

    /**
     * Adds {@code headers} as public header files. These files will be made visible to dependent
     * rules. They may be parsed/preprocessed or compiled into a header module depending on the
     * configuration.
     */
    public CcCompilationHelper addPublicHeaders(Artifact... headers) {
        addPublicHeaders(Arrays.asList(headers));
        return this;
    }

    /**
     * Adds {@code headers} as public header files. These files will be made visible to dependent
     * rules. They may be parsed/preprocessed or compiled into a header module depending on the
     * configuration.
     */
    public CcCompilationHelper addPublicHeaders(Iterable<Pair<Artifact, Label>> headers) {
        for (Pair<Artifact, Label> header : headers) {
            addHeader(header.first, header.second);
        }
        return this;
    }

    /**
     * Add the corresponding files as public header files, i.e., these files will not be compiled, but
     * are made visible as includes to dependent rules in module maps.
     */
    public CcCompilationHelper addAdditionalExportedHeaders(Iterable<PathFragment> additionalExportedHeaders) {
        Iterables.addAll(this.additionalExportedHeaders, additionalExportedHeaders);
        return this;
    }

    /**
     * Add the corresponding files as public textual header files. These files will not be compiled
     * into a target's header module, but will be made visible as textual includes to dependent rules.
     */
    public CcCompilationHelper addPublicTextualHeaders(Iterable<Artifact> textualHeaders) {
        Iterables.addAll(this.publicTextualHeaders, textualHeaders);
        for (Artifact header : textualHeaders) {
            this.additionalExportedHeaders.add(header.getExecPath());
        }
        return this;
    }

    public CcCompilationHelper addPrivateHeaders(Collection<Artifact> privateHeaders) {
        for (Artifact privateHeader : privateHeaders) {
            addPrivateHeader(privateHeader, ruleContext.getLabel());
        }
        return this;
    }

    public CcCompilationHelper addPrivateHeaders(Iterable<Pair<Artifact, Label>> privateHeaders) {
        for (Pair<Artifact, Label> headerLabelPair : privateHeaders) {
            addPrivateHeader(headerLabelPair.first, headerLabelPair.second);
        }
        return this;
    }

    private CcCompilationHelper addPrivateHeader(Artifact privateHeader, Label label) {
        boolean isHeader = CppFileTypes.CPP_HEADER.matches(privateHeader.getExecPath());
        boolean isTextualInclude = CppFileTypes.CPP_TEXTUAL_INCLUDE.matches(privateHeader.getExecPath());
        Preconditions.checkState(isHeader || isTextualInclude);

        if (shouldProcessHeaders() && !isTextualInclude) {
            compilationUnitSources.add(CppSource.create(privateHeader, label, CppSource.Type.HEADER));
        }
        this.privateHeaders.add(privateHeader);
        return this;
    }

    /**
     * Add the corresponding files as source files. These may also be header files, in which case they
     * will not be compiled, but also not made visible as includes to dependent rules. The given build
     * variables will be added to those used for compiling this source.
     */
    public CcCompilationHelper addSources(Collection<Artifact> sources) {
        for (Artifact source : sources) {
            addSource(source, ruleContext.getLabel());
        }
        return this;
    }

    /**
     * Add the corresponding files as source files. These may also be header files, in which case they
     * will not be compiled, but also not made visible as includes to dependent rules.
     */
    public CcCompilationHelper addSources(Iterable<Pair<Artifact, Label>> sources) {
        for (Pair<Artifact, Label> source : sources) {
            addSource(source.first, source.second);
        }
        return this;
    }

    /**
     * Add the corresponding files as source files. These may also be header files, in which case they
     * will not be compiled, but also not made visible as includes to dependent rules.
     */
    public CcCompilationHelper addSources(Artifact... sources) {
        return addSources(Arrays.asList(sources));
    }

    /** Add the corresponding files as non-header, non-source input files. */
    public CcCompilationHelper addAdditionalInputs(Collection<Artifact> inputs) {
        Iterables.addAll(additionalInputs, inputs);
        return this;
    }

    /**
     * Adds a header to {@code publicHeaders} and in case header processing is switched on for the
     * file type also to compilationUnitSources.
     */
    private void addHeader(Artifact header, Label label) {
        // We assume TreeArtifacts passed in are directories containing proper headers.
        boolean isHeader = CppFileTypes.CPP_HEADER.matches(header.getExecPath()) || header.isTreeArtifact();
        boolean isTextualInclude = CppFileTypes.CPP_TEXTUAL_INCLUDE.matches(header.getExecPath());
        publicHeaders.add(header);
        if (isTextualInclude || !isHeader || !shouldProcessHeaders()) {
            return;
        }
        compilationUnitSources.add(CppSource.create(header, label, CppSource.Type.HEADER));
    }

    /** Adds a header to {@code publicHeaders}, but not to this target's module map. */
    public CcCompilationHelper addNonModuleMapHeader(Artifact header) {
        Preconditions.checkNotNull(header);
        nonModuleMapHeaders.add(header);
        return this;
    }

    /**
     * Adds a source to {@code compilationUnitSources} if it is a compiled file type (including
     * parsed/preprocessed header) and to {@code privateHeaders} if it is a header.
     */
    private void addSource(Artifact source, Label label) {
        Preconditions.checkNotNull(featureConfiguration);
        Preconditions.checkState(!CppFileTypes.CPP_HEADER.matches(source.getExecPath()));
        // We assume TreeArtifacts passed in are directories containing proper sources for compilation.
        if (!sourceCategory.getSourceTypes().matches(source.getExecPathString()) && !source.isTreeArtifact()) {
            // TODO(plf): If it's a non-source file we ignore it. This is only the case for precompiled
            // files which should be forbidden in srcs of cc_library|binary and instead be migrated to
            // cc_import rules.
            return;
        }

        boolean isClifInputProto = CppFileTypes.CLIF_INPUT_PROTO.matches(source.getExecPathString());
        CppSource.Type type;
        if (isClifInputProto) {
            type = CppSource.Type.CLIF_INPUT_PROTO;
        } else {
            type = CppSource.Type.SOURCE;
        }
        compilationUnitSources.add(CppSource.create(source, label, type));
    }

    private boolean shouldProcessHeaders() {
        // If parse_headers_verifies_modules is switched on, we verify that headers are
        // self-contained by building the module instead.
        return !cppConfiguration.getParseHeadersVerifiesModules()
                && (featureConfiguration.isEnabled(CppRuleClasses.PREPROCESS_HEADERS)
                        || featureConfiguration.isEnabled(CppRuleClasses.PARSE_HEADERS));
    }

    /**
     * Returns the compilation unit sources. That includes all compiled source files as well as
     * headers that will be parsed or preprocessed. Each source file contains the label it arises from
     * in the build graph as well as {@code FeatureConfiguration} that should be used during its
     * compilation.
     */
    public ImmutableSet<CppSource> getCompilationUnitSources() {
        return ImmutableSet.copyOf(this.compilationUnitSources);
    }

    /**
     * Add the corresponding files as linker inputs for no-PIC links. If the corresponding files are
     * compiled with PIC, the final link may or may not fail. Note that the final link may not happen
     * here, if {@code --start_end_lib} is enabled, but instead at any binary that transitively
     * depends on the current rule.
     */
    public CcCompilationHelper addObjectFiles(Iterable<Artifact> objectFiles) {
        for (Artifact objectFile : objectFiles) {
            Preconditions.checkArgument(Link.OBJECT_FILETYPES.matches(objectFile.getFilename()));
        }
        Iterables.addAll(this.objectFiles, objectFiles);
        return this;
    }

    /**
     * Add the corresponding files as linker inputs for PIC links. If the corresponding files are not
     * compiled with PIC, the final link may or may not fail. Note that the final link may not happen
     * here, if {@code --start_end_lib} is enabled, but instead at any binary that transitively
     * depends on the current rule.
     */
    public CcCompilationHelper addPicObjectFiles(Iterable<Artifact> picObjectFiles) {
        for (Artifact objectFile : objectFiles) {
            Preconditions.checkArgument(Link.OBJECT_FILETYPES.matches(objectFile.getFilename()));
        }
        Iterables.addAll(this.picObjectFiles, picObjectFiles);
        return this;
    }

    public CcCompilationHelper setCopts(Iterable<String> copts) {
        this.copts = ImmutableList.copyOf(copts);
        return this;
    }

    /** Sets a pattern that is used to filter copts; set to {@code null} for no filtering. */
    private void setCoptsFilter(CoptsFilter coptsFilter) {
        this.coptsFilter = Preconditions.checkNotNull(coptsFilter);
    }

    /** Adds the given defines to the compiler command line. */
    public CcCompilationHelper addDefines(Iterable<String> defines) {
        Iterables.addAll(this.defines, defines);
        return this;
    }

    /**
     * Adds the given targets as dependencies - this can include explicit dependencies on other rules
     * (like from a "deps" attribute) and also implicit dependencies on runtime libraries.
     */
    public CcCompilationHelper addDeps(Iterable<? extends TransitiveInfoCollection> deps) {
        for (TransitiveInfoCollection dep : deps) {
            this.deps.add(dep);
        }
        return this;
    }

    /** For adding CC compilation contexts that affect compilation, e.g: from dependencies. */
    public CcCompilationHelper addCcCompilationContexts(List<CcCompilationContext> ccCompilationContexts) {
        this.ccCompilationContexts.addAll(Preconditions.checkNotNull(ccCompilationContexts));
        return this;
    }

    /**
     * Adds the given precompiled files to this helper. Shared and static libraries are added as
     * compilation prerequisites, and object files are added as pic or no-PIC object files
     * respectively.
     */
    public CcCompilationHelper addPrecompiledFiles(PrecompiledFiles precompiledFiles) {
        addObjectFiles(precompiledFiles.getObjectFiles(false));
        addPicObjectFiles(precompiledFiles.getObjectFiles(true));
        return this;
    }

    /**
     * Adds the given directories to the loose include directories that are only allowed to be
     * referenced when headers checking is {@link HeadersCheckingMode#LOOSE} or {@link
     * HeadersCheckingMode#WARN}.
     */
    private void addLooseIncludeDirs(Iterable<PathFragment> looseIncludeDirs) {
        Iterables.addAll(this.looseIncludeDirs, looseIncludeDirs);
    }

    /**
     * Adds the given directories to the system include directories (they are passed with {@code
     * "-isystem"} to the compiler); these are also passed to dependent rules.
     */
    public CcCompilationHelper addSystemIncludeDirs(Iterable<PathFragment> systemIncludeDirs) {
        Iterables.addAll(this.systemIncludeDirs, systemIncludeDirs);
        return this;
    }

    /**
     * Adds the given directories to the include directories (they are passed with {@code "-I"} to the
     * compiler); these are also passed to dependent rules.
     */
    public CcCompilationHelper addIncludeDirs(Iterable<PathFragment> includeDirs) {
        Iterables.addAll(this.includeDirs, includeDirs);
        return this;
    }

    /** Adds a variableExtension to template the crosstool. */
    public CcCompilationHelper addVariableExtension(VariablesExtension variableExtension) {
        Preconditions.checkNotNull(variableExtension);
        this.variablesExtensions.add(variableExtension);
        return this;
    }

    /** Sets a module map artifact for this build. */
    public CcCompilationHelper setCppModuleMap(CppModuleMap cppModuleMap) {
        Preconditions.checkNotNull(cppModuleMap);
        this.cppModuleMap = cppModuleMap;
        return this;
    }

    /** Signals that this target's module map should not be an input to c++ compile actions. */
    public CcCompilationHelper setPropagateModuleMapToCompileAction(boolean propagatesModuleMap) {
        this.propagateModuleMapToCompileAction = propagatesModuleMap;
        return this;
    }

    /** Sets the given headers checking mode. The default is {@link HeadersCheckingMode#LOOSE}. */
    public CcCompilationHelper setHeadersCheckingMode(HeadersCheckingMode headersCheckingMode) {
        this.headersCheckingMode = Preconditions.checkNotNull(headersCheckingMode);
        return this;
    }

    /**
     * Marks the resulting code as fake, i.e., the code will not actually be compiled or linked, but
     * instead, the compile command is written to a file and added to the runfiles. This is currently
     * used for non-compilation tests. Unfortunately, the design is problematic, so please don't add
     * any further uses.
     */
    public CcCompilationHelper setFake(boolean fake) {
        this.fake = fake;
        return this;
    }

    /**
     * Disables checking that the deps actually are C++ rules. By default, the {@link #compile} method
     * uses {@link LanguageDependentFragment.Checker#depSupportsLanguage} to check that all deps
     * provide C++ providers.
     */
    public CcCompilationHelper setCheckDepsGenerateCpp(boolean checkDepsGenerateCpp) {
        this.checkDepsGenerateCpp = checkDepsGenerateCpp;
        return this;
    }

    /**
     * Enables the output of the {@code files_to_compile} and {@code compilation_prerequisites} output
     * groups.
     */
    // TODO(bazel-team): We probably need to adjust this for the multi-language rules.
    public CcCompilationHelper enableCompileProviders() {
        this.emitCompileProviders = true;
        return this;
    }

    /**
     * Causes actions generated from this CcCompilationHelper not to use build semantics (includes,
     * headers, srcs) from dependencies.
     */
    public CcCompilationHelper doNotUseDeps() {
        this.useDeps = false;
        return this;
    }

    /** Whether to generate no-PIC actions. */
    public CcCompilationHelper setGenerateNoPicAction(boolean generateNoPicAction) {
        this.generateNoPicAction = generateNoPicAction;
        return this;
    }

    /** Whether to generate PIC actions. */
    public CcCompilationHelper setGeneratePicAction(boolean generatePicAction) {
        this.generatePicAction = generatePicAction;
        return this;
    }

    /** Adds mandatory inputs for the compilation action. */
    public CcCompilationHelper addAdditionalCompilationInputs(Collection<Artifact> compilationMandatoryInputs) {
        this.additionalCompilationInputs.addAll(compilationMandatoryInputs);
        return this;
    }

    /** Adds additional includes to be scanned. */
    // TODO(plf): This is only needed for CLIF. Investigate whether this is strictly necessary or
    // there is a way to avoid include scanning for CLIF rules.
    public CcCompilationHelper addAditionalIncludeScanningRoots(
            Collection<Artifact> additionalIncludeScanningRoots) {
        this.additionalIncludeScanningRoots.addAll(additionalIncludeScanningRoots);
        return this;
    }

    /**
     * Create the C++ compile actions, and the corresponding compilation related providers.
     *
     * @throws RuleErrorException
     */
    public CompilationInfo compile() throws RuleErrorException {
        if (checkDepsGenerateCpp) {
            for (LanguageDependentFragment dep : AnalysisUtils.getProviders(deps,
                    LanguageDependentFragment.class)) {
                LanguageDependentFragment.Checker.depSupportsLanguage(ruleContext, dep, CppRuleClasses.LANGUAGE,
                        "deps");
            }
        }

        if (!generatePicAction && !generateNoPicAction) {
            ruleContext.ruleError("Either PIC or no PIC actions have to be created.");
        }

        ccCompilationContext = initializeCcCompilationContext();

        boolean compileHeaderModules = featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES);
        Preconditions.checkState(!compileHeaderModules || ccCompilationContext.getCppModuleMap() != null,
                "All cc rules must support module maps.");

        // Create compile actions (both PIC and no-PIC).
        CcCompilationOutputs ccOutputs = createCcCompileActions();
        if (!objectFiles.isEmpty() || !picObjectFiles.isEmpty()) {
            // Merge the pre-compiled object files into the compiler outputs.
            ccOutputs = new CcCompilationOutputs.Builder().merge(ccOutputs).addObjectFiles(objectFiles)
                    .addPicObjectFiles(picObjectFiles).build();
        }

        DwoArtifactsCollector dwoArtifacts = DwoArtifactsCollector.transitiveCollector(ccOutputs, deps,
                /*generateDwo=*/ false, /*ltoBackendArtifactsUsePic=*/ false,
                /*ltoBackendArtifacts=*/ ImmutableList.of());

        // Be very careful when adding new providers here - it can potentially affect a lot of rules.
        // We should consider merging most of these providers into a single provider.
        TransitiveInfoProviderMapBuilder providers = new TransitiveInfoProviderMapBuilder().add(
                new CppDebugFileProvider(dwoArtifacts.getDwoArtifacts(), dwoArtifacts.getPicDwoArtifacts()),
                collectTransitiveLipoInfo(ccOutputs));
        CcCompilationInfo.Builder ccCompilationInfoBuilder = CcCompilationInfo.Builder.create();
        ccCompilationInfoBuilder.setCcCompilationContext(ccCompilationContext);
        providers.put(ccCompilationInfoBuilder.build());

        Map<String, NestedSet<Artifact>> outputGroups = new TreeMap<>();
        outputGroups.put(OutputGroupInfo.TEMP_FILES, getTemps(ccOutputs));
        if (emitCompileProviders) {
            boolean isLipoCollector = cppConfiguration.isLipoContextCollector();
            boolean processHeadersInDependencies = cppConfiguration.processHeadersInDependencies();
            boolean usePic = CppHelper.usePicForDynamicLibraries(ruleContext, ccToolchain);
            outputGroups.put(OutputGroupInfo.FILES_TO_COMPILE,
                    ccOutputs.getFilesToCompile(isLipoCollector, processHeadersInDependencies, usePic));
            outputGroups.put(OutputGroupInfo.COMPILATION_PREREQUISITES,
                    CcCommon.collectCompilationPrerequisites(ruleContext, ccCompilationContext));
        }

        return new CompilationInfo(providers.build(), outputGroups, ccOutputs);
    }

    @Immutable
    private static class PublicHeaders {
        private final ImmutableList<Artifact> headers;
        private final ImmutableList<Artifact> moduleMapHeaders;
        private final @Nullable PathFragment virtualIncludePath;

        private PublicHeaders(ImmutableList<Artifact> headers, ImmutableList<Artifact> moduleMapHeaders,
                PathFragment virtualIncludePath) {
            this.headers = headers;
            this.moduleMapHeaders = moduleMapHeaders;
            this.virtualIncludePath = virtualIncludePath;
        }

        private ImmutableList<Artifact> getHeaders() {
            return headers;
        }

        private ImmutableList<Artifact> getModuleMapHeaders() {
            return moduleMapHeaders;
        }

        @Nullable
        private PathFragment getVirtualIncludePath() {
            return virtualIncludePath;
        }
    }

    private PublicHeaders computePublicHeaders() {
        if (!ruleContext.attributes().has("strip_include_prefix", Type.STRING)
                || !ruleContext.attributes().has("include_prefix", Type.STRING)) {
            return new PublicHeaders(ImmutableList.copyOf(Iterables.concat(publicHeaders, nonModuleMapHeaders)),
                    ImmutableList.copyOf(publicHeaders), null);
        }

        PathFragment prefix = null;
        if (ruleContext.attributes().isAttributeValueExplicitlySpecified("include_prefix")) {
            String prefixAttr = ruleContext.attributes().get("include_prefix", Type.STRING);
            prefix = PathFragment.create(prefixAttr);
            if (PathFragment.containsUplevelReferences(prefixAttr)) {
                ruleContext.attributeError("include_prefix", "should not contain uplevel references");
            }
            if (prefix.isAbsolute()) {
                ruleContext.attributeError("include_prefix", "should be a relative path");
            }
        }

        PathFragment stripPrefix;
        if (ruleContext.attributes().isAttributeValueExplicitlySpecified("strip_include_prefix")) {
            String stripPrefixAttr = ruleContext.attributes().get("strip_include_prefix", Type.STRING);
            if (PathFragment.containsUplevelReferences(stripPrefixAttr)) {
                ruleContext.attributeError("strip_include_prefix", "should not contain uplevel references");
            }
            stripPrefix = PathFragment.create(stripPrefixAttr);
            if (stripPrefix.isAbsolute()) {
                stripPrefix = ruleContext.getLabel().getPackageIdentifier().getRepository().getSourceRoot()
                        .getRelative(stripPrefix.toRelative());
            } else {
                stripPrefix = ruleContext.getPackageDirectory().getRelative(stripPrefix);
            }
        } else if (prefix != null) {
            stripPrefix = ruleContext.getPackageDirectory();
        } else {
            stripPrefix = null;
        }

        if (stripPrefix == null && prefix == null) {
            // Simple case, no magic needed
            return new PublicHeaders(ImmutableList.copyOf(Iterables.concat(publicHeaders, nonModuleMapHeaders)),
                    ImmutableList.copyOf(publicHeaders), null);
        }

        if (ruleContext.hasErrors()) {
            return new PublicHeaders(ImmutableList.<Artifact>of(), ImmutableList.<Artifact>of(), null);
        }

        ImmutableList.Builder<Artifact> moduleHeadersBuilder = ImmutableList.builder();

        for (Artifact originalHeader : publicHeaders) {
            if (!originalHeader.getRootRelativePath().startsWith(stripPrefix)) {
                ruleContext.ruleError(String.format("header '%s' is not under the specified strip prefix '%s'",
                        originalHeader.getExecPathString(), stripPrefix.getPathString()));
                continue;
            }

            PathFragment includePath = originalHeader.getRootRelativePath().relativeTo(stripPrefix);
            if (prefix != null) {
                includePath = prefix.getRelative(includePath);
            }

            if (!originalHeader.getExecPath().equals(includePath)) {
                Artifact virtualHeader = ruleContext.getUniqueDirectoryArtifact("_virtual_includes", includePath,
                        ruleContext.getBinOrGenfilesDirectory());
                ruleContext.registerAction(new SymlinkAction(ruleContext.getActionOwner(), originalHeader,
                        virtualHeader, "Symlinking virtual headers for " + ruleContext.getLabel()));
                moduleHeadersBuilder.add(virtualHeader);
            } else {
                moduleHeadersBuilder.add(originalHeader);
            }
        }

        ImmutableList<Artifact> moduleMapHeaders = moduleHeadersBuilder.build();
        ImmutableList<Artifact> virtualHeaders = ImmutableList.<Artifact>builder().addAll(moduleMapHeaders)
                .addAll(nonModuleMapHeaders).build();

        return new PublicHeaders(virtualHeaders, moduleMapHeaders, ruleContext.getBinOrGenfilesDirectory()
                .getExecPath().getRelative(ruleContext.getUniqueDirectory("_virtual_includes")));
    }

    /**
     * Create {@code CcCompilationContext} for cc compile action from generated inputs.
     *
     * <p>TODO(plf): Try to pull out CcCompilationContext building out of this class.
     */
    public CcCompilationContext initializeCcCompilationContext() {
        CcCompilationContext.Builder ccCompilationContextBuilder = new CcCompilationContext.Builder(ruleContext);

        // Setup the include path; local include directories come before those inherited from deps or
        // from the toolchain; in case of aliasing (same include file found on different entries),
        // prefer the local include rather than the inherited one.

        // Add in the roots for well-formed include names for source files and
        // generated files. It is important that the execRoot (EMPTY_FRAGMENT) comes
        // before the genfilesFragment to preferably pick up source files. Otherwise
        // we might pick up stale generated files.
        PathFragment repositoryPath = ruleContext.getLabel().getPackageIdentifier().getRepository()
                .getPathUnderExecRoot();
        ccCompilationContextBuilder.addQuoteIncludeDir(repositoryPath);
        ccCompilationContextBuilder.addQuoteIncludeDir(
                ruleContext.getConfiguration().getGenfilesFragment().getRelative(repositoryPath));

        for (PathFragment systemIncludeDir : systemIncludeDirs) {
            ccCompilationContextBuilder.addSystemIncludeDir(systemIncludeDir);
        }
        for (PathFragment includeDir : includeDirs) {
            ccCompilationContextBuilder.addIncludeDir(includeDir);
        }

        PublicHeaders publicHeaders = computePublicHeaders();
        if (publicHeaders.getVirtualIncludePath() != null) {
            ccCompilationContextBuilder.addIncludeDir(publicHeaders.getVirtualIncludePath());
        }

        if (useDeps) {
            ccCompilationContextBuilder
                    .mergeDependentCcCompilationContexts(CcCompilationInfo.getCcCompilationContexts(deps));
            ccCompilationContextBuilder.mergeDependentCcCompilationContexts(ccCompilationContexts);
        }
        CppHelper.mergeToolchainDependentCcCompilationContext(ruleContext, ccToolchain,
                ccCompilationContextBuilder);

        // But defines come after those inherited from deps.
        ccCompilationContextBuilder.addDefines(defines);

        // There are no ordering constraints for declared include dirs/srcs, or the pregrepped headers.
        ccCompilationContextBuilder.addDeclaredIncludeSrcs(publicHeaders.getHeaders());
        ccCompilationContextBuilder.addDeclaredIncludeSrcs(publicTextualHeaders);
        ccCompilationContextBuilder.addDeclaredIncludeSrcs(privateHeaders);
        ccCompilationContextBuilder.addDeclaredIncludeSrcs(additionalInputs);
        ccCompilationContextBuilder.addNonCodeInputs(additionalInputs);
        ccCompilationContextBuilder.addModularHdrs(publicHeaders.getHeaders());
        ccCompilationContextBuilder.addModularHdrs(privateHeaders);
        ccCompilationContextBuilder.addTextualHdrs(publicTextualHeaders);
        ccCompilationContextBuilder.addPregreppedHeaders(
                CppHelper.createExtractInclusions(ruleContext, semantics, publicHeaders.getHeaders()));
        ccCompilationContextBuilder.addPregreppedHeaders(
                CppHelper.createExtractInclusions(ruleContext, semantics, publicTextualHeaders));
        ccCompilationContextBuilder
                .addPregreppedHeaders(CppHelper.createExtractInclusions(ruleContext, semantics, privateHeaders));

        // Add this package's dir to declaredIncludeDirs, & this rule's headers to declaredIncludeSrcs
        // Note: no include dir for STRICT mode.
        if (headersCheckingMode == HeadersCheckingMode.WARN) {
            ccCompilationContextBuilder.addDeclaredIncludeWarnDir(ruleContext.getLabel().getPackageFragment());
            for (PathFragment looseIncludeDir : looseIncludeDirs) {
                ccCompilationContextBuilder.addDeclaredIncludeWarnDir(looseIncludeDir);
            }
        } else if (headersCheckingMode == HeadersCheckingMode.LOOSE) {
            ccCompilationContextBuilder.addDeclaredIncludeDir(ruleContext.getLabel().getPackageFragment());
            for (PathFragment looseIncludeDir : looseIncludeDirs) {
                ccCompilationContextBuilder.addDeclaredIncludeDir(looseIncludeDir);
            }
        }

        if (featureConfiguration.isEnabled(CppRuleClasses.MODULE_MAPS)) {
            if (cppModuleMap == null) {
                cppModuleMap = CppHelper.createDefaultCppModuleMap(ruleContext, /*suffix=*/ "");
            }

            ccCompilationContextBuilder.setPropagateCppModuleMapAsActionInput(propagateModuleMapToCompileAction);
            ccCompilationContextBuilder.setCppModuleMap(cppModuleMap);
            // There are different modes for module compilation:
            // 1. We create the module map and compile the module so that libraries depending on us can
            //    use the resulting module artifacts in their compilation (compiled is true).
            // 2. We create the module map so that libraries depending on us will include the headers
            //    textually (compiled is false).
            boolean compiled = featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES)
                    || featureConfiguration.isEnabled(CppRuleClasses.COMPILE_ALL_MODULES);
            Iterable<CppModuleMap> dependentModuleMaps = collectModuleMaps();

            if (generateModuleMap) {
                Optional<Artifact> umbrellaHeader = cppModuleMap.getUmbrellaHeader();
                if (umbrellaHeader.isPresent()) {
                    ruleContext.registerAction(createUmbrellaHeaderAction(umbrellaHeader.get(), publicHeaders));
                }

                ruleContext.registerAction(
                        createModuleMapAction(cppModuleMap, publicHeaders, dependentModuleMaps, compiled));
            }
            if (getGeneratesPicHeaderModule()) {
                ccCompilationContextBuilder.setPicHeaderModule(getPicHeaderModule(cppModuleMap.getArtifact()));
            }
            if (getGeneratesNoPicHeaderModule()) {
                ccCompilationContextBuilder.setHeaderModule(getHeaderModule(cppModuleMap.getArtifact()));
            }
            if (!compiled && featureConfiguration.isEnabled(CppRuleClasses.PARSE_HEADERS)
                    && featureConfiguration.isEnabled(CppRuleClasses.USE_HEADER_MODULES)
                    && cppConfiguration.getParseHeadersVerifiesModules()) {
                // Here, we are creating a compiled module to verify that headers are self-contained and
                // modules ready, but we don't use the corresponding module map or compiled file anywhere
                // else.
                CppModuleMap verificationMap = CppHelper.createDefaultCppModuleMap(ruleContext,
                        /*suffix=*/ ".verify");
                ruleContext.registerAction(createModuleMapAction(verificationMap, publicHeaders,
                        dependentModuleMaps, /*compiledModule=*/ true));
                ccCompilationContextBuilder.setVerificationModuleMap(verificationMap);
            }
        }
        ccCompilationContextBuilder.setPurpose(purpose);

        semantics.setupCcCompilationContext(ruleContext, ccCompilationContextBuilder);
        return ccCompilationContextBuilder.build();
    }

    /**
     * Collects all preprocessed header files (*.h.processed) from dependencies and the current rule.
     */
    public static NestedSet<Artifact> collectHeaderTokens(RuleContext ruleContext,
            CcCompilationOutputs ccCompilationOutputs) {
        NestedSetBuilder<Artifact> headerTokens = NestedSetBuilder.stableOrder();
        for (OutputGroupInfo dep : ruleContext.getPrerequisites("deps", Mode.TARGET,
                OutputGroupInfo.SKYLARK_CONSTRUCTOR)) {
            headerTokens.addTransitive(dep.getOutputGroup(CcCompilationHelper.HIDDEN_HEADER_TOKENS));
        }
        if (ruleContext.getFragment(CppConfiguration.class).processHeadersInDependencies()) {
            headerTokens.addAll(ccCompilationOutputs.getHeaderTokenFiles());
        }
        return headerTokens.build();
    }

    public void registerAdditionalModuleMap(CppModuleMap cppModuleMap) {
        this.additionalCppModuleMaps.add(Preconditions.checkNotNull(cppModuleMap));
    }

    /** Don't generate a module map for this target if a custom module map is provided. */
    public CcCompilationHelper doNotGenerateModuleMap() {
        generateModuleMap = false;
        return this;
    }

    /**
     * Sets the purpose for the {@code CcCompilationContext}.
     *
     * @see CcCompilationContext.Builder#setPurpose
     * @param purpose must be a string which is suitable for use as a filename. A single rule may have
     *     many middlemen with distinct purposes.
     */
    public CcCompilationHelper setPurpose(@Nullable String purpose) {
        this.purpose = purpose;
        return this;
    }

    private UmbrellaHeaderAction createUmbrellaHeaderAction(Artifact umbrellaHeader, PublicHeaders publicHeaders) {
        return new UmbrellaHeaderAction(ruleContext.getActionOwner(), umbrellaHeader,
                featureConfiguration.isEnabled(CppRuleClasses.ONLY_DOTH_HEADERS_IN_MODULE_MAPS)
                        ? Iterables.filter(publicHeaders.getModuleMapHeaders(), CppFileTypes.MODULE_MAP_HEADER)
                        : publicHeaders.getModuleMapHeaders(),
                additionalExportedHeaders);
    }

    private CppModuleMapAction createModuleMapAction(CppModuleMap moduleMap, PublicHeaders publicHeaders,
            Iterable<CppModuleMap> dependentModuleMaps, boolean compiledModule) {
        return new CppModuleMapAction(ruleContext.getActionOwner(), moduleMap,
                featureConfiguration.isEnabled(CppRuleClasses.EXCLUDE_PRIVATE_HEADERS_IN_MODULE_MAPS)
                        ? ImmutableList.<Artifact>of()
                        : privateHeaders,
                featureConfiguration.isEnabled(CppRuleClasses.ONLY_DOTH_HEADERS_IN_MODULE_MAPS)
                        ? Iterables.filter(publicHeaders.getModuleMapHeaders(), CppFileTypes.MODULE_MAP_HEADER)
                        : publicHeaders.getModuleMapHeaders(),
                dependentModuleMaps, additionalExportedHeaders, compiledModule,
                featureConfiguration.isEnabled(CppRuleClasses.MODULE_MAP_HOME_CWD),
                featureConfiguration.isEnabled(CppRuleClasses.GENERATE_SUBMODULES),
                !featureConfiguration.isEnabled(CppRuleClasses.MODULE_MAP_WITHOUT_EXTERN_MODULE));
    }

    private Iterable<CppModuleMap> collectModuleMaps() {
        // Cpp module maps may be null for some rules. We filter the nulls out at the end.
        List<CppModuleMap> result = deps.stream().map(CPP_DEPS_TO_MODULES).collect(toCollection(ArrayList::new));
        if (ruleContext.getRule().getAttributeDefinition(":stl") != null) {
            CcCompilationInfo stl = ruleContext.getPrerequisite(":stl", Mode.TARGET, CcCompilationInfo.PROVIDER);
            if (stl != null) {
                result.add(stl.getCcCompilationContext().getCppModuleMap());
            }
        }

        if (ccToolchain != null) {
            result.add(ccToolchain.getCcCompilationContext().getCppModuleMap());
        }
        for (CppModuleMap additionalCppModuleMap : additionalCppModuleMaps) {
            result.add(additionalCppModuleMap);
        }

        return Iterables.filter(result, Predicates.<CppModuleMap>notNull());
    }

    private TransitiveLipoInfoProvider collectTransitiveLipoInfo(CcCompilationOutputs outputs) {
        if (fdoSupport.getFdoSupport().getFdoRoot() == null) {
            return TransitiveLipoInfoProvider.EMPTY;
        }
        NestedSetBuilder<IncludeScannable> scannableBuilder = NestedSetBuilder.stableOrder();
        // TODO(bazel-team): Only fetch the STL prerequisite in one place.
        TransitiveInfoCollection stl = ruleContext.getPrerequisite(":stl", Mode.TARGET);
        if (stl != null) {
            TransitiveLipoInfoProvider provider = stl.getProvider(TransitiveLipoInfoProvider.class);
            if (provider != null) {
                scannableBuilder.addTransitive(provider.getTransitiveIncludeScannables());
            }
        }

        for (TransitiveLipoInfoProvider dep : AnalysisUtils.getProviders(deps, TransitiveLipoInfoProvider.class)) {
            scannableBuilder.addTransitive(dep.getTransitiveIncludeScannables());
        }

        for (IncludeScannable scannable : outputs.getLipoScannables()) {
            Preconditions.checkState(scannable.getIncludeScannerSources().size() == 1);
            scannableBuilder.add(scannable);
        }
        return new TransitiveLipoInfoProvider(scannableBuilder.build());
    }

    private NestedSet<Artifact> getTemps(CcCompilationOutputs compilationOutputs) {
        return cppConfiguration.isLipoContextCollector() ? NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER)
                : compilationOutputs.getTemps();
    }

    /** @return whether this target needs to generate a pic header module. */
    private boolean getGeneratesPicHeaderModule() {
        return shouldProvideHeaderModules() && !fake && generatePicAction;
    }

    /** @return whether this target needs to generate a no-PIC header module. */
    private boolean getGeneratesNoPicHeaderModule() {
        return shouldProvideHeaderModules() && !fake && generateNoPicAction;
    }

    /** @return whether we want to provide header modules for the current target. */
    private boolean shouldProvideHeaderModules() {
        return featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES)
                && !cppConfiguration.isLipoContextCollector();
    }

    /** @return the no-PIC header module artifact for the current target. */
    private Artifact getHeaderModule(Artifact moduleMapArtifact) {
        PathFragment objectDir = CppHelper.getObjDirectory(ruleContext.getLabel());
        PathFragment outputName = objectDir.getRelative(
                cppConfiguration.shortenObjFilePath() ? moduleMapArtifact.getRootRelativePath().getBaseName()
                        : moduleMapArtifact.getRootRelativePath().getPathString());
        return ruleContext.getRelatedArtifact(outputName, ".pcm");
    }

    /** @return the pic header module artifact for the current target. */
    private Artifact getPicHeaderModule(Artifact moduleMapArtifact) {
        PathFragment objectDir = CppHelper.getObjDirectory(ruleContext.getLabel());
        PathFragment outputName = objectDir.getRelative(
                cppConfiguration.shortenObjFilePath() ? moduleMapArtifact.getRootRelativePath().getBaseName()
                        : moduleMapArtifact.getRootRelativePath().getPathString());
        return ruleContext.getRelatedArtifact(outputName, ".pic.pcm");
    }

    /**
     * Calculate the output names for object file paths from a set of source files.
     *
     * <p>The object file path is constructed in the following format:
     *    <bazel-bin>/<target_package_path>/_objs/<target_name>/<output_name>.<obj_extension>
     * When there's no two source files having the same basename:
     *   <output_name> = <prefixDir>/<source_file_base_name>
     * otherwise:
     *   <output_name> = <prefixDir>/N/<source_file_base_name>,
     *   N = the files order among the source files with the same basename, starts with 0
     *
     * <p>Examples: 1. Output names for ["lib1/foo.cc", "lib2/bar.cc"] are ["foo", "bar"]
     *              2. Output names for ["foo.cc", "bar.cc", "foo.cpp", "lib/foo.cc"]
     *                 are ["0/foo", "bar", "1/foo", "2/foo"]
     */
    private ImmutableMap<Artifact, String> calculateOutputNameMap(NestedSet<Artifact> sourceArtifacts,
            String prefixDir) {
        ImmutableMap.Builder<Artifact, String> builder = ImmutableMap.builder();

        HashMap<String, Integer> count = new LinkedHashMap<>();
        HashMap<String, Integer> number = new LinkedHashMap<>();
        for (Artifact source : sourceArtifacts) {
            String outputName = FileSystemUtils.removeExtension(source.getRootRelativePath()).getBaseName();
            count.put(outputName.toLowerCase(), count.getOrDefault(outputName.toLowerCase(), 0) + 1);
        }

        for (Artifact source : sourceArtifacts) {
            String outputName = FileSystemUtils.removeExtension(source.getRootRelativePath()).getBaseName();
            if (count.getOrDefault(outputName.toLowerCase(), 0) > 1) {
                int num = number.getOrDefault(outputName.toLowerCase(), 0);
                number.put(outputName.toLowerCase(), num + 1);
                outputName = num + "/" + outputName;
            }
            // If prefixDir is set, prepend it to the outputName
            if (prefixDir != null) {
                outputName = prefixDir + "/" + outputName;
            }
            builder.put(source, outputName);
        }

        return builder.build();
    }

    /**
     * Calculate outputNameMap for different source types separately. Returns a merged outputNameMap
     * for all artifacts.
     */
    private ImmutableMap<Artifact, String> calculateOutputNameMapByType(Set<CppSource> sources, String prefixDir) {
        ImmutableMap.Builder<Artifact, String> builder = ImmutableMap.builder();
        builder.putAll(calculateOutputNameMap(getSourceArtifactsByType(sources, CppSource.Type.SOURCE), prefixDir));
        builder.putAll(calculateOutputNameMap(getSourceArtifactsByType(sources, CppSource.Type.HEADER), prefixDir));
        // TODO(plf): Removing CLIF logic
        builder.putAll(calculateOutputNameMap(getSourceArtifactsByType(sources, CppSource.Type.CLIF_INPUT_PROTO),
                prefixDir));
        return builder.build();
    }

    private NestedSet<Artifact> getSourceArtifactsByType(Set<CppSource> sources, CppSource.Type type) {
        NestedSetBuilder<Artifact> result = NestedSetBuilder.stableOrder();
        result.addAll(sources.stream().filter(source -> source.getType().equals(type)).map(CppSource::getSource)
                .collect(Collectors.toList()));
        return result.build();
    }

    /**
     * Constructs the C++ compiler actions. It generally creates one action for every specified source
     * file. It takes into account LIPO, fake-ness, coverage, and PIC, in addition to using the
     * settings specified on the current object. This method should only be called once.
     */
    private CcCompilationOutputs createCcCompileActions() throws RuleErrorException {
        CcCompilationOutputs.Builder result = new CcCompilationOutputs.Builder();
        Preconditions.checkNotNull(ccCompilationContext);
        AnalysisEnvironment env = ruleContext.getAnalysisEnvironment();

        if (shouldProvideHeaderModules()) {
            Label moduleMapLabel = Label.parseAbsoluteUnchecked(ccCompilationContext.getCppModuleMap().getName());
            Collection<Artifact> modules = createModuleAction(result, ccCompilationContext.getCppModuleMap());
            if (featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULE_CODEGEN)) {
                for (Artifact module : modules) {
                    // TODO(djasper): Investigate whether we need to use a label separate from that of the
                    // module map. It is used for per-file-copts.
                    createModuleCodegenAction(result, moduleMapLabel, module);
                }
            }
        } else if (ccCompilationContext.getVerificationModuleMap() != null) {
            Collection<Artifact> modules = createModuleAction(result,
                    ccCompilationContext.getVerificationModuleMap());
            for (Artifact module : modules) {
                result.addHeaderTokenFile(module);
            }
        }

        ImmutableMap<Artifact, String> outputNameMap = null;
        if (cppConfiguration.shortenObjFilePath()) {
            String outputNamePrefixDir = null;
            // purpose is only used by objc rules, it ends with either "_non_objc_arc" or "_objc_arc".
            // Here we use it to distinguish arc and non-arc compilation.
            if (purpose != null) {
                outputNamePrefixDir = purpose.endsWith("_non_objc_arc") ? "non_arc" : "arc";
            }
            outputNameMap = calculateOutputNameMapByType(compilationUnitSources, outputNamePrefixDir);
        }

        for (CppSource source : compilationUnitSources) {
            Artifact sourceArtifact = source.getSource();
            Label sourceLabel = source.getLabel();
            CppCompileActionBuilder builder = initializeCompileAction(sourceArtifact);

            builder.setSemantics(semantics).addMandatoryInputs(additionalCompilationInputs)
                    .addAdditionalIncludeScanningRoots(additionalIncludeScanningRoots);

            boolean bitcodeOutput = featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)
                    && CppFileTypes.LTO_SOURCE.matches(sourceArtifact.getFilename());

            String outputName = cppConfiguration.shortenObjFilePath() ? outputNameMap.get(sourceArtifact)
                    : FileSystemUtils.removeExtension(sourceArtifact.getRootRelativePath()).getPathString();

            if (!sourceArtifact.isTreeArtifact()) {
                switch (source.getType()) {
                case HEADER:
                    createHeaderAction(sourceLabel, outputName, result, env, builder,
                            isGenerateDotdFile(sourceArtifact));
                    break;
                default:
                    createSourceAction(sourceLabel, outputName, result, env, sourceArtifact, builder,
                            // TODO(plf): Continue removing CLIF logic from C++. Follow up changes would include
                            // refactoring CppSource.Type and ArtifactCategory to be classes instead of enums
                            // that could be instantiated with arbitrary values.
                            source.getType() == CppSource.Type.CLIF_INPUT_PROTO ? ArtifactCategory.CLIF_OUTPUT_PROTO
                                    : ArtifactCategory.OBJECT_FILE,
                            ccCompilationContext.getCppModuleMap(), /* addObject= */ true, isCodeCoverageEnabled(),
                            // The source action does not generate dwo when it has bitcode
                            // output (since it isn't generating a native object with debug
                            // info). In that case the LtoBackendAction will generate the dwo.
                            ccToolchain.shouldCreatePerObjectDebugInfo(featureConfiguration) && !bitcodeOutput,
                            isGenerateDotdFile(sourceArtifact));
                    break;
                }
            } else {
                switch (source.getType()) {
                case HEADER:
                    Artifact headerTokenFile = createCompileActionTemplate(env, source, outputName, builder,
                            ImmutableList.of(ArtifactCategory.GENERATED_HEADER, ArtifactCategory.PROCESSED_HEADER),
                            false);
                    result.addHeaderTokenFile(headerTokenFile);
                    break;
                case SOURCE:
                    Artifact objectFile = createCompileActionTemplate(env, source, outputName, builder,
                            ImmutableList.of(ArtifactCategory.OBJECT_FILE), false);
                    result.addObjectFile(objectFile);

                    if (generatePicAction) {
                        Artifact picObjectFile = createCompileActionTemplate(env, source, outputName, builder,
                                ImmutableList.of(ArtifactCategory.PIC_OBJECT_FILE), true);
                        result.addPicObjectFile(picObjectFile);
                    }
                    break;
                default:
                    throw new IllegalStateException(
                            "Encountered invalid source types when creating CppCompileActionTemplates");
                }
            }
        }

        return result.build();
    }

    private Artifact createCompileActionTemplate(AnalysisEnvironment env, CppSource source, String outputName,
            CppCompileActionBuilder builder, Iterable<ArtifactCategory> outputCategories, boolean usePic) {
        SpecialArtifact sourceArtifact = (SpecialArtifact) source.getSource();
        SpecialArtifact outputFiles = CppHelper.getCompileOutputTreeArtifact(ruleContext, sourceArtifact,
                outputName, usePic);
        // TODO(rduan): Dotd file output is not supported yet.
        builder.setOutputs(outputFiles, /* dotdFile= */ null);
        builder.setVariables(setupCompileBuildVariables(builder, /* sourceLabel= */ null, /* outputName= */ null,
                usePic, /* ccRelativeName= */ null, /* autoFdoImportPath= */ null,
                ccCompilationContext.getCppModuleMap(), /* gcnoFile= */ null, /* dwoFile= */ null,
                /* ltoIndexingFile= */ null, /* additionalBuildVariables= */ ImmutableMap.of()));
        semantics.finalizeCompileActionBuilder(ruleContext, builder);
        // Make sure this builder doesn't reference ruleContext outside of analysis phase.
        CppCompileActionTemplate actionTemplate = new CppCompileActionTemplate(sourceArtifact, outputFiles, builder,
                ccToolchain, outputCategories, ruleContext.getActionOwner());
        env.registerAction(actionTemplate);

        return outputFiles;
    }

    /**
     * Return flags that were specified on the Blaze command line. Take the filetype of sourceFilename
     * into account.
     */
    public static ImmutableList<String> getCoptsFromOptions(CppConfiguration config, String sourceFilename) {
        ImmutableList.Builder<String> flagsBuilder = ImmutableList.builder();

        flagsBuilder.addAll(config.getCopts());

        if (CppFileTypes.C_SOURCE.matches(sourceFilename)) {
            flagsBuilder.addAll(config.getCOptions());
        }

        if (CppFileTypes.CPP_SOURCE.matches(sourceFilename) || CppFileTypes.CPP_HEADER.matches(sourceFilename)
                || CppFileTypes.CPP_MODULE_MAP.matches(sourceFilename)
                || CppFileTypes.CLIF_INPUT_PROTO.matches(sourceFilename)) {
            flagsBuilder.addAll(config.getCxxopts());
        }

        return flagsBuilder.build();
    }

    private CcToolchainVariables setupCompileBuildVariables(CppCompileActionBuilder builder, Label sourceLabel,
            String outputName, boolean usePic, PathFragment ccRelativeName, PathFragment autoFdoImportPath,
            CppModuleMap cppModuleMap, Artifact gcnoFile, Artifact dwoFile, Artifact ltoIndexingFile,
            ImmutableMap<String, String> additionalBuildVariables) {
        Artifact sourceFile = builder.getSourceFile();
        ImmutableList.Builder<String> userCompileFlags = ImmutableList.builder();
        userCompileFlags.addAll(getCoptsFromOptions(cppConfiguration, sourceFile.getExecPathString()));
        userCompileFlags.addAll(copts);
        if (sourceFile != null && sourceLabel != null) {
            userCompileFlags.addAll(collectPerFileCopts(sourceFile, sourceLabel));
        }
        String dotdFileExecPath = null;
        if (isGenerateDotdFile(builder.getSourceFile())) {
            Preconditions.checkNotNull(builder.getDotdFile());
            dotdFileExecPath = builder.getDotdFile().getSafeExecPath().getPathString();
        }
        ImmutableMap.Builder<String, String> allAdditionalBuildVariables = ImmutableMap.builder();
        allAdditionalBuildVariables.putAll(additionalBuildVariables);
        if (ccRelativeName != null) {
            allAdditionalBuildVariables.putAll(fdoSupport.getFdoSupport().configureCompilation(builder, ruleContext,
                    ccRelativeName, autoFdoImportPath, PathFragment.create(outputName), usePic,
                    featureConfiguration, fdoSupport));
        }
        return CompileBuildVariables.setupVariablesOrReportRuleError(ruleContext, featureConfiguration, ccToolchain,
                toPathString(sourceFile), toPathString(builder.getOutputFile()), toPathString(gcnoFile),
                toPathString(dwoFile), toPathString(ltoIndexingFile), ImmutableList.of(), userCompileFlags.build(),
                cppModuleMap, usePic, builder.getTempOutputFile(),
                CppHelper.getFdoBuildStamp(ruleContext, fdoSupport.getFdoSupport()), dotdFileExecPath,
                ImmutableList.copyOf(variablesExtensions), allAdditionalBuildVariables.build(),
                ccCompilationContext.getDirectModuleMaps(), ccCompilationContext.getIncludeDirs(),
                ccCompilationContext.getQuoteIncludeDirs(), ccCompilationContext.getSystemIncludeDirs(),
                ccCompilationContext.getDefines());
    }

    private static String toPathString(Artifact a) {
        return a == null ? null : a.getExecPathString();
    }

    /**
     * Returns a {@code CppCompileActionBuilder} with the common fields for a C++ compile action being
     * initialized.
     */
    private CppCompileActionBuilder initializeCompileAction(Artifact sourceArtifact) {
        CppCompileActionBuilder builder = createCompileActionBuilder(sourceArtifact);
        builder.setFeatureConfiguration(featureConfiguration);

        return builder;
    }

    /**
     * Creates a basic cpp compile action builder for source file. Configures options, crosstool
     * inputs, output and dotd file names, {@code CcCompilationContext} and copts.
     */
    private CppCompileActionBuilder createCompileActionBuilder(Artifact source) {
        CppCompileActionBuilder builder = new CppCompileActionBuilder(ruleContext, ccToolchain, configuration);
        builder.setSourceFile(source);
        builder.setCcCompilationContext(ccCompilationContext);
        builder.setCoptsFilter(coptsFilter);
        return builder;
    }

    private void createModuleCodegenAction(CcCompilationOutputs.Builder result, Label sourceLabel, Artifact module)
            throws RuleErrorException {
        if (fake) {
            // We can't currently foresee a situation where we'd want nocompile tests for module codegen.
            // If we find one, support needs to be added here.
            return;
        }
        String outputName = cppConfiguration.shortenObjFilePath() ? module.getRootRelativePath().getBaseName()
                : module.getRootRelativePath().getPathString();

        // TODO(djasper): Make this less hacky after refactoring how the PIC/noPIC actions are created.
        boolean pic = module.getFilename().contains(".pic.");

        CppCompileActionBuilder builder = initializeCompileAction(module);
        builder.setSemantics(semantics);
        builder.setPicMode(pic);
        builder.setOutputs(ruleContext, ArtifactCategory.OBJECT_FILE, outputName, isGenerateDotdFile(module));
        PathFragment ccRelativeName = module.getRootRelativePath();

        String gcnoFileName = CppHelper.getArtifactNameForCategory(ruleContext, ccToolchain,
                ArtifactCategory.COVERAGE_DATA_FILE, outputName);
        // TODO(djasper): This is now duplicated. Refactor the various create..Action functions.
        Artifact gcnoFile = isCodeCoverageEnabled() && !CppHelper.isLipoOptimization(cppConfiguration, ccToolchain)
                ? CppHelper.getCompileOutputArtifact(ruleContext, gcnoFileName, configuration)
                : null;

        boolean generateDwo = ccToolchain.shouldCreatePerObjectDebugInfo(featureConfiguration);
        Artifact dwoFile = generateDwo ? getDwoFile(builder.getOutputFile()) : null;
        // TODO(tejohnson): Add support for ThinLTO if needed.
        boolean bitcodeOutput = featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)
                && CppFileTypes.LTO_SOURCE.matches(module.getFilename());
        Preconditions.checkState(!bitcodeOutput);

        builder.setVariables(setupCompileBuildVariables(builder, sourceLabel, outputName, /* usePic= */ pic,
                ccRelativeName, module.getExecPath(), ccCompilationContext.getCppModuleMap(), gcnoFile, dwoFile,
                /* ltoIndexingFile= */ null, /* additionalBuildVariables= */ ImmutableMap.of()));

        builder.setGcnoFile(gcnoFile);
        builder.setDwoFile(dwoFile);

        semantics.finalizeCompileActionBuilder(ruleContext, builder);
        CppCompileAction compileAction = builder.buildOrThrowRuleError(ruleContext);
        AnalysisEnvironment env = ruleContext.getAnalysisEnvironment();
        env.registerAction(compileAction);
        Artifact objectFile = compileAction.getOutputFile();
        if (pic) {
            result.addPicObjectFile(objectFile);
        } else {
            result.addObjectFile(objectFile);
        }
    }

    /** Returns true if Dotd file should be generated. */
    private boolean isGenerateDotdFile(Artifact sourceArtifact) {
        return CppFileTypes.headerDiscoveryRequired(sourceArtifact)
                && !featureConfiguration.isEnabled(CppRuleClasses.PARSE_SHOWINCLUDES);
    }

    private void createHeaderAction(Label sourceLabel, String outputName, CcCompilationOutputs.Builder result,
            AnalysisEnvironment env, CppCompileActionBuilder builder, boolean generateDotd)
            throws RuleErrorException {
        String outputNameBase = CppHelper.getArtifactNameForCategory(ruleContext, ccToolchain,
                ArtifactCategory.GENERATED_HEADER, outputName);

        builder.setOutputs(ruleContext, ArtifactCategory.PROCESSED_HEADER, outputNameBase, generateDotd)
                // If we generate pic actions, we prefer the header actions to use the pic artifacts.
                .setPicMode(generatePicAction);
        builder.setVariables(setupCompileBuildVariables(builder, sourceLabel, /* outputName= */ null,
                generatePicAction, /* ccRelativeName= */ null, /* autoFdoImportPath= */ null,
                ccCompilationContext.getCppModuleMap(), /* gcnoFile= */ null, /* dwoFile= */ null,
                /* ltoIndexingFile= */ null, /* additionalBuildVariables= */ ImmutableMap.of()));
        semantics.finalizeCompileActionBuilder(ruleContext, builder);
        CppCompileAction compileAction = builder.buildOrThrowRuleError(ruleContext);
        env.registerAction(compileAction);
        Artifact tokenFile = compileAction.getOutputFile();
        result.addHeaderTokenFile(tokenFile);
    }

    private Collection<Artifact> createModuleAction(CcCompilationOutputs.Builder result, CppModuleMap cppModuleMap)
            throws RuleErrorException {
        AnalysisEnvironment env = ruleContext.getAnalysisEnvironment();
        Artifact moduleMapArtifact = cppModuleMap.getArtifact();
        CppCompileActionBuilder builder = initializeCompileAction(moduleMapArtifact);

        builder.setSemantics(semantics);

        // A header module compile action is just like a normal compile action, but:
        // - the compiled source file is the module map
        // - it creates a header module (.pcm file).
        return createSourceAction(Label.parseAbsoluteUnchecked(cppModuleMap.getName()),
                // The header module(.pcm) is generated at most one file per target,
                // so it's safe to remove module map's package path from its output name.
                cppConfiguration.shortenObjFilePath()
                        ? FileSystemUtils.removeExtension(moduleMapArtifact.getRootRelativePath()).getBaseName()
                        : FileSystemUtils.removeExtension(moduleMapArtifact.getRootRelativePath()).getPathString(),
                result, env, moduleMapArtifact, builder, ArtifactCategory.CPP_MODULE, cppModuleMap,
                /* addObject= */ false, /* enableCoverage= */ false, /* generateDwo= */ false,
                isGenerateDotdFile(moduleMapArtifact));
    }

    private Collection<Artifact> createSourceAction(Label sourceLabel, String outputName,
            CcCompilationOutputs.Builder result, AnalysisEnvironment env, Artifact sourceArtifact,
            CppCompileActionBuilder builder, ArtifactCategory outputCategory, CppModuleMap cppModuleMap,
            boolean addObject, boolean enableCoverage, boolean generateDwo, boolean generateDotd)
            throws RuleErrorException {
        ImmutableList.Builder<Artifact> directOutputs = new ImmutableList.Builder<>();
        PathFragment ccRelativeName = sourceArtifact.getRootRelativePath();
        if (CppHelper.isLipoOptimization(cppConfiguration, ccToolchain)) {
            // TODO(bazel-team): we shouldn't be needing this, merging ccCompilationContext with the
            // binary
            // is a superset of necessary information.
            LipoContextProvider lipoProvider = Preconditions
                    .checkNotNull(CppHelper.getLipoContextProvider(ruleContext), outputName);
            builder.setCcCompilationContext(CcCompilationContext
                    .mergeForLipo(lipoProvider.getLipoCcCompilationContext(), ccCompilationContext));
        }
        if (fake) {
            boolean usePic = !generateNoPicAction;
            createFakeSourceAction(sourceLabel, outputName, result, env, builder, outputCategory, addObject,
                    ccRelativeName, sourceArtifact.getExecPath(), usePic, generateDotd);
        } else {
            boolean bitcodeOutput = featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)
                    && CppFileTypes.LTO_SOURCE.matches(sourceArtifact.getFilename());

            // Create PIC compile actions (same as no-PIC, but use -fPIC and
            // generate .pic.o, .pic.d, .pic.gcno instead of .o, .d, .gcno.)
            if (generatePicAction) {
                String picOutputBase = CppHelper.getArtifactNameForCategory(ruleContext, ccToolchain,
                        ArtifactCategory.PIC_FILE, outputName);
                CppCompileActionBuilder picBuilder = copyAsPicBuilder(builder, picOutputBase, outputCategory,
                        generateDotd);
                String gcnoFileName = CppHelper.getArtifactNameForCategory(ruleContext, ccToolchain,
                        ArtifactCategory.COVERAGE_DATA_FILE, picOutputBase);
                Artifact gcnoFile = enableCoverage
                        ? CppHelper.getCompileOutputArtifact(ruleContext, gcnoFileName, configuration)
                        : null;
                Artifact dwoFile = generateDwo ? getDwoFile(picBuilder.getOutputFile()) : null;
                Artifact ltoIndexingFile = bitcodeOutput ? getLtoIndexingFile(picBuilder.getOutputFile()) : null;

                picBuilder.setVariables(setupCompileBuildVariables(picBuilder, sourceLabel, outputName,
                        /* usePic= */ true, ccRelativeName, sourceArtifact.getExecPath(),
                        ccCompilationContext.getCppModuleMap(), gcnoFile, dwoFile, ltoIndexingFile,
                        /* additionalBuildVariables= */ ImmutableMap.of()));

                result.addTemps(createTempsActions(sourceArtifact, sourceLabel, outputName, picBuilder,
                        /* usePic= */ true, /* generateDotd= */ generateDotd, ccRelativeName));

                picBuilder.setGcnoFile(gcnoFile);
                picBuilder.setDwoFile(dwoFile);
                picBuilder.setLtoIndexingFile(ltoIndexingFile);

                semantics.finalizeCompileActionBuilder(ruleContext, picBuilder);
                CppCompileAction picAction = picBuilder.buildOrThrowRuleError(ruleContext);
                env.registerAction(picAction);
                directOutputs.add(picAction.getOutputFile());
                if (addObject) {
                    result.addPicObjectFile(picAction.getOutputFile());

                    if (bitcodeOutput) {
                        result.addLtoBitcodeFile(picAction.getOutputFile(), ltoIndexingFile);
                    }
                }
                if (dwoFile != null) {
                    // Host targets don't produce .dwo files.
                    result.addPicDwoFile(dwoFile);
                }
                if (cppConfiguration.isLipoContextCollector() && !generateNoPicAction) {
                    result.addLipoScannable(picAction);
                }
            }

            if (generateNoPicAction) {
                Artifact noPicOutputFile = CppHelper.getCompileOutputArtifact(ruleContext,
                        CppHelper.getArtifactNameForCategory(ruleContext, ccToolchain, outputCategory, outputName),
                        configuration);
                builder.setOutputs(ruleContext, outputCategory, outputName, generateDotd);
                String gcnoFileName = CppHelper.getArtifactNameForCategory(ruleContext, ccToolchain,
                        ArtifactCategory.COVERAGE_DATA_FILE, outputName);

                // Create no-PIC compile actions
                Artifact gcnoFile = !CppHelper.isLipoOptimization(cppConfiguration, ccToolchain) && enableCoverage
                        ? CppHelper.getCompileOutputArtifact(ruleContext, gcnoFileName, configuration)
                        : null;

                Artifact noPicDwoFile = generateDwo ? getDwoFile(noPicOutputFile) : null;
                Artifact ltoIndexingFile = bitcodeOutput ? getLtoIndexingFile(builder.getOutputFile()) : null;

                builder.setVariables(setupCompileBuildVariables(builder, sourceLabel, outputName,
                        /* usePic= */ false, ccRelativeName, sourceArtifact.getExecPath(), cppModuleMap, gcnoFile,
                        noPicDwoFile, ltoIndexingFile, /* additionalBuildVariables= */ ImmutableMap.of()));

                result.addTemps(createTempsActions(sourceArtifact, sourceLabel, outputName, builder,
                        /* usePic= */ false, generateDotd, ccRelativeName));

                builder.setGcnoFile(gcnoFile);
                builder.setDwoFile(noPicDwoFile);
                builder.setLtoIndexingFile(ltoIndexingFile);

                semantics.finalizeCompileActionBuilder(ruleContext, builder);
                CppCompileAction compileAction = builder.buildOrThrowRuleError(ruleContext);
                env.registerAction(compileAction);
                Artifact objectFile = compileAction.getOutputFile();
                directOutputs.add(objectFile);
                if (addObject) {
                    result.addObjectFile(objectFile);
                    if (bitcodeOutput) {
                        result.addLtoBitcodeFile(objectFile, ltoIndexingFile);
                    }
                }
                if (noPicDwoFile != null) {
                    // Host targets don't produce .dwo files.
                    result.addDwoFile(noPicDwoFile);
                }
                if (cppConfiguration.isLipoContextCollector()) {
                    result.addLipoScannable(compileAction);
                }
            }
        }
        return directOutputs.build();
    }

    /**
     * Creates cpp PIC compile action builder from the given builder by adding necessary copt and
     * changing output and dotd file names.
     */
    private CppCompileActionBuilder copyAsPicBuilder(CppCompileActionBuilder builder, String outputName,
            ArtifactCategory outputCategory, boolean generateDotd) throws RuleErrorException {
        CppCompileActionBuilder picBuilder = new CppCompileActionBuilder(builder);
        picBuilder.setPicMode(true).setOutputs(ruleContext, outputCategory, outputName, generateDotd);

        return picBuilder;
    }

    String getOutputNameBaseWith(String base, boolean usePic) throws RuleErrorException {
        return usePic
                ? CppHelper.getArtifactNameForCategory(ruleContext, ccToolchain, ArtifactCategory.PIC_FILE, base)
                : base;
    }

    private void createFakeSourceAction(Label sourceLabel, String outputName, CcCompilationOutputs.Builder result,
            AnalysisEnvironment env, CppCompileActionBuilder builder, ArtifactCategory outputCategory,
            boolean addObject, PathFragment ccRelativeName, PathFragment execPath, boolean usePic,
            boolean generateDotd) throws RuleErrorException {
        String outputNameBase = getOutputNameBaseWith(outputName, usePic);
        String tempOutputName = ruleContext.getConfiguration().getBinFragment()
                .getRelative(CppHelper.getObjDirectory(ruleContext.getLabel()))
                .getRelative(CppHelper.getArtifactNameForCategory(ruleContext, ccToolchain, outputCategory,
                        getOutputNameBaseWith(outputName + ".temp", usePic)))
                .getPathString();
        builder.setPicMode(usePic).setOutputs(ruleContext, outputCategory, outputNameBase, generateDotd)
                .setTempOutputFile(PathFragment.create(tempOutputName));

        builder.setVariables(setupCompileBuildVariables(builder, sourceLabel, outputName, usePic, ccRelativeName,
                execPath, ccCompilationContext.getCppModuleMap(), /* gcnoFile= */ null, /* dwoFile= */ null,
                /* ltoIndexingFile= */ null, /* additionalBuildVariables= */ ImmutableMap.of()));
        semantics.finalizeCompileActionBuilder(ruleContext, builder);
        CppCompileAction action = builder.buildOrThrowRuleError(ruleContext);
        env.registerAction(action);
        if (addObject) {
            if (usePic) {
                result.addPicObjectFile(action.getOutputFile());
            } else {
                result.addObjectFile(action.getOutputFile());
            }
        }
    }

    /** Returns true iff code coverage is enabled for the given target. */
    private boolean isCodeCoverageEnabled() {
        if (configuration.isCodeCoverageEnabled()) {
            // If rule is matched by the instrumentation filter, enable instrumentation
            if (InstrumentedFilesCollector.shouldIncludeLocalSources(ruleContext)) {
                return true;
            }
            // At this point the rule itself is not matched by the instrumentation filter. However, we
            // might still want to instrument C++ rules if one of the targets listed in "deps" is
            // instrumented and, therefore, can supply header files that we would want to collect code
            // coverage for. For example, think about cc_test rule that tests functionality defined in a
            // header file that is supplied by the cc_library.
            //
            // Note that we only check direct prerequisites and not the transitive closure. This is done
            // for two reasons:
            // a) It is a good practice to declare libraries which you directly rely on. Including headers
            //    from a library hidden deep inside the transitive closure makes build dependencies less
            //    readable and can lead to unexpected breakage.
            // b) Traversing the transitive closure for each C++ compile action would require more complex
            //    implementation (with caching results of this method) to avoid O(N^2) slowdown.
            if (ruleContext.getRule().isAttrDefined("deps", BuildType.LABEL_LIST)) {
                for (TransitiveInfoCollection dep : ruleContext.getPrerequisites("deps", Mode.TARGET)) {
                    CcCompilationInfo ccCompilationInfo = dep.get(CcCompilationInfo.PROVIDER);
                    if (ccCompilationInfo != null && ccCompilationInfo.getCcCompilationContext() != null
                            && InstrumentedFilesCollector.shouldIncludeLocalSources(configuration, dep)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    private ImmutableList<String> collectPerFileCopts(Artifact sourceFile, Label sourceLabel) {
        return cppConfiguration.getPerFileCopts().stream()
                .filter(perLabelOptions -> (sourceLabel != null && perLabelOptions.isIncluded(sourceLabel))
                        || perLabelOptions.isIncluded(sourceFile))
                .map(PerLabelOptions::getOptions).flatMap(options -> options.stream())
                .collect(ImmutableList.toImmutableList());
    }

    private Artifact getDwoFile(Artifact outputFile) {
        return ruleContext.getRelatedArtifact(outputFile.getRootRelativePath(), ".dwo");
    }

    private Artifact getLtoIndexingFile(Artifact outputFile) {
        String ext = Iterables.getOnlyElement(CppFileTypes.LTO_INDEXING_OBJECT_FILE.getExtensions());
        return ruleContext.getRelatedArtifact(outputFile.getRootRelativePath(), ext);
    }

    /** Create the actions for "--save_temps". */
    private ImmutableList<Artifact> createTempsActions(Artifact source, Label sourceLabel, String outputName,
            CppCompileActionBuilder builder, boolean usePic, boolean generateDotd, PathFragment ccRelativeName)
            throws RuleErrorException {
        if (!cppConfiguration.getSaveTemps()) {
            return ImmutableList.of();
        }

        String path = source.getFilename();
        boolean isCFile = CppFileTypes.C_SOURCE.matches(path);
        boolean isCppFile = CppFileTypes.CPP_SOURCE.matches(path);

        if (!isCFile && !isCppFile) {
            return ImmutableList.of();
        }

        ArtifactCategory category = isCFile ? ArtifactCategory.PREPROCESSED_C_SOURCE
                : ArtifactCategory.PREPROCESSED_CPP_SOURCE;

        String outputArtifactNameBase = getOutputNameBaseWith(outputName, usePic);

        CppCompileActionBuilder dBuilder = new CppCompileActionBuilder(builder);
        dBuilder.setOutputs(ruleContext, category, outputArtifactNameBase, generateDotd);
        dBuilder.setVariables(setupCompileBuildVariables(dBuilder, sourceLabel, outputName, usePic, ccRelativeName,
                source.getExecPath(), ccCompilationContext.getCppModuleMap(), /* gcnoFile= */ null,
                /* dwoFile= */ null, /* ltoIndexingFile= */ null,
                ImmutableMap.of(CompileBuildVariables.OUTPUT_PREPROCESS_FILE.getVariableName(),
                        dBuilder.getRealOutputFilePath().getSafePathString())));
        semantics.finalizeCompileActionBuilder(ruleContext, dBuilder);
        CppCompileAction dAction = dBuilder.buildOrThrowRuleError(ruleContext);
        ruleContext.registerAction(dAction);

        CppCompileActionBuilder sdBuilder = new CppCompileActionBuilder(builder);
        sdBuilder.setOutputs(ruleContext, ArtifactCategory.GENERATED_ASSEMBLY, outputArtifactNameBase,
                generateDotd);
        sdBuilder.setVariables(setupCompileBuildVariables(sdBuilder, sourceLabel, outputName, usePic,
                ccRelativeName, source.getExecPath(), ccCompilationContext.getCppModuleMap(), /* gcnoFile= */ null,
                /* dwoFile= */ null, /* ltoIndexingFile= */ null,
                ImmutableMap.of(CompileBuildVariables.OUTPUT_ASSEMBLY_FILE.getVariableName(),
                        sdBuilder.getRealOutputFilePath().getSafePathString())));
        semantics.finalizeCompileActionBuilder(ruleContext, sdBuilder);
        CppCompileAction sdAction = sdBuilder.buildOrThrowRuleError(ruleContext);
        ruleContext.registerAction(sdAction);

        return ImmutableList.of(dAction.getOutputFile(), sdAction.getOutputFile());
    }
}