| // 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.Preconditions; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.actions.CommandLine; |
| import com.google.devtools.build.lib.actions.CommandLineExpansionException; |
| import com.google.devtools.build.lib.rules.cpp.CcCommon.CoptsFilter; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.ExpansionException; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; |
| import com.google.devtools.build.lib.util.Pair; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import com.google.errorprone.annotations.CanIgnoreReturnValue; |
| import java.util.ArrayList; |
| import java.util.List; |
| import javax.annotation.Nullable; |
| |
| /** The compile command line for the C++ compile action. */ |
| public final class CompileCommandLine { |
| private final Artifact sourceFile; |
| private final CoptsFilter coptsFilter; |
| private final FeatureConfiguration featureConfiguration; |
| private final CcToolchainVariables variables; |
| private final String actionName; |
| private final Artifact dotdFile; |
| |
| private CompileCommandLine( |
| Artifact sourceFile, |
| CoptsFilter coptsFilter, |
| FeatureConfiguration featureConfiguration, |
| CcToolchainVariables variables, |
| String actionName, |
| Artifact dotdFile) { |
| this.sourceFile = Preconditions.checkNotNull(sourceFile); |
| this.coptsFilter = coptsFilter; |
| this.featureConfiguration = Preconditions.checkNotNull(featureConfiguration); |
| this.variables = variables; |
| this.actionName = actionName; |
| this.dotdFile = isGenerateDotdFile(sourceFile) ? dotdFile : null; |
| } |
| |
| /** 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. */ |
| ImmutableMap<String, String> getEnvironment() throws CommandLineExpansionException { |
| try { |
| return featureConfiguration.getEnvironmentVariables(actionName, variables); |
| } catch (ExpansionException e) { |
| throw new CommandLineExpansionException(e.getMessage()); |
| } |
| } |
| |
| /** Returns the tool path for the compilation based on the current feature configuration. */ |
| @VisibleForTesting |
| public String getToolPath() { |
| Preconditions.checkArgument( |
| featureConfiguration.actionIsConfigured(actionName), |
| "Expected action_config for '%s' to be configured", |
| actionName); |
| return featureConfiguration.getToolPathForAction(actionName); |
| } |
| |
| /** |
| * @param overwrittenVariables: Variables that will overwrite original build variables. When null, |
| * unmodified original variables are used. |
| */ |
| List<String> getArguments( |
| @Nullable PathFragment parameterFilePath, @Nullable CcToolchainVariables overwrittenVariables) |
| throws CommandLineExpansionException { |
| List<String> commandLine = new ArrayList<>(); |
| |
| // first: The command name. |
| commandLine.add(getToolPath()); |
| |
| // second: The compiler options. |
| if (parameterFilePath != null) { |
| commandLine.add("@" + parameterFilePath.getSafePathString()); |
| } else { |
| commandLine.addAll(getCompilerOptions(overwrittenVariables)); |
| } |
| return commandLine; |
| } |
| |
| /** |
| * Returns {@link CommandLine} instance that contains the exactly same command line as the {@link |
| * CppCompileAction}. |
| * |
| * @param cppCompileAction - {@link CppCompileAction} owning this {@link CompileCommandLine}. |
| */ |
| public CommandLine getFilteredFeatureConfigurationCommandLine(CppCompileAction cppCompileAction) { |
| return new CommandLine() { |
| |
| @Override |
| public Iterable<String> arguments() throws CommandLineExpansionException { |
| CcToolchainVariables overwrittenVariables = cppCompileAction.getOverwrittenVariables(); |
| List<String> compilerOptions = getCompilerOptions(overwrittenVariables); |
| return ImmutableList.<String>builder().add(getToolPath()).addAll(compilerOptions).build(); |
| } |
| }; |
| } |
| |
| public List<String> getCompilerOptions(@Nullable CcToolchainVariables overwrittenVariables) |
| throws CommandLineExpansionException { |
| try { |
| List<String> options = new ArrayList<>(); |
| |
| CcToolchainVariables updatedVariables = variables; |
| if (variables != null && overwrittenVariables != null) { |
| CcToolchainVariables.Builder variablesBuilder = CcToolchainVariables.builder(variables); |
| variablesBuilder.addAllNonTransitive(overwrittenVariables); |
| updatedVariables = variablesBuilder.build(); |
| } |
| addFilteredOptions( |
| options, featureConfiguration.getPerFeatureExpansions(actionName, updatedVariables)); |
| |
| return options; |
| } catch (ExpansionException e) { |
| throw new CommandLineExpansionException(e.getMessage()); |
| } |
| } |
| |
| // For each option in 'in', add it to 'out' unless it is matched by the 'coptsFilter' regexp. |
| private void addFilteredOptions( |
| List<String> out, List<Pair<String, List<String>>> expandedFeatures) { |
| for (Pair<String, List<String>> pair : expandedFeatures) { |
| if (pair.getFirst().equals(CppRuleClasses.UNFILTERED_COMPILE_FLAGS_FEATURE_NAME)) { |
| out.addAll(pair.getSecond()); |
| continue; |
| } |
| // We do not uses Java's stream API here as it causes a substantial overhead compared to the |
| // very little work that this is actually doing. |
| for (String flag : pair.getSecond()) { |
| if (coptsFilter.passesFilter(flag)) { |
| out.add(flag); |
| } |
| } |
| } |
| } |
| |
| public Artifact getSourceFile() { |
| return sourceFile; |
| } |
| |
| public Artifact getDotdFile() { |
| return dotdFile; |
| } |
| |
| public CcToolchainVariables getVariables() { |
| return variables; |
| } |
| |
| /** |
| * Returns all user provided copts flags. |
| * |
| * TODO(b/64108724): Get rid of this method when we don't need to parse copts to collect include |
| * directories anymore (meaning there is a way of specifying include directories using an |
| * explicit attribute, not using platform-dependent garbage bag that copts is). |
| */ |
| public ImmutableList<String> getCopts() { |
| if (variables.isAvailable(CompileBuildVariables.USER_COMPILE_FLAGS.getVariableName())) { |
| try { |
| return CcToolchainVariables.toStringList( |
| variables, CompileBuildVariables.USER_COMPILE_FLAGS.getVariableName()); |
| } catch (ExpansionException e) { |
| throw new IllegalStateException( |
| "Should not happen - 'user_compile_flags' should be a string list, but wasn't.", e); |
| } |
| } else { |
| return ImmutableList.of(); |
| } |
| } |
| |
| public static Builder builder( |
| Artifact sourceFile, CoptsFilter coptsFilter, String actionName, Artifact dotdFile) { |
| return new Builder(sourceFile, coptsFilter, actionName, dotdFile); |
| } |
| |
| /** A builder for a {@link CompileCommandLine}. */ |
| public static final class Builder { |
| private final Artifact sourceFile; |
| private CoptsFilter coptsFilter; |
| private FeatureConfiguration featureConfiguration; |
| private CcToolchainVariables variables = CcToolchainVariables.EMPTY; |
| private final String actionName; |
| @Nullable private final Artifact dotdFile; |
| |
| public CompileCommandLine build() { |
| return new CompileCommandLine( |
| Preconditions.checkNotNull(sourceFile), |
| Preconditions.checkNotNull(coptsFilter), |
| Preconditions.checkNotNull(featureConfiguration), |
| Preconditions.checkNotNull(variables), |
| Preconditions.checkNotNull(actionName), |
| dotdFile); |
| } |
| |
| private Builder( |
| Artifact sourceFile, CoptsFilter coptsFilter, String actionName, Artifact dotdFile) { |
| this.sourceFile = sourceFile; |
| this.coptsFilter = coptsFilter; |
| this.actionName = actionName; |
| this.dotdFile = dotdFile; |
| } |
| |
| /** Sets the feature configuration for this compile action. */ |
| @CanIgnoreReturnValue |
| public Builder setFeatureConfiguration(FeatureConfiguration featureConfiguration) { |
| this.featureConfiguration = featureConfiguration; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public Builder setVariables(CcToolchainVariables variables) { |
| this.variables = variables; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| @VisibleForTesting |
| Builder setCoptsFilter(CoptsFilter filter) { |
| this.coptsFilter = Preconditions.checkNotNull(filter); |
| return this; |
| } |
| } |
| } |