// Copyright 2017 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.rules.cpp; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.config.PerLabelOptions; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; import com.google.devtools.build.lib.rules.cpp.CppCompileAction.DotdFile; import com.google.devtools.build.lib.util.FileType; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import javax.annotation.Nullable; /** The compile command line for the C++ compile action. */ public final class CompileCommandLine { private final Artifact sourceFile; private final Artifact outputFile; private final Label sourceLabel; private final DotdFile dotdFile; private final List<String> copts; private final Predicate<String> coptsFilter; private final Collection<String> features; private final FeatureConfiguration featureConfiguration; @VisibleForTesting public final CcToolchainFeatures.Variables variables; private final String actionName; private final CppConfiguration cppConfiguration; private final CcToolchainProvider cppProvider; public CompileCommandLine( Artifact sourceFile, Artifact outputFile, Label sourceLabel, ImmutableList<String> copts, Predicate<String> coptsFilter, Collection<String> features, FeatureConfiguration featureConfiguration, CppConfiguration cppConfiguration, CcToolchainFeatures.Variables variables, String actionName, DotdFile dotdFile, CcToolchainProvider cppProvider) { this.sourceFile = Preconditions.checkNotNull(sourceFile); this.outputFile = Preconditions.checkNotNull(outputFile); this.sourceLabel = Preconditions.checkNotNull(sourceLabel); this.copts = Preconditions.checkNotNull(copts); this.coptsFilter = coptsFilter; this.features = Preconditions.checkNotNull(features); this.featureConfiguration = Preconditions.checkNotNull(featureConfiguration); this.cppConfiguration = Preconditions.checkNotNull(cppConfiguration); this.variables = variables; this.actionName = actionName; this.dotdFile = isGenerateDotdFile(sourceFile) ? Preconditions.checkNotNull(dotdFile) : null; this.cppProvider = cppProvider; } /** Returns true if Dotd file should be generated. */ private boolean isGenerateDotdFile(Artifact sourceArtifact) { return CppFileTypes.headerDiscoveryRequired(sourceArtifact) && !featureConfiguration.isEnabled(CppRuleClasses.PARSE_SHOWINCLUDES); } /** Returns the environment variables that should be set for C++ compile actions. */ protected Map<String, String> getEnvironment() { return featureConfiguration.getEnvironmentVariables(actionName, variables); } protected List<String> getArgv( PathFragment outputFile, CcToolchainFeatures.Variables overwrittenVariables) { List<String> commandLine = new ArrayList<>(); // first: The command name. if (!featureConfiguration.actionIsConfigured(actionName)) { commandLine.add( cppConfiguration.getToolPathFragment(CppConfiguration.Tool.GCC).getPathString()); } else { commandLine.add( featureConfiguration .getToolForAction(actionName) .getToolPath(cppConfiguration.getCrosstoolTopPathFragment()) .getPathString()); } // second: The compiler options. commandLine.addAll(getCompilerOptions(overwrittenVariables)); if (!featureConfiguration.isEnabled("compile_action_flags_in_flag_set")) { // third: The file to compile! commandLine.add("-c"); commandLine.add(sourceFile.getExecPathString()); // finally: The output file. (Prefixed with -o). commandLine.add("-o"); commandLine.add(outputFile.getPathString()); } return commandLine; } private boolean isObjcCompile(String actionName) { return (actionName.equals(CppCompileAction.OBJC_COMPILE) || actionName.equals(CppCompileAction.OBJCPP_COMPILE)); } public List<String> getCompilerOptions( @Nullable CcToolchainFeatures.Variables overwrittenVariables) { List<String> options = new ArrayList<>(); CppConfiguration toolchain = cppConfiguration; addFilteredOptions(options, toolchain.getCompilerOptions(features)); String sourceFilename = sourceFile.getExecPathString(); if (CppFileTypes.C_SOURCE.matches(sourceFilename)) { addFilteredOptions(options, toolchain.getCOptions()); } if (CppFileTypes.CPP_SOURCE.matches(sourceFilename) || CppFileTypes.CPP_HEADER.matches(sourceFilename) || CppFileTypes.CPP_MODULE_MAP.matches(sourceFilename) || CppFileTypes.CLIF_INPUT_PROTO.matches(sourceFilename)) { addFilteredOptions(options, toolchain.getCxxOptions(features)); } // TODO(bazel-team): This needs to be before adding getUnfilteredCompilerOptions() and after // adding the warning flags until all toolchains are migrated; currently toolchains use the // unfiltered compiler options to inject include paths, which is superseded by the feature // configuration; on the other hand toolchains switch off warnings for the layering check // that will be re-added by the feature flags. CcToolchainFeatures.Variables updatedVariables = variables; if (variables != null && overwrittenVariables != null) { CcToolchainFeatures.Variables.Builder variablesBuilder = new CcToolchainFeatures.Variables.Builder(); variablesBuilder.addAll(variables); variablesBuilder.addAndOverwriteAll(overwrittenVariables); updatedVariables = variablesBuilder.build(); } addFilteredOptions(options, featureConfiguration.getCommandLine(actionName, updatedVariables)); // Users don't expect the explicit copts to be filtered by coptsFilter, add them verbatim. // Make sure these are added after the options from the feature configuration, so that // those options can be overriden. options.addAll(copts); // Unfiltered compiler options contain system include paths. These must be added after // the user provided options, otherwise users adding include paths will not pick up their // own include paths first. if (!isObjcCompile(actionName)) { options.addAll(cppProvider.getUnfilteredCompilerOptions(features)); } // Add the options of --per_file_copt, if the label or the base name of the source file // matches the specified regular expression filter. for (PerLabelOptions perLabelOptions : cppConfiguration.getPerFileCopts()) { if ((sourceLabel != null && perLabelOptions.isIncluded(sourceLabel)) || perLabelOptions.isIncluded(sourceFile)) { options.addAll(perLabelOptions.getOptions()); } } if (!featureConfiguration.isEnabled("compile_action_flags_in_flag_set")) { if (FileType.contains(outputFile, CppFileTypes.ASSEMBLER, CppFileTypes.PIC_ASSEMBLER)) { options.add("-S"); } else if (FileType.contains( outputFile, CppFileTypes.PREPROCESSED_C, CppFileTypes.PREPROCESSED_CPP, CppFileTypes.PIC_PREPROCESSED_C, CppFileTypes.PIC_PREPROCESSED_CPP)) { options.add("-E"); } } return options; } // For each option in 'in', add it to 'out' unless it is matched by the 'coptsFilter' regexp. private void addFilteredOptions(List<String> out, List<String> in) { Iterables.addAll(out, Iterables.filter(in, coptsFilter)); } public Artifact getSourceFile() { return sourceFile; } public DotdFile getDotdFile() { return dotdFile; } public List<String> getCopts() { return copts; } }