blob: cb66aa5bfcf3397364ce16fd6cc773dd2f60391d [file] [log] [blame]
// 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 com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.devtools.build.lib.rules.cpp.LinkBuildVariables.LINKER_PARAM_FILE;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.actions.AbstractCommandLine;
import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
import com.google.devtools.build.lib.actions.CommandLineExpansionException;
import com.google.devtools.build.lib.actions.CommandLines;
import com.google.devtools.build.lib.actions.ParamFileInfo;
import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType;
import com.google.devtools.build.lib.actions.PathMapper;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
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.rules.cpp.Link.LinkTargetType;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
import net.starlark.java.eval.EvalException;
import net.starlark.java.eval.Starlark;
/**
* Represents the command line of a linker invocation. It supports executables and dynamic libraries
* as well as static libraries.
*/
@Immutable
public final class LinkCommandLine extends AbstractCommandLine {
private final String actionName;
private final String forcedToolPath;
private final CcToolchainVariables variables;
// The feature config can be null for tests.
@Nullable private final FeatureConfiguration featureConfiguration;
private final LinkTargetType linkTargetType;
private final boolean splitCommandLine;
private final ParameterFileType parameterFileType;
private LinkCommandLine(
String actionName,
String forcedToolPath,
LinkTargetType linkTargetType,
boolean splitCommandLine,
ParameterFileType parameterFileType,
CcToolchainVariables variables,
@Nullable FeatureConfiguration featureConfiguration) {
this.actionName = actionName;
this.forcedToolPath = forcedToolPath;
this.variables = variables;
this.featureConfiguration = featureConfiguration;
this.linkTargetType = Preconditions.checkNotNull(linkTargetType);
this.splitCommandLine = splitCommandLine;
this.parameterFileType = parameterFileType;
}
public String getActionName() {
return actionName;
}
/** Returns the path to the linker. */
public String getLinkerPathString() throws EvalException {
if (forcedToolPath != null) {
return forcedToolPath;
} else {
if (!featureConfiguration.actionIsConfigured(actionName)) {
throw Starlark.errorf("Expected action_config for '%s' to be configured", actionName);
}
return featureConfiguration.getToolPathForAction(linkTargetType.getActionName());
}
}
/** Returns the build variables used to template the crosstool for this linker invocation. */
@VisibleForTesting
public CcToolchainVariables getBuildVariables() {
return this.variables;
}
public ImmutableList<String> getParamCommandLine(@Nullable ArtifactExpander expander)
throws CommandLineExpansionException {
ImmutableList.Builder<String> argv = ImmutableList.builder();
try {
if (variables.isAvailable(LINKER_PARAM_FILE.getVariableName())) {
// Filter out linker_param_file
String linkerParamFile =
variables
.getVariable(LINKER_PARAM_FILE.getVariableName())
.getStringValue(LINKER_PARAM_FILE.getVariableName());
argv.addAll(
featureConfiguration.getCommandLine(actionName, variables, expander).stream()
.filter(s -> !s.contains(linkerParamFile))
.collect(toImmutableList()));
} else {
argv.addAll(featureConfiguration.getCommandLine(actionName, variables, expander));
}
} catch (ExpansionException e) {
throw new CommandLineExpansionException(e.getMessage());
}
return argv.build();
}
CommandLines getCommandLines() throws EvalException {
CommandLines.Builder builder = CommandLines.builder();
builder.addSingleArgument(getLinkerPathString());
ParamFileInfo paramFileInfo = null;
if (splitCommandLine) {
try {
Optional<String> formatString =
featureConfiguration.getCommandLine(actionName, variables, null).stream()
.filter(s -> s.contains("LINKER_PARAM_FILE_PLACEHOLDER"))
.findAny();
if (formatString.isPresent()) {
paramFileInfo =
ParamFileInfo.builder(parameterFileType)
.setFlagFormatString(
formatString
.get()
.replace("%", "%%")
.replace("LINKER_PARAM_FILE_PLACEHOLDER", "%s"))
.setUseAlways(true)
.build();
}
} catch (ExpansionException e) {
throw new EvalException(e);
}
}
builder.addCommandLine(this, paramFileInfo);
return builder.build();
}
@Override
public List<String> arguments() throws CommandLineExpansionException {
return arguments(null, null);
}
@Override
public List<String> arguments(ArtifactExpander artifactExpander, PathMapper pathMapper)
throws CommandLineExpansionException {
return getParamCommandLine(artifactExpander);
}
/** A builder for a {@link LinkCommandLine}. */
public static final class Builder {
private String forcedToolPath;
@Nullable private LinkTargetType linkTargetType;
private boolean splitCommandLine;
private ParameterFileType parameterFileType = ParameterFileType.UNQUOTED;
private CcToolchainVariables variables;
private FeatureConfiguration featureConfiguration;
private String actionName;
public LinkCommandLine build() {
if (variables == null) {
variables = CcToolchainVariables.EMPTY;
}
return new LinkCommandLine(
actionName,
forcedToolPath,
linkTargetType,
splitCommandLine,
parameterFileType,
variables,
featureConfiguration);
}
/** Use given tool path instead of the one from feature configuration */
@CanIgnoreReturnValue
public Builder forceToolPath(String forcedToolPath) {
this.forcedToolPath = forcedToolPath;
return this;
}
/** Sets the feature configuration for this link action. */
@CanIgnoreReturnValue
public Builder setFeatureConfiguration(FeatureConfiguration featureConfiguration) {
this.featureConfiguration = featureConfiguration;
return this;
}
/**
* Sets the type of the link. It is an error to try to set this to {@link
* LinkTargetType#INTERFACE_DYNAMIC_LIBRARY}. Note that all the static target types (see {@link
* LinkTargetType#linkerOrArchiver}) are equivalent, and there is no check that the output
* artifact matches the target type extension.
*/
@CanIgnoreReturnValue
public Builder setLinkTargetType(LinkTargetType linkTargetType) {
Preconditions.checkArgument(linkTargetType != LinkTargetType.INTERFACE_DYNAMIC_LIBRARY);
this.linkTargetType = linkTargetType;
return this;
}
@CanIgnoreReturnValue
public Builder setSplitCommandLine(boolean splitCommandLine) {
this.splitCommandLine = splitCommandLine;
return this;
}
@CanIgnoreReturnValue
public Builder setParameterFileType(ParameterFileType parameterFileType) {
this.parameterFileType = parameterFileType;
return this;
}
@CanIgnoreReturnValue
public Builder setBuildVariables(CcToolchainVariables variables) {
this.variables = variables;
return this;
}
@CanIgnoreReturnValue
public Builder setActionName(String actionName) {
this.actionName = actionName;
return this;
}
}
}