blob: f0b6e4d61b1bc2493b738e3787616a65c078588f [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 com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Predicate;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildOptions;
import com.google.devtools.build.lib.analysis.config.CompilationMode;
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
import com.google.devtools.build.lib.analysis.config.PerLabelOptions;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.rules.cpp.CppConfigurationLoader.CppConfigurationParameters;
import com.google.devtools.build.lib.rules.cpp.CppLinkActionConfigs.CppLinkPlatform;
import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType;
import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig;
import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain;
import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain.ActionConfig;
import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain.ArtifactNamePattern;
import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.LinkingModeFlags;
import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.LipoMode;
import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.ToolPath;
import com.google.devtools.common.options.OptionsParsingException;
import com.google.protobuf.TextFormat;
import com.google.protobuf.TextFormat.ParseException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* This class represents the C/C++ parts of the {@link BuildConfiguration}, including the host
* architecture, target architecture, compiler version, and a standard library version. It has
* information about the tools locations and the flags required for compiling.
*/
@SkylarkModule(
name = "cpp",
doc = "A configuration fragment for C++",
category = SkylarkModuleCategory.CONFIGURATION_FRAGMENT
)
@Immutable
public class CppConfiguration extends BuildConfiguration.Fragment {
/**
* String indicating a Mac system, for example when used in a crosstool configuration's host or
* target system name.
*/
public static final String MAC_SYSTEM_NAME = "x86_64-apple-macosx";
/**
* An enumeration of all the tools that comprise a toolchain.
*/
public enum Tool {
AR("ar"),
CPP("cpp"),
GCC("gcc"),
GCOV("gcov"),
GCOVTOOL("gcov-tool"),
LD("ld"),
NM("nm"),
OBJCOPY("objcopy"),
OBJDUMP("objdump"),
STRIP("strip"),
DWP("dwp");
private final String namePart;
private Tool(String namePart) {
this.namePart = namePart;
}
public String getNamePart() {
return namePart;
}
}
/**
* Values for the --hdrs_check option. Note that Bazel only supports and will default to "strict".
*/
public static enum HeadersCheckingMode {
/**
* Legacy behavior: Silently allow any source header file in any of the directories of the
* containing package to be included by sources in this rule and dependent rules.
*/
LOOSE,
/** Warn about undeclared headers. */
WARN,
/** Disallow undeclared headers. */
STRICT
}
/**
* --dynamic_mode parses to DynamicModeFlag, but AUTO will be translated based on platform,
* resulting in a DynamicMode value.
*/
public enum DynamicMode { OFF, DEFAULT, FULLY }
/**
* This enumeration is used for the --strip option.
*/
public enum StripMode {
ALWAYS("always"), // Always strip.
SOMETIMES("sometimes"), // Strip iff compilationMode == FASTBUILD.
NEVER("never"); // Never strip.
private final String mode;
private StripMode(String mode) {
this.mode = mode;
}
@Override
public String toString() {
return mode;
}
}
/** Storage for the libc label, if given. */
public static class LibcTop implements Serializable {
private final Label label;
LibcTop(Label label) {
Preconditions.checkArgument(label != null);
this.label = label;
}
public Label getLabel() {
return label;
}
public PathFragment getSysroot() {
return label.getPackageFragment();
}
@Override
public String toString() {
return label.toString();
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (other instanceof LibcTop) {
return label.equals(((LibcTop) other).label);
} else {
return false;
}
}
@Override
public int hashCode() {
return label.hashCode();
}
}
/**
* This macro will be passed as a command-line parameter (eg. -DBUILD_FDO_TYPE="LIPO").
* For possible values see {@code CppModel.getFdoBuildStamp()}.
*/
public static final String FDO_STAMP_MACRO = "BUILD_FDO_TYPE";
/**
* Represents an optional flag that can be toggled using the package features mechanism.
*/
@Immutable
@VisibleForTesting
static class OptionalFlag implements Serializable {
private final String name;
private final ImmutableList<String> flags;
@VisibleForTesting
OptionalFlag(String name, ImmutableList<String> flags) {
this.name = name;
this.flags = flags;
}
private ImmutableList<String> getFlags() {
return flags;
}
private String getName() {
return name;
}
}
@Immutable
@VisibleForTesting
static class FlagList implements Serializable {
private final ImmutableList<String> prefixFlags;
private final ImmutableList<OptionalFlag> optionalFlags;
private final ImmutableList<String> suffixFlags;
@VisibleForTesting
FlagList(ImmutableList<String> prefixFlags,
ImmutableList<OptionalFlag> optionalFlags,
ImmutableList<String> suffixFlags) {
this.prefixFlags = prefixFlags;
this.optionalFlags = optionalFlags;
this.suffixFlags = suffixFlags;
}
@VisibleForTesting
ImmutableList<String> evaluate(Iterable<String> features) {
ImmutableSet<String> featureSet = ImmutableSet.copyOf(features);
ImmutableList.Builder<String> result = ImmutableList.builder();
result.addAll(prefixFlags);
for (OptionalFlag optionalFlag : optionalFlags) {
// The flag is added if the default is true and the flag is not specified,
// or if the default is false and the flag is specified.
if (featureSet.contains(optionalFlag.getName())) {
result.addAll(optionalFlag.getFlags());
}
}
result.addAll(suffixFlags);
return result.build();
}
}
private final Label crosstoolTop;
private final String hostSystemName;
private final String compiler;
private final String targetCpu;
private final String targetSystemName;
private final String targetLibc;
private final LipoMode lipoMode;
private final PathFragment crosstoolTopPathFragment;
private final String abi;
private final String abiGlibcVersion;
private final String toolchainIdentifier;
private final CcToolchainFeatures toolchainFeatures;
private final boolean supportsGoldLinker;
private final boolean supportsStartEndLib;
private final boolean supportsDynamicLinker;
private final boolean supportsInterfaceSharedObjects;
private final boolean supportsEmbeddedRuntimes;
private final boolean supportsFission;
// We encode three states with two booleans:
// (1) (false false) -> no pic code
// (2) (true false) -> shared libraries as pic, but not binaries
// (3) (true true) -> both shared libraries and binaries as pic
private final boolean toolchainNeedsPic;
private final boolean usePicForBinaries;
private final Path fdoZip;
// TODO(bazel-team): All these labels (except for ccCompilerRuleLabel) can be removed once the
// transition to the cc_compiler rule is complete.
private final Label libcLabel;
private final Label staticRuntimeLibsLabel;
private final Label dynamicRuntimeLibsLabel;
private final Label ccToolchainLabel;
private final PathFragment sysroot;
private final PathFragment runtimeSysroot;
private final List<PathFragment> builtInIncludeDirectories;
private final Map<String, PathFragment> toolPaths;
private final PathFragment ldExecutable;
// Only used during construction.
private final ImmutableList<String> commonLinkOptions;
private final ListMultimap<CompilationMode, String> linkOptionsFromCompilationMode;
private final ListMultimap<LipoMode, String> linkOptionsFromLipoMode;
private final ListMultimap<LinkingMode, String> linkOptionsFromLinkingMode;
private final FlagList compilerFlags;
private final FlagList cxxFlags;
private final FlagList unfilteredCompilerFlags;
private final ImmutableList<String> cOptions;
private final FlagList fullyStaticLinkFlags;
private final FlagList mostlyStaticLinkFlags;
private final FlagList mostlyStaticSharedLinkFlags;
private final FlagList dynamicLinkFlags;
private final FlagList dynamicLibraryLinkFlags;
private final ImmutableList<String> testOnlyLinkFlags;
private final ImmutableList<String> linkOptions;
private final ImmutableList<String> ltoindexOptions;
private final ImmutableList<String> objcopyOptions;
private final ImmutableList<String> ldOptions;
private final ImmutableList<String> arOptions;
private final ImmutableMap<String, String> additionalMakeVariables;
private final CppOptions cppOptions;
// The dynamic mode for linking.
private final DynamicMode dynamicMode;
private final boolean stripBinaries;
private final String solibDirectory;
private final CompilationMode compilationMode;
private final boolean useLLVMCoverageMap;
/**
* If true, the ConfiguredTarget is only used to get the necessary cross-referenced
* CppCompilationContexts, but registering build actions is disabled.
*/
private final boolean lipoContextCollector;
protected CppConfiguration(CppConfigurationParameters params)
throws InvalidConfigurationException {
CrosstoolConfig.CToolchain toolchain = params.toolchain;
cppOptions = params.cppOptions;
this.hostSystemName = toolchain.getHostSystemName();
this.compiler = toolchain.getCompiler();
this.targetCpu = toolchain.getTargetCpu();
this.lipoMode = cppOptions.getLipoMode();
this.targetSystemName = toolchain.getTargetSystemName();
this.targetLibc = toolchain.getTargetLibc();
this.crosstoolTop = params.crosstoolTop;
this.ccToolchainLabel = params.ccToolchainLabel;
this.compilationMode = params.commonOptions.compilationMode;
this.useLLVMCoverageMap = params.commonOptions.useLLVMCoverageMapFormat;
this.lipoContextCollector = cppOptions.lipoCollector;
this.crosstoolTopPathFragment = crosstoolTop.getPackageIdentifier().getPathUnderExecRoot();
try {
this.staticRuntimeLibsLabel =
crosstoolTop.getRelative(
toolchain.hasStaticRuntimesFilegroup()
? toolchain.getStaticRuntimesFilegroup()
: "static-runtime-libs-" + targetCpu);
this.dynamicRuntimeLibsLabel =
crosstoolTop.getRelative(
toolchain.hasDynamicRuntimesFilegroup()
? toolchain.getDynamicRuntimesFilegroup()
: "dynamic-runtime-libs-" + targetCpu);
} catch (LabelSyntaxException e) {
// All of the above label.getRelative() calls are valid labels, and the crosstool_top
// was already checked earlier in the process.
throw new AssertionError(e);
}
if (cppOptions.lipoMode == LipoMode.BINARY) {
// TODO(bazel-team): implement dynamic linking with LIPO
this.dynamicMode = DynamicMode.OFF;
} else {
switch (cppOptions.dynamicMode) {
case DEFAULT:
this.dynamicMode = DynamicMode.DEFAULT; break;
case OFF: this.dynamicMode = DynamicMode.OFF; break;
case FULLY: this.dynamicMode = DynamicMode.FULLY; break;
default: throw new IllegalStateException("Invalid dynamicMode.");
}
}
this.fdoZip = params.fdoZip;
this.stripBinaries =
(cppOptions.stripBinaries == StripMode.ALWAYS
|| (cppOptions.stripBinaries == StripMode.SOMETIMES
&& compilationMode == CompilationMode.FASTBUILD));
CrosstoolConfigurationIdentifier crosstoolConfig =
CrosstoolConfigurationIdentifier.fromToolchain(toolchain);
Preconditions.checkState(crosstoolConfig.getCpu().equals(targetCpu));
Preconditions.checkState(crosstoolConfig.getCompiler().equals(compiler));
Preconditions.checkState(crosstoolConfig.getLibc().equals(targetLibc));
this.solibDirectory = "_solib_" + targetCpu;
this.toolchainIdentifier = toolchain.getToolchainIdentifier();
toolchain = addLegacyFeatures(toolchain);
this.toolchainFeatures = new CcToolchainFeatures(toolchain);
this.supportsGoldLinker = toolchain.getSupportsGoldLinker();
this.supportsStartEndLib = toolchain.getSupportsStartEndLib();
this.supportsInterfaceSharedObjects = toolchain.getSupportsInterfaceSharedObjects();
this.supportsEmbeddedRuntimes = toolchain.getSupportsEmbeddedRuntimes();
this.supportsFission = toolchain.getSupportsFission();
this.toolchainNeedsPic = toolchain.getNeedsPic();
this.usePicForBinaries =
toolchain.getNeedsPic() && compilationMode != CompilationMode.OPT;
this.toolPaths = Maps.newHashMap();
for (CrosstoolConfig.ToolPath tool : toolchain.getToolPathList()) {
PathFragment path = new PathFragment(tool.getPath());
if (!path.isNormalized()) {
throw new IllegalArgumentException("The include path '" + tool.getPath()
+ "' is not normalized.");
}
toolPaths.put(tool.getName(), crosstoolTopPathFragment.getRelative(path));
}
if (toolPaths.isEmpty()) {
// If no paths are specified, we just use the names of the tools as the path.
for (Tool tool : Tool.values()) {
toolPaths.put(tool.getNamePart(),
crosstoolTopPathFragment.getRelative(tool.getNamePart()));
}
} else {
Iterable<Tool> neededTools = Iterables.filter(EnumSet.allOf(Tool.class),
new Predicate<Tool>() {
@Override
public boolean apply(Tool tool) {
if (tool == Tool.DWP) {
// When fission is unsupported, don't check for the dwp tool.
return supportsFission();
} else if (tool == Tool.GCOVTOOL || tool == Tool.OBJCOPY) {
// gcov-tool and objcopy are optional, don't check whether they're present
return false;
} else {
return true;
}
}
});
for (Tool tool : neededTools) {
if (!toolPaths.containsKey(tool.getNamePart())) {
throw new IllegalArgumentException("Tool path for '" + tool.getNamePart()
+ "' is missing");
}
}
}
ListMultimap<CompilationMode, String> cFlags = ArrayListMultimap.create();
ListMultimap<CompilationMode, String> cxxFlags = ArrayListMultimap.create();
linkOptionsFromCompilationMode = ArrayListMultimap.create();
for (CrosstoolConfig.CompilationModeFlags flags : toolchain.getCompilationModeFlagsList()) {
// Remove this when CROSSTOOL files no longer contain 'coverage'.
if (flags.getMode() == CrosstoolConfig.CompilationMode.COVERAGE) {
continue;
}
CompilationMode realmode = importCompilationMode(flags.getMode());
cFlags.putAll(realmode, flags.getCompilerFlagList());
cxxFlags.putAll(realmode, flags.getCxxFlagList());
linkOptionsFromCompilationMode.putAll(realmode, flags.getLinkerFlagList());
}
ListMultimap<LipoMode, String> lipoCFlags = ArrayListMultimap.create();
ListMultimap<LipoMode, String> lipoCxxFlags = ArrayListMultimap.create();
linkOptionsFromLipoMode = ArrayListMultimap.create();
for (CrosstoolConfig.LipoModeFlags flags : toolchain.getLipoModeFlagsList()) {
LipoMode realmode = flags.getMode();
lipoCFlags.putAll(realmode, flags.getCompilerFlagList());
lipoCxxFlags.putAll(realmode, flags.getCxxFlagList());
linkOptionsFromLipoMode.putAll(realmode, flags.getLinkerFlagList());
}
linkOptionsFromLinkingMode = ArrayListMultimap.create();
// If a toolchain supports dynamic libraries at all, there must be at least one
// of the following:
// - a "DYNAMIC" section in linking_mode_flags (even if no flags are needed)
// - a non-empty list in one of the dynamicLibraryLinkerFlag fields
// If none of the above contain data, then the toolchain can't do dynamic linking.
boolean haveDynamicMode = false;
for (LinkingModeFlags flags : toolchain.getLinkingModeFlagsList()) {
LinkingMode realmode = importLinkingMode(flags.getMode());
if (realmode == LinkingMode.DYNAMIC) {
haveDynamicMode = true;
}
linkOptionsFromLinkingMode.putAll(realmode, flags.getLinkerFlagList());
}
this.commonLinkOptions = ImmutableList.copyOf(toolchain.getLinkerFlagList());
List<String> linkerFlagList = toolchain.getDynamicLibraryLinkerFlagList();
List<CToolchain.OptionalFlag> optionalLinkerFlagList =
toolchain.getOptionalDynamicLibraryLinkerFlagList();
if (!linkerFlagList.isEmpty() || !optionalLinkerFlagList.isEmpty()) {
haveDynamicMode = true;
}
this.supportsDynamicLinker = haveDynamicMode;
dynamicLibraryLinkFlags = new FlagList(
ImmutableList.copyOf(linkerFlagList),
convertOptionalOptions(optionalLinkerFlagList),
ImmutableList.<String>of());
this.objcopyOptions = ImmutableList.copyOf(toolchain.getObjcopyEmbedFlagList());
this.ldOptions = ImmutableList.copyOf(toolchain.getLdEmbedFlagList());
this.arOptions = copyOrDefaultIfEmpty(toolchain.getArFlagList(), "rcsD");
this.abi = toolchain.getAbiVersion();
this.abiGlibcVersion = toolchain.getAbiLibcVersion();
// The default value for optional string attributes is the empty string.
PathFragment defaultSysroot = toolchain.getBuiltinSysroot().length() == 0
? null
: new PathFragment(toolchain.getBuiltinSysroot());
if ((defaultSysroot != null) && !defaultSysroot.isNormalized()) {
throw new IllegalArgumentException("The built-in sysroot '" + defaultSysroot
+ "' is not normalized.");
}
if ((cppOptions.libcTop != null) && (defaultSysroot == null)) {
throw new InvalidConfigurationException("The selected toolchain " + toolchainIdentifier
+ " does not support setting --grte_top.");
}
LibcTop libcTop = cppOptions.libcTop;
if ((libcTop == null) && !toolchain.getDefaultGrteTop().isEmpty()) {
try {
libcTop = new CppOptions.LibcTopConverter().convert(toolchain.getDefaultGrteTop());
} catch (OptionsParsingException e) {
throw new InvalidConfigurationException(e.getMessage(), e);
}
}
if ((libcTop != null) && (libcTop.getLabel() != null)) {
libcLabel = libcTop.getLabel();
} else {
libcLabel = null;
}
ImmutableList.Builder<PathFragment> builtInIncludeDirectoriesBuilder
= ImmutableList.builder();
sysroot = libcTop == null ? defaultSysroot : libcTop.getSysroot();
for (String s : toolchain.getCxxBuiltinIncludeDirectoryList()) {
builtInIncludeDirectoriesBuilder.add(
resolveIncludeDir(s, sysroot, crosstoolTopPathFragment));
}
builtInIncludeDirectories = builtInIncludeDirectoriesBuilder.build();
// The runtime sysroot should really be set from --grte_top. However, currently libc has no
// way to set the sysroot. The CROSSTOOL file does set the runtime sysroot, in the
// builtin_sysroot field. This implies that you can not arbitrarily mix and match Crosstool
// and libc versions, you must always choose compatible ones.
runtimeSysroot = defaultSysroot;
String sysrootFlag;
if (sysroot != null) {
sysrootFlag = "--sysroot=" + sysroot;
} else {
sysrootFlag = null;
}
ImmutableList.Builder<String> unfilteredCoptsBuilder = ImmutableList.builder();
if (sysrootFlag != null) {
unfilteredCoptsBuilder.add(sysrootFlag);
}
unfilteredCoptsBuilder.addAll(toolchain.getUnfilteredCxxFlagList());
unfilteredCompilerFlags = new FlagList(
unfilteredCoptsBuilder.build(),
convertOptionalOptions(toolchain.getOptionalUnfilteredCxxFlagList()),
ImmutableList.<String>of());
ImmutableList.Builder<String> linkoptsBuilder = ImmutableList.builder();
linkoptsBuilder.addAll(cppOptions.linkoptList);
if (cppOptions.experimentalOmitfp) {
linkoptsBuilder.add("-Wl,--eh-frame-hdr");
}
if (sysrootFlag != null) {
linkoptsBuilder.add(sysrootFlag);
}
this.linkOptions = linkoptsBuilder.build();
ImmutableList.Builder<String> ltoindexoptsBuilder = ImmutableList.builder();
ltoindexoptsBuilder.addAll(cppOptions.ltoindexoptList);
this.ltoindexOptions = ltoindexoptsBuilder.build();
ImmutableList.Builder<String> coptsBuilder = ImmutableList.<String>builder()
.addAll(toolchain.getCompilerFlagList())
.addAll(cFlags.get(compilationMode))
.addAll(lipoCFlags.get(cppOptions.getLipoMode()));
if (cppOptions.experimentalOmitfp) {
coptsBuilder.add("-fomit-frame-pointer");
coptsBuilder.add("-fasynchronous-unwind-tables");
coptsBuilder.add("-DNO_FRAME_POINTER");
}
this.compilerFlags = new FlagList(
coptsBuilder.build(),
convertOptionalOptions(toolchain.getOptionalCompilerFlagList()),
ImmutableList.copyOf(cppOptions.coptList));
this.cOptions = ImmutableList.copyOf(cppOptions.conlyoptList);
ImmutableList.Builder<String> cxxOptsBuilder = ImmutableList.<String>builder()
.addAll(toolchain.getCxxFlagList())
.addAll(cxxFlags.get(compilationMode))
.addAll(lipoCxxFlags.get(cppOptions.getLipoMode()));
this.cxxFlags = new FlagList(
cxxOptsBuilder.build(),
convertOptionalOptions(toolchain.getOptionalCxxFlagList()),
ImmutableList.copyOf(cppOptions.cxxoptList));
this.ldExecutable = getToolPathFragment(CppConfiguration.Tool.LD);
boolean stripBinaries =
(cppOptions.stripBinaries == StripMode.ALWAYS)
|| ((cppOptions.stripBinaries == StripMode.SOMETIMES)
&& (compilationMode == CompilationMode.FASTBUILD));
fullyStaticLinkFlags = new FlagList(
configureLinkerOptions(compilationMode, lipoMode, LinkingMode.FULLY_STATIC,
ldExecutable, stripBinaries),
convertOptionalOptions(toolchain.getOptionalLinkerFlagList()),
ImmutableList.<String>of());
mostlyStaticLinkFlags = new FlagList(
configureLinkerOptions(compilationMode, lipoMode, LinkingMode.MOSTLY_STATIC,
ldExecutable, stripBinaries),
convertOptionalOptions(toolchain.getOptionalLinkerFlagList()),
ImmutableList.<String>of());
mostlyStaticSharedLinkFlags = new FlagList(
configureLinkerOptions(compilationMode, lipoMode,
LinkingMode.MOSTLY_STATIC_LIBRARIES, ldExecutable, stripBinaries),
convertOptionalOptions(toolchain.getOptionalLinkerFlagList()),
ImmutableList.<String>of());
dynamicLinkFlags = new FlagList(
configureLinkerOptions(compilationMode, lipoMode, LinkingMode.DYNAMIC,
ldExecutable, stripBinaries),
convertOptionalOptions(toolchain.getOptionalLinkerFlagList()),
ImmutableList.<String>of());
testOnlyLinkFlags = ImmutableList.copyOf(toolchain.getTestOnlyLinkerFlagList());
Map<String, String> makeVariablesBuilder = new HashMap<>();
// The following are to be used to allow some build rules to avoid the limits on stack frame
// sizes and variable-length arrays. Ensure that these are always set.
makeVariablesBuilder.put("STACK_FRAME_UNLIMITED", "");
makeVariablesBuilder.put("CC_FLAGS", "");
for (CrosstoolConfig.MakeVariable variable : toolchain.getMakeVariableList()) {
makeVariablesBuilder.put(variable.getName(), variable.getValue());
}
if (sysrootFlag != null) {
String ccFlags = makeVariablesBuilder.get("CC_FLAGS");
ccFlags = ccFlags.isEmpty() ? sysrootFlag : ccFlags + " " + sysrootFlag;
makeVariablesBuilder.put("CC_FLAGS", ccFlags);
}
this.additionalMakeVariables = ImmutableMap.copyOf(makeVariablesBuilder);
}
private ImmutableList<OptionalFlag> convertOptionalOptions(
List<CrosstoolConfig.CToolchain.OptionalFlag> optionalFlagList)
throws IllegalArgumentException {
ImmutableList.Builder<OptionalFlag> result = ImmutableList.builder();
for (CrosstoolConfig.CToolchain.OptionalFlag crosstoolOptionalFlag : optionalFlagList) {
String name = crosstoolOptionalFlag.getDefaultSettingName();
result.add(new OptionalFlag(
name,
ImmutableList.copyOf(crosstoolOptionalFlag.getFlagList())));
}
return result.build();
}
private boolean linkActionsAreConfigured(CToolchain toolchain) {
for (LinkTargetType type : Link.MANDATORY_LINK_TARGET_TYPES) {
boolean typeIsConfigured = false;
for (ActionConfig actionConfig : toolchain.getActionConfigList()) {
if (actionConfig.getActionName().equals(type.getActionName())) {
typeIsConfigured = true;
break;
}
}
if (!typeIsConfigured) {
return false;
}
}
return true;
}
// TODO(bazel-team): Remove this once bazel supports all crosstool flags through
// feature configuration, and all crosstools have been converted.
private CToolchain addLegacyFeatures(CToolchain toolchain) {
CToolchain.Builder toolchainBuilder = CToolchain.newBuilder();
Set<ArtifactCategory> definedCategories = new HashSet<>();
for (ArtifactNamePattern pattern : toolchainBuilder.getArtifactNamePatternList()) {
try {
definedCategories.add(ArtifactCategory.valueOf(pattern.getCategoryName().toUpperCase()));
} catch (IllegalArgumentException e) {
// Invalid category name, will be detected later.
continue;
}
}
for (ArtifactCategory category : ArtifactCategory.values()) {
if (!definedCategories.contains(category) && category.getDefaultPattern() != null) {
toolchainBuilder.addArtifactNamePattern(ArtifactNamePattern.newBuilder()
.setCategoryName(category.toString().toLowerCase())
.setPattern(category.getDefaultPattern())
.build());
}
}
ImmutableSet.Builder<String> featuresBuilder = ImmutableSet.builder();
for (CToolchain.Feature feature : toolchain.getFeatureList()) {
featuresBuilder.add(feature.getName());
}
Set<String> features = featuresBuilder.build();
if (!features.contains(CppRuleClasses.NO_LEGACY_FEATURES)) {
try {
if (!linkActionsAreConfigured(toolchain)) {
String linkerToolPath = "DUMMY_LINKER_TOOL";
for (ToolPath tool : toolchain.getToolPathList()) {
if (tool.getName().equals(Tool.GCC.getNamePart())) {
linkerToolPath =
crosstoolTopPathFragment
.getRelative(new PathFragment(tool.getPath()))
.getPathString();
}
}
if (getTargetLibc().equals("macosx")) {
TextFormat.merge(
CppLinkActionConfigs.getCppLinkActionConfigs(
CppLinkPlatform.MAC, features, linkerToolPath),
toolchainBuilder);
} else {
TextFormat.merge(
CppLinkActionConfigs.getCppLinkActionConfigs(
CppLinkPlatform.LINUX, features, linkerToolPath),
toolchainBuilder);
}
}
if (!features.contains("dependency_file")) {
// Gcc options:
// -MD turns on .d file output as a side-effect (doesn't imply -E)
// -MM[D] enables user includes only, not system includes
// -MF <name> specifies the dotd file name
// Issues:
// -M[M] alone subverts actual .o output (implies -E)
// -M[M]D alone breaks some of the .d naming assumptions
// This combination gets user and system includes with specified name:
// -MD -MF <name>
TextFormat.merge(
""
+ "feature {"
+ " name: 'dependency_file'"
+ " flag_set {"
+ " action: 'assemble'"
+ " action: 'preprocess-assemble'"
+ " action: 'c-compile'"
+ " action: 'c++-compile'"
+ " action: 'c++-module-compile'"
+ " action: 'objc-compile'"
+ " action: 'objc++-compile'"
+ " action: 'c++-header-preprocessing'"
+ " action: 'c++-header-parsing'"
+ " expand_if_all_available: 'dependency_file'"
+ " flag_group {"
+ " flag: '-MD'"
+ " flag: '-MF'"
+ " flag: '%{dependency_file}'"
+ " }"
+ " }"
+ "}",
toolchainBuilder);
}
if (!features.contains("random_seed")) {
// GCC and Clang give randomized names to symbols which are defined in
// an anonymous namespace but have external linkage. To make
// computation of these deterministic, we want to override the
// default seed for the random number generator. It's safe to use
// any value which differs for all translation units; we use the
// path to the object file.
TextFormat.merge(
""
+ "feature {"
+ " name: 'random_seed'"
+ " flag_set {"
+ " action: 'c++-compile'"
+ " action: 'c++-module-compile'"
+ " flag_group {"
+ " flag: '-frandom-seed=%{output_file}'"
+ " }"
+ " }"
+ "}",
toolchainBuilder);
}
if (!features.contains("pic")) {
TextFormat.merge(
""
+ "feature {"
+ " name: 'pic'"
+ " flag_set {"
+ " action: 'c-compile'"
+ " action: 'c++-compile'"
+ " action: 'c++-module-compile'"
+ " action: 'preprocess-assemble'"
+ " expand_if_all_available: 'pic'"
+ " flag_group {"
+ " flag: '-fPIC'"
+ " }"
+ " }"
+ "}",
toolchainBuilder);
}
if (!features.contains("per_object_debug_info")) {
TextFormat.merge(
""
+ "feature {"
+ " name: 'per_object_debug_info'"
+ " flag_set {"
+ " action: 'c-compile'"
+ " action: 'c++-compile'"
+ " action: 'assemble'"
+ " action: 'preprocess-assemble'"
+ " expand_if_all_available: 'per_object_debug_info_file'"
+ " flag_group {"
+ " flag: '-gsplit-dwarf'"
+ " }"
+ " }"
+ "}",
toolchainBuilder);
}
if (!features.contains("preprocessor_defines")) {
TextFormat.merge(
""
+ "feature {"
+ " name: 'preprocessor_defines'"
+ " flag_set {"
+ " action: 'preprocess-assemble'"
+ " action: 'c-compile'"
+ " action: 'c++-compile'"
+ " action: 'c++-header-parsing'"
+ " action: 'c++-header-preprocessing'"
+ " action: 'c++-module-compile'"
+ " action: 'clif-match'"
+ " flag_group {"
+ " flag: '-D%{preprocessor_defines}'"
+ " }"
+ " }"
+ "}",
toolchainBuilder);
}
if (!features.contains("include_paths")) {
TextFormat.merge(
""
+ "feature {"
+ " name: 'include_paths'"
+ " flag_set {"
+ " action: 'preprocess-assemble'"
+ " action: 'c-compile'"
+ " action: 'c++-compile'"
+ " action: 'c++-header-parsing'"
+ " action: 'c++-header-preprocessing'"
+ " action: 'c++-module-compile'"
+ " action: 'clif-match'"
+ " action: 'objc-compile'"
+ " action: 'objc++-compile'"
+ " flag_group {"
+ " flag: '-iquote'"
+ " flag: '%{quote_include_paths}'"
+ " }"
+ " flag_group {"
+ " flag: '-I%{include_paths}'"
+ " }"
+ " flag_group {"
+ " flag: '-isystem'"
+ " flag: '%{system_include_paths}'"
+ " }"
+ " }"
+ "}",
toolchainBuilder);
}
if (!features.contains("fdo_instrument")) {
TextFormat.merge(
""
+ "feature {"
+ " name: 'fdo_instrument'"
+ " provides: 'profile'"
+ " flag_set {"
+ " action: 'c-compile'"
+ " action: 'c++-compile'"
+ " action: 'c++-link-interface-dynamic-library'"
+ " action: 'c++-link-dynamic-library'"
+ " action: 'c++-link-executable'"
+ " flag_group {"
+ " flag: '-fprofile-generate=%{fdo_instrument_path}'"
+ " flag: '-fno-data-sections'"
+ " }"
+ " }"
+ "}",
toolchainBuilder);
}
if (!features.contains("fdo_optimize")) {
TextFormat.merge(
""
+ "feature {"
+ " name: 'fdo_optimize'"
+ " provides: 'profile'"
+ " flag_set {"
+ " action: 'c-compile'"
+ " action: 'c++-compile'"
+ " expand_if_all_available: 'fdo_profile_path'"
+ " flag_group {"
+ " flag: '-fprofile-use=%{fdo_profile_path}'"
+ " flag: '-Xclang-only=-Wno-profile-instr-unprofiled'"
+ " flag: '-Xclang-only=-Wno-profile-instr-out-of-date'"
+ " flag: '-fprofile-correction'"
+ " }"
+ " }"
+ "}",
toolchainBuilder);
}
if (!features.contains("autofdo")) {
TextFormat.merge(
""
+ "feature {"
+ " name: 'autofdo'"
+ " provides: 'profile'"
+ " flag_set {"
+ " action: 'c-compile'"
+ " action: 'c++-compile'"
+ " expand_if_all_available: 'fdo_profile_path'"
+ " flag_group {"
+ " flag: '-fauto-profile=%{fdo_profile_path}'"
+ " flag: '-fprofile-correction'"
+ " }"
+ " }"
+ "}",
toolchainBuilder);
}
if (!features.contains("lipo")) {
TextFormat.merge(
""
+ "feature {"
+ " name: 'lipo'"
+ " requires { feature: 'autofdo' }"
+ " requires { feature: 'fdo_optimize' }"
+ " requires { feature: 'fdo_instrument' }"
+ " flag_set {"
+ " action: 'c-compile'"
+ " action: 'c++-compile'"
+ " flag_group {"
+ " flag: '-fripa'"
+ " }"
+ " }"
+ "}",
toolchainBuilder);
}
if (!features.contains("coverage")) {
String compileFlags;
String linkerFlags;
if (useLLVMCoverageMap) {
compileFlags =
"flag_group {"
+ " flag: '-fprofile-instr-generate'"
+ " flag: '-fcoverage-mapping'"
+ "}";
linkerFlags = " flag_group {" + " flag: '-fprofile-instr-generate'" + "}";
} else {
compileFlags =
" expand_if_all_available: 'gcov_gcno_file'"
+ "flag_group {"
+ " flag: '-fprofile-arcs'"
+ " flag: '-ftest-coverage'"
+ "}";
linkerFlags = " flag_group {" + " flag: '-lgcov'" + "}";
}
TextFormat.merge(
""
+ "feature {"
+ " name: 'coverage'"
+ " provides: 'profile'"
+ " flag_set {"
+ " action: 'preprocess-assemble'"
+ " action: 'c-compile'"
+ " action: 'c++-compile'"
+ " action: 'c++-header-parsing'"
+ " action: 'c++-header-preprocessing'"
+ " action: 'c++-module-compile'"
+ compileFlags
+ " }"
+ " flag_set {"
+ " action: 'c++-link-interface-dynamic-library'"
+ " action: 'c++-link-dynamic-library'"
+ " action: 'c++-link-executable'"
+ linkerFlags
+ " }"
+ "}",
toolchainBuilder);
}
} catch (ParseException e) {
// Can only happen if we change the proto definition without changing our
// configuration above.
throw new RuntimeException(e);
}
}
toolchainBuilder.mergeFrom(toolchain);
return toolchainBuilder.build();
}
private static ImmutableList<String> copyOrDefaultIfEmpty(List<String> list,
String defaultValue) {
return list.isEmpty() ? ImmutableList.of(defaultValue) : ImmutableList.copyOf(list);
}
@VisibleForTesting
static CompilationMode importCompilationMode(CrosstoolConfig.CompilationMode mode) {
return CompilationMode.valueOf(mode.name());
}
@VisibleForTesting
static LinkingMode importLinkingMode(CrosstoolConfig.LinkingMode mode) {
return LinkingMode.valueOf(mode.name());
}
private static final String SYSROOT_START = "%sysroot%/";
private static final String WORKSPACE_START = "%workspace%/";
private static final String CROSSTOOL_START = "%crosstool_top%/";
private static final String PACKAGE_START = "%package(";
private static final String PACKAGE_END = ")%";
/**
* Resolve the given include directory.
*
* <p>If it starts with %sysroot%/, that part is replaced with the actual sysroot.
*
* <p>If it starts with %workspace%/, that part is replaced with the empty string
* (essentially making it relative to the build directory).
*
* <p>If it starts with %crosstool_top%/ or is any relative path, it is
* interpreted relative to the crosstool top. The use of assumed-crosstool-relative
* specifications is considered deprecated, and all such uses should eventually
* be replaced by "%crosstool_top%/".
*
* <p>If it is of the form %package(@repository//my/package)%/folder, then it is
* interpreted as the named folder in the appropriate package. All of the normal
* package syntax is supported. The /folder part is optional.
*
* <p>It is illegal if it starts with a % and does not match any of the above
* forms to avoid accidentally silently ignoring misspelled prefixes.
*
* <p>If it is absolute, it remains unchanged.
*/
static PathFragment resolveIncludeDir(String s, PathFragment sysroot,
PathFragment crosstoolTopPathFragment) throws InvalidConfigurationException {
PathFragment pathPrefix;
String pathString;
int packageEndIndex = s.indexOf(PACKAGE_END);
if (packageEndIndex != -1 && s.startsWith(PACKAGE_START)) {
String packageString = s.substring(PACKAGE_START.length(), packageEndIndex);
try {
pathPrefix = PackageIdentifier.parse(packageString).getSourceRoot();
} catch (LabelSyntaxException e) {
throw new InvalidConfigurationException("The package '" + packageString + "' is not valid");
}
int pathStartIndex = packageEndIndex + PACKAGE_END.length();
if (pathStartIndex + 1 < s.length()) {
if (s.charAt(pathStartIndex) != '/') {
throw new InvalidConfigurationException(
"The path in the package for '" + s + "' is not valid");
}
pathString = s.substring(pathStartIndex + 1, s.length());
} else {
pathString = "";
}
} else if (s.startsWith(SYSROOT_START)) {
if (sysroot == null) {
throw new InvalidConfigurationException("A %sysroot% prefix is only allowed if the "
+ "default_sysroot option is set");
}
pathPrefix = sysroot;
pathString = s.substring(SYSROOT_START.length(), s.length());
} else if (s.startsWith(WORKSPACE_START)) {
pathPrefix = PathFragment.EMPTY_FRAGMENT;
pathString = s.substring(WORKSPACE_START.length(), s.length());
} else {
pathPrefix = crosstoolTopPathFragment;
if (s.startsWith(CROSSTOOL_START)) {
pathString = s.substring(CROSSTOOL_START.length(), s.length());
} else if (s.startsWith("%")) {
throw new InvalidConfigurationException(
"The include path '" + s + "' has an " + "unrecognized %prefix%");
} else {
pathString = s;
}
}
PathFragment path = new PathFragment(pathString);
if (!path.isNormalized()) {
throw new InvalidConfigurationException("The include path '" + s + "' is not normalized.");
}
return pathPrefix.getRelative(path);
}
@VisibleForTesting
ImmutableList<String> configureLinkerOptions(
CompilationMode compilationMode, LipoMode lipoMode, LinkingMode linkingMode,
PathFragment ldExecutable, boolean stripBinaries) {
List<String> result = new ArrayList<>();
result.addAll(commonLinkOptions);
if (stripBinaries) {
result.add("-Wl,-S");
}
result.addAll(linkOptionsFromCompilationMode.get(compilationMode));
result.addAll(linkOptionsFromLipoMode.get(lipoMode));
result.addAll(linkOptionsFromLinkingMode.get(linkingMode));
return ImmutableList.copyOf(result);
}
/**
* Returns the toolchain identifier, which uniquely identifies the compiler
* version, target libc version, target cpu, and LIPO linkage.
*/
public String getToolchainIdentifier() {
return toolchainIdentifier;
}
/**
* Returns the path of the crosstool.
*/
public PathFragment getCrosstoolTopPathFragment() {
return crosstoolTopPathFragment;
}
/**
* Returns the system name which is required by the toolchain to run.
*/
public String getHostSystemName() {
return hostSystemName;
}
@Override
public String toString() {
return toolchainIdentifier;
}
/**
* Returns the compiler version string (e.g. "gcc-4.1.1").
*/
@SkylarkCallable(name = "compiler", structField = true, doc = "C++ compiler.")
public String getCompiler() {
return compiler;
}
/**
* Returns the libc version string (e.g. "glibc-2.2.2").
*/
@SkylarkCallable(name = "libc", structField = true, doc = "libc version string.")
public String getTargetLibc() {
return targetLibc;
}
/**
* Returns the target architecture using blaze-specific constants (e.g. "piii").
*/
@SkylarkCallable(name = "cpu", structField = true, doc = "Target CPU of the C++ toolchain.")
public String getTargetCpu() {
return targetCpu;
}
/**
* Returns the path fragment that is either absolute or relative to the
* execution root that can be used to execute the given tool.
*
* <p>Note that you must not use this method to get the linker location, but
* use {@link #getLdExecutable} instead!
*/
public PathFragment getToolPathFragment(CppConfiguration.Tool tool) {
return toolPaths.get(tool.getNamePart());
}
/**
* Returns a label that forms a dependency to the files required for the
* sysroot that is used.
*/
public Label getLibcLabel() {
return libcLabel;
}
/**
* Returns a label that references the library files needed to statically
* link the C++ runtime (i.e. libgcc.a, libgcc_eh.a, libstdc++.a) for the
* target architecture.
*/
public Label getStaticRuntimeLibsLabel() {
return supportsEmbeddedRuntimes() ? staticRuntimeLibsLabel : null;
}
/**
* Returns a label that references the library files needed to dynamically
* link the C++ runtime (i.e. libgcc_s.so, libstdc++.so) for the target
* architecture.
*/
public Label getDynamicRuntimeLibsLabel() {
return supportsEmbeddedRuntimes() ? dynamicRuntimeLibsLabel : null;
}
/**
* Returns the label of the <code>cc_compiler</code> rule for the C++ configuration.
*/
public Label getCcToolchainRuleLabel() {
return ccToolchainLabel;
}
/**
* Returns the abi we're using, which is a gcc version. E.g.: "gcc-3.4".
* Note that in practice we might be using gcc-3.4 as ABI even when compiling
* with gcc-4.1.0, because ABIs are backwards compatible.
*/
// TODO(bazel-team): The javadoc should clarify how this is used in Blaze.
public String getAbi() {
return abi;
}
/**
* Returns the glibc version used by the abi we're using. This is a
* glibc version number (e.g., "2.2.2"). Note that in practice we
* might be using glibc 2.2.2 as ABI even when compiling with
* gcc-4.2.2, gcc-4.3.1, or gcc-4.4.0 (which use glibc 2.3.6),
* because ABIs are backwards compatible.
*/
// TODO(bazel-team): The javadoc should clarify how this is used in Blaze.
public String getAbiGlibcVersion() {
return abiGlibcVersion;
}
/**
* Returns the configured features of the toolchain. Rules should not call this directly, but
* instead use {@code CcToolchainProvider.getFeatures}.
*/
public CcToolchainFeatures getFeatures() {
return toolchainFeatures;
}
/**
* Returns the configured current compilation mode. Rules should not call this directly, but
* instead use {@code CcToolchainProvider.getCompilationMode}.
*/
public CompilationMode getCompilationMode() {
return compilationMode;
}
/**
* Returns whether the toolchain supports the gold linker.
*/
public boolean supportsGoldLinker() {
return supportsGoldLinker;
}
/**
* Returns whether the toolchain supports the --start-lib/--end-lib options.
*/
public boolean supportsStartEndLib() {
return supportsStartEndLib;
}
/**
* Returns whether the toolchain supports dynamic linking.
*/
public boolean supportsDynamicLinker() {
return supportsDynamicLinker;
}
/**
* Returns whether build_interface_so can build interface shared objects for this toolchain.
* Should be true if this toolchain generates ELF objects.
*/
public boolean supportsInterfaceSharedObjects() {
return supportsInterfaceSharedObjects;
}
/**
* Returns whether the toolchain supports linking C/C++ runtime libraries
* supplied inside the toolchain distribution.
*/
public boolean supportsEmbeddedRuntimes() {
return supportsEmbeddedRuntimes;
}
/**
* Returns whether the toolchain supports EXEC_ORIGIN libraries resolution.
*/
public boolean supportsExecOrigin() {
// We're rolling out support for this in the same release that also supports embedded runtimes.
return supportsEmbeddedRuntimes;
}
/**
* Returns whether the toolchain supports "Fission" C++ builds, i.e. builds
* where compilation partitions object code and debug symbols into separate
* output files.
*/
public boolean supportsFission() {
return supportsFission;
}
/**
* Returns whether shared libraries must be compiled with position
* independent code on this platform.
*/
public boolean toolchainNeedsPic() {
return toolchainNeedsPic;
}
/**
* Returns whether binaries must be compiled with position independent code.
*/
public boolean usePicForBinaries() {
return usePicForBinaries;
}
/**
* Returns the type of archives being used.
*/
public Link.ArchiveType archiveType() {
return useStartEndLib() ? Link.ArchiveType.START_END_LIB : Link.ArchiveType.REGULAR;
}
/**
* Returns the ar flags to be used.
*/
public ImmutableList<String> getArFlags() {
return arOptions;
}
/**
* Returns the built-in list of system include paths for the toolchain
* compiler. All paths in this list should be relative to the exec directory.
* They may be absolute if they are also installed on the remote build nodes or
* for local compilation.
*/
@SkylarkCallable(name = "built_in_include_directories", structField = true,
doc = "Built-in system include paths for the toolchain compiler. All paths in this list"
+ " should be relative to the exec directory. They may be absolute if they are also installed"
+ " on the remote build nodes or for local compilation.")
public List<PathFragment> getBuiltInIncludeDirectories() {
return builtInIncludeDirectories;
}
/**
* Returns the sysroot to be used. If the toolchain compiler does not support
* different sysroots, or the sysroot is the same as the default sysroot, then
* this method returns <code>null</code>.
*/
@SkylarkCallable(name = "sysroot", structField = true,
doc = "Returns the sysroot to be used. If the toolchain compiler does not support "
+ "different sysroots, or the sysroot is the same as the default sysroot, then "
+ "this method returns <code>None</code>.")
public PathFragment getSysroot() {
return sysroot;
}
/**
* Returns the run time sysroot, which is where the dynamic linker
* and system libraries are found at runtime. This is usually an absolute path. If the
* toolchain compiler does not support sysroots, then this method returns <code>null</code>.
*/
public PathFragment getRuntimeSysroot() {
return runtimeSysroot;
}
/**
* Returns the default options to use for compiling C, C++, and assembler.
* This is just the options that should be used for all three languages.
* There may be additional C-specific or C++-specific options that should be used,
* in addition to the ones returned by this method.
*/
@SkylarkCallable(
name = "compiler_options",
doc =
"Returns the default options to use for compiling C, C++, and assembler. "
+ "This is just the options that should be used for all three languages. "
+ "There may be additional C-specific or C++-specific options that should be used, "
+ "in addition to the ones returned by this method"
)
public ImmutableList<String> getCompilerOptions(Iterable<String> features) {
return compilerFlags.evaluate(features);
}
/**
* Returns the list of additional C-specific options to use for compiling
* C. These should be go on the command line after the common options
* returned by {@link #getCompilerOptions}.
*/
@SkylarkCallable(name = "c_options", structField = true,
doc = "Returns the list of additional C-specific options to use for compiling C. "
+ "These should be go on the command line after the common options returned by "
+ "<code>compiler_options</code>")
public ImmutableList<String> getCOptions() {
return cOptions;
}
/**
* Returns the list of additional C++-specific options to use for compiling
* C++. These should be go on the command line after the common options
* returned by {@link #getCompilerOptions}.
*/
@SkylarkCallable(
name = "cxx_options",
doc =
"Returns the list of additional C++-specific options to use for compiling C++. "
+ "These should be go on the command line after the common options returned by "
+ "<code>compiler_options</code>"
)
public ImmutableList<String> getCxxOptions(Iterable<String> features) {
return cxxFlags.evaluate(features);
}
/**
* Returns the default list of options which cannot be filtered by BUILD
* rules. These should be appended to the command line after filtering.
*/
@SkylarkCallable(
name = "unfiltered_compiler_options",
doc =
"Returns the default list of options which cannot be filtered by BUILD "
+ "rules. These should be appended to the command line after filtering."
)
public ImmutableList<String> getUnfilteredCompilerOptions(Iterable<String> features) {
return unfilteredCompilerFlags.evaluate(features);
}
/**
* Returns the set of command-line linker options, including any flags
* inferred from the command-line options.
*
* @see Link
*/
// TODO(bazel-team): Clean up the linker options computation!
@SkylarkCallable(name = "link_options", structField = true,
doc = "Returns the set of command-line linker options, including any flags "
+ "inferred from the command-line options.")
public ImmutableList<String> getLinkOptions() {
return linkOptions;
}
/** Returns the set of command-line LTO indexing options. */
public ImmutableList<String> getLTOIndexOptions() {
return ltoindexOptions;
}
/**
* Returns the immutable list of linker options for fully statically linked
* outputs. Does not include command-line options passed via --linkopt or
* --linkopts.
*
* @param features default settings affecting this link
* @param sharedLib true if the output is a shared lib, false if it's an executable
*/
@SkylarkCallable(
name = "fully_static_link_options",
doc =
"Returns the immutable list of linker options for fully statically linked "
+ "outputs. Does not include command-line options passed via --linkopt or "
+ "--linkopts."
)
public ImmutableList<String> getFullyStaticLinkOptions(Iterable<String> features,
Boolean sharedLib) {
if (sharedLib) {
return getSharedLibraryLinkOptions(mostlyStaticLinkFlags, features);
} else {
return fullyStaticLinkFlags.evaluate(features);
}
}
/**
* Returns the immutable list of linker options for mostly statically linked
* outputs. Does not include command-line options passed via --linkopt or
* --linkopts.
*
* @param features default settings affecting this link
* @param sharedLib true if the output is a shared lib, false if it's an executable
*/
@SkylarkCallable(
name = "mostly_static_link_options",
doc =
"Returns the immutable list of linker options for mostly statically linked "
+ "outputs. Does not include command-line options passed via --linkopt or "
+ "--linkopts."
)
public ImmutableList<String> getMostlyStaticLinkOptions(Iterable<String> features,
Boolean sharedLib) {
if (sharedLib) {
return getSharedLibraryLinkOptions(
supportsEmbeddedRuntimes ? mostlyStaticSharedLinkFlags : dynamicLinkFlags,
features);
} else {
return mostlyStaticLinkFlags.evaluate(features);
}
}
/**
* Returns the immutable list of linker options for artifacts that are not
* fully or mostly statically linked. Does not include command-line options
* passed via --linkopt or --linkopts.
*
* @param features default settings affecting this link
* @param sharedLib true if the output is a shared lib, false if it's an executable
*/
@SkylarkCallable(
name = "dynamic_link_options",
doc =
"Returns the immutable list of linker options for artifacts that are not "
+ "fully or mostly statically linked. Does not include command-line options "
+ "passed via --linkopt or --linkopts."
)
public ImmutableList<String> getDynamicLinkOptions(Iterable<String> features, Boolean sharedLib) {
if (sharedLib) {
return getSharedLibraryLinkOptions(dynamicLinkFlags, features);
} else {
return dynamicLinkFlags.evaluate(features);
}
}
/**
* Returns link options for the specified flag list, combined with universal options
* for all shared libraries (regardless of link staticness).
*/
private ImmutableList<String> getSharedLibraryLinkOptions(FlagList flags,
Iterable<String> features) {
return ImmutableList.<String>builder()
.addAll(flags.evaluate(features))
.addAll(dynamicLibraryLinkFlags.evaluate(features))
.build();
}
/**
* Returns test-only link options such that certain test-specific features can be configured
* separately (e.g. lazy binding).
*/
public ImmutableList<String> getTestOnlyLinkOptions() {
return testOnlyLinkFlags;
}
/**
* Returns the list of options to be used with 'objcopy' when converting
* binary files to object files, or {@code null} if this operation is not
* supported.
*/
public ImmutableList<String> getObjCopyOptionsForEmbedding() {
return objcopyOptions;
}
/**
* Returns the list of options to be used with 'ld' when converting
* binary files to object files, or {@code null} if this operation is not
* supported.
*/
public ImmutableList<String> getLdOptionsForEmbedding() {
return ldOptions;
}
/**
* Returns a map of additional make variables for use by {@link
* BuildConfiguration}. These are to used to allow some build rules to
* avoid the limits on stack frame sizes and variable-length arrays.
*
* <p>The returned map must contain an entry for {@code STACK_FRAME_UNLIMITED},
* though the entry may be an empty string.
*/
@VisibleForTesting
public ImmutableMap<String, String> getAdditionalMakeVariables() {
return additionalMakeVariables;
}
/**
* Returns the execution path to the linker binary to use for this build.
* Relative paths are relative to the execution root.
*/
public PathFragment getLdExecutable() {
return ldExecutable;
}
/**
* Returns the dynamic linking mode (full, off, or default).
*/
public DynamicMode getDynamicMode() {
return dynamicMode;
}
/*
* If true then the directory name for non-LIPO targets will have a '-lipodata' suffix in
* AutoFDO mode.
*/
public boolean getAutoFdoLipoData() {
return cppOptions.autoFdoLipoData;
}
/**
* Returns the STL label if given on the command line. {@code null}
* otherwise.
*/
public Label getStl() {
return cppOptions.stl;
}
/**
* Returns the currently active LIPO compilation mode.
*/
public LipoMode getLipoMode() {
return cppOptions.lipoMode;
}
public boolean isFdo() {
return cppOptions.isFdo();
}
public boolean isLipoOptimization() {
// The LIPO optimization bits are set in the LIPO context collector configuration, too.
return cppOptions.isLipoOptimization() && !isLipoContextCollector();
}
public boolean isLipoOptimizationOrInstrumentation() {
return cppOptions.isLipoOptimizationOrInstrumentation() && !isLipoContextCollector();
}
/**
* Returns true if it is AutoFDO LIPO build.
*/
public boolean isAutoFdoLipo() {
return cppOptions.fdoOptimize != null
&& CppFileTypes.GCC_AUTO_PROFILE.matches(cppOptions.fdoOptimize)
&& getLipoMode() != LipoMode.OFF;
}
/**
* Returns whether or not to strip the binaries.
*/
public boolean shouldStripBinaries() {
return stripBinaries;
}
/**
* Returns the additional options to pass to strip when generating a
* {@code <name>.stripped} binary by this build.
*/
public ImmutableList<String> getStripOpts() {
return ImmutableList.copyOf(cppOptions.stripoptList);
}
/**
* Returns whether temporary outputs from gcc will be saved.
*/
public boolean getSaveTemps() {
return cppOptions.saveTemps;
}
/**
* Returns the {@link PerLabelOptions} to apply to the gcc command line, if
* the label of the compiled file matches the regular expression.
*/
public ImmutableList<PerLabelOptions> getPerFileCopts() {
return ImmutableList.copyOf(cppOptions.perFileCopts);
}
public Label getLipoContextLabel() {
return cppOptions.getLipoContextLabel();
}
/**
* Returns the custom malloc library label.
*/
public Label customMalloc() {
return cppOptions.customMalloc;
}
/**
* Returns true if mostly-static C++ binaries should be skipped.
*/
public boolean skipStaticOutputs() {
return cppOptions.skipStaticOutputs;
}
/**
* Returns whether we are processing headers in dependencies of built C++ targets.
*/
public boolean processHeadersInDependencies() {
return cppOptions.processHeadersInDependencies;
}
/**
* Returns true if Fission is specified for this build and supported by the crosstool.
*/
public boolean useFission() {
return cppOptions.fissionModes.contains(compilationMode) && supportsFission();
}
/**
* Returns true if Fission is enabled for this build and the user requested automatic building
* of .dwp files for C++ test targets.
*/
public boolean shouldBuildTestDwp() {
return useFission() && cppOptions.buildTestDwp;
}
/**
* Returns true if all C++ compilations should produce position-independent code, links should
* produce position-independent executables, and dependencies with equivalent pre-built pic and
* nopic versions should apply the pic versions. Returns false if default settings should be
* applied (i.e. make no special provisions for pic code).
*/
public boolean forcePic() {
return cppOptions.forcePic;
}
public boolean useStartEndLib() {
return cppOptions.useStartEndLib && supportsStartEndLib();
}
/**
* Returns true if interface shared objects should be used.
*/
public boolean useInterfaceSharedObjects() {
return supportsInterfaceSharedObjects() && cppOptions.useInterfaceSharedObjects;
}
public boolean forceIgnoreDashStatic() {
return cppOptions.forceIgnoreDashStatic;
}
/**
* Returns true if shared libraries must be compiled with position independent code
* on this platform or in this configuration.
*/
public boolean needsPic() {
return forcePic() || toolchainNeedsPic();
}
/**
* Returns true iff we should use ".pic.o" files when linking executables.
*/
public boolean usePicObjectsForBinaries() {
return forcePic() || usePicForBinaries();
}
public boolean legacyWholeArchive() {
return cppOptions.legacyWholeArchive;
}
public boolean getSymbolCounts() {
return cppOptions.symbolCounts;
}
public boolean getInmemoryDotdFiles() {
return cppOptions.inmemoryDotdFiles;
}
public boolean getSkipUnusedModules() {
return cppOptions.skipUnusedModules;
}
public boolean getPruneMoreModules() {
return cppOptions.pruneMoreModules;
}
public LibcTop getLibcTop() {
return cppOptions.libcTop;
}
public boolean getUseInterfaceSharedObjects() {
return cppOptions.useInterfaceSharedObjects;
}
/**
* Return the name of the directory (relative to the bin directory) that
* holds mangled links to shared libraries. This name is always set to
* the '{@code _solib_<cpu_archictecture_name>}.
*/
public String getSolibDirectory() {
return solibDirectory;
}
/**
* Returns the path to the GNU binutils 'objcopy' binary to use for this
* build. (Corresponds to $(OBJCOPY) in make-dbg.) Relative paths are
* relative to the execution root.
*/
@SkylarkCallable(name = "objcopy_executable", structField = true,
doc = "Path to GNU binutils 'objcopy' binary")
public PathFragment getObjCopyExecutable() {
return getToolPathFragment(CppConfiguration.Tool.OBJCOPY);
}
/**
* Returns the path to the GNU binutils 'gcc' binary that should be used
* by this build. This binary should support compilation of both C (*.c)
* and C++ (*.cc) files. Relative paths are relative to the execution root.
*/
@SkylarkCallable(name = "compiler_executable", structField = true,
doc = "Path to C/C++ compiler binary")
public PathFragment getCppExecutable() {
return getToolPathFragment(CppConfiguration.Tool.GCC);
}
/**
* Returns the path to the GNU binutils 'g++' binary that should be used
* by this build. This binary should support linking of both C (*.c)
* and C++ (*.cc) files. Relative paths are relative to the execution root.
*/
public PathFragment getCppLinkExecutable() {
return getToolPathFragment(CppConfiguration.Tool.GCC);
}
/**
* Returns the path to the GNU binutils 'cpp' binary that should be used
* by this build. Relative paths are relative to the execution root.
*/
@SkylarkCallable(name = "preprocessor_executable", structField = true,
doc = "Path to C/C++ preprocessor binary")
public PathFragment getCpreprocessorExecutable() {
return getToolPathFragment(CppConfiguration.Tool.CPP);
}
/**
* Returns the path to the GNU binutils 'gcov' binary that should be used
* by this build to analyze C++ coverage data. Relative paths are relative to
* the execution root.
*/
public PathFragment getGcovExecutable() {
return getToolPathFragment(CppConfiguration.Tool.GCOV);
}
/**
* Returns the path to the 'gcov-tool' executable that should be used
* by this build. Relative paths are relative to the execution root.
*/
public PathFragment getGcovToolExecutable() {
return getToolPathFragment(CppConfiguration.Tool.GCOVTOOL);
}
/**
* Returns the path to the GNU binutils 'nm' executable that should be used
* by this build. Used only for testing. Relative paths are relative to the
* execution root.
*/
@SkylarkCallable(name = "nm_executable", structField = true,
doc = "Path to GNU binutils 'nm' binary")
public PathFragment getNmExecutable() {
return getToolPathFragment(CppConfiguration.Tool.NM);
}
/**
* Returns the path to the GNU binutils 'objdump' executable that should be
* used by this build. Used only for testing. Relative paths are relative to
* the execution root.
*/
@SkylarkCallable(name = "objdump_executable", structField = true,
doc = "Path to GNU binutils 'objdump' binary")
public PathFragment getObjdumpExecutable() {
return getToolPathFragment(CppConfiguration.Tool.OBJDUMP);
}
/**
* Returns the path to the GNU binutils 'ar' binary to use for this build.
* Relative paths are relative to the execution root.
*/
@SkylarkCallable(name = "ar_executable", structField = true,
doc = "Path to GNU binutils 'ar' binary")
public PathFragment getArExecutable() {
return getToolPathFragment(CppConfiguration.Tool.AR);
}
/**
* Returns the path to the GNU binutils 'strip' executable that should be used
* by this build. Relative paths are relative to the execution root.
*/
@SkylarkCallable(name = "strip_executable", structField = true,
doc = "Path to GNU binutils 'strip' binary")
public PathFragment getStripExecutable() {
return getToolPathFragment(CppConfiguration.Tool.STRIP);
}
/**
* Returns the path to the GNU binutils 'dwp' binary that should be used by this
* build to combine debug info output from individual C++ compilations (i.e. .dwo
* files) into aggregate target-level debug packages. Relative paths are relative to the
* execution root. See https://gcc.gnu.org/wiki/DebugFission .
*/
public PathFragment getDwpExecutable() {
return getToolPathFragment(CppConfiguration.Tool.DWP);
}
/**
* Returns the GNU System Name
*/
@SkylarkCallable(name = "target_gnu_system_name", structField = true,
doc = "The GNU System Name.")
public String getTargetGnuSystemName() {
return targetSystemName;
}
/**
* Returns the architecture component of the GNU System Name
*/
public String getGnuSystemArch() {
if (targetSystemName.indexOf('-') == -1) {
return targetSystemName;
}
return targetSystemName.substring(0, targetSystemName.indexOf('-'));
}
/**
* Returns whether the configuration's purpose is only to collect LIPO-related data.
*/
public boolean isLipoContextCollector() {
return lipoContextCollector;
}
@Override
public void reportInvalidOptions(EventHandler reporter, BuildOptions buildOptions) {
CppOptions cppOptions = buildOptions.get(CppOptions.class);
if (stripBinaries) {
boolean warn = cppOptions.coptList.contains("-g");
for (PerLabelOptions opt : cppOptions.perFileCopts) {
warn |= opt.getOptions().contains("-g");
}
if (warn) {
reporter.handle(Event.warn("Stripping enabled, but '--copt=-g' (or --per_file_copt=...@-g) "
+ "specified. Debug information will be generated and then stripped away. This is "
+ "probably not what you want! Use '-c dbg' for debug mode, or use '--strip=never' "
+ "to disable stripping"));
}
}
if (cppOptions.fdoInstrument != null && cppOptions.fdoOptimize != null) {
reporter.handle(Event.error("Cannot instrument and optimize for FDO at the same time. "
+ "Remove one of the '--fdo_instrument' and '--fdo_optimize' options"));
}
if (cppOptions.lipoContext != null) {
if (cppOptions.lipoMode != LipoMode.BINARY || cppOptions.fdoOptimize == null) {
reporter.handle(Event.warn("The --lipo_context option can only be used together with "
+ "--fdo_optimize=<profile zip> and --lipo=binary. LIPO context will be ignored."));
}
} else {
if (cppOptions.lipoMode == LipoMode.BINARY && cppOptions.fdoOptimize != null) {
reporter.handle(Event.error("The --lipo_context option must be specified when using "
+ "--fdo_optimize=<profile zip> and --lipo=binary"));
}
}
if (cppOptions.lipoMode == LipoMode.BINARY && compilationMode != CompilationMode.OPT) {
reporter.handle(Event.error(
"'--lipo=binary' can only be used with '--compilation_mode=opt' (or '-c opt')"));
}
if (cppOptions.fissionModes.contains(compilationMode) && !supportsFission()) {
reporter.handle(
Event.warn(
"Fission is not supported by this crosstool. Please use a supporting "
+ "crosstool to enable fission"));
}
if (cppOptions.buildTestDwp && !useFission()) {
reporter.handle(Event.warn("Test dwp file requested, but Fission is not enabled. To "
+ "generate a dwp for the test executable, use '--fission=yes' with a toolchain "
+ "that supports Fission and build statically."));
}
}
@Override
public void addGlobalMakeVariables(Builder<String, String> globalMakeEnvBuilder) {
// hardcoded CC->gcc setting for unit tests
globalMakeEnvBuilder.put("CC", getCppExecutable().getPathString());
// Make variables provided by crosstool/gcc compiler suite.
globalMakeEnvBuilder.put("AR", getArExecutable().getPathString());
globalMakeEnvBuilder.put("NM", getNmExecutable().getPathString());
PathFragment objcopyTool = getObjCopyExecutable();
if (objcopyTool != null) {
// objcopy is optional in Crosstool
globalMakeEnvBuilder.put("OBJCOPY", objcopyTool.getPathString());
}
globalMakeEnvBuilder.put("STRIP", getStripExecutable().getPathString());
PathFragment gcovtool = getGcovToolExecutable();
if (gcovtool != null) {
// gcov-tool is optional in Crosstool
globalMakeEnvBuilder.put("GCOVTOOL", gcovtool.getPathString());
}
if (getTargetLibc().startsWith("glibc-")) {
globalMakeEnvBuilder.put("GLIBC_VERSION",
getTargetLibc().substring("glibc-".length()));
} else {
globalMakeEnvBuilder.put("GLIBC_VERSION", getTargetLibc());
}
globalMakeEnvBuilder.put("C_COMPILER", getCompiler());
globalMakeEnvBuilder.put("TARGET_CPU", getTargetCpu());
// Deprecated variables
// TODO(bazel-team): delete all of these.
globalMakeEnvBuilder.put("CROSSTOOLTOP", crosstoolTopPathFragment.getPathString());
globalMakeEnvBuilder.putAll(getAdditionalMakeVariables());
globalMakeEnvBuilder.put("ABI_GLIBC_VERSION", getAbiGlibcVersion());
globalMakeEnvBuilder.put("ABI", abi);
}
@Override
public String getOutputDirectoryName() {
String lipoSuffix;
if (getLipoMode() != LipoMode.OFF && !isAutoFdoLipo()) {
lipoSuffix = "-lipo";
} else if (getAutoFdoLipoData()) {
lipoSuffix = "-lipodata";
} else {
lipoSuffix = "";
}
return toolchainIdentifier + lipoSuffix;
}
@Override
public String getPlatformName() {
return getToolchainIdentifier();
}
public boolean alwaysAttachExtraActions() {
return true;
}
/**
* Returns true if we should share identical native libraries between different targets.
*/
public boolean shareNativeDeps() {
return cppOptions.shareNativeDeps;
}
public boolean isStrictSystemIncludes() {
return cppOptions.strictSystemIncludes;
}
@Override
public Map<String, Object> lateBoundOptionDefaults() {
// --cpu and --compiler initially default to null because their *actual* defaults aren't known
// until they're read from the CROSSTOOL. Feed the CROSSTOOL defaults in here.
return ImmutableMap.<String, Object>of(
"cpu", getTargetCpu(),
"compiler", getCompiler());
}
public PathFragment getFdoInstrument() {
return cppOptions.fdoInstrument;
}
public Path getFdoZip() {
return fdoZip;
}
/**
* Return set of features enabled by the CppConfiguration, specifically
* the FDO and LIPO related features enabled by options.
*/
@Override
public ImmutableSet<String> configurationEnabledFeatures(RuleContext ruleContext) {
ImmutableSet.Builder<String> requestedFeatures = ImmutableSet.builder();
if (cppOptions.fdoInstrument != null) {
requestedFeatures.add(CppRuleClasses.FDO_INSTRUMENT);
}
boolean isFdo = fdoZip != null && compilationMode == CompilationMode.OPT;
if (isFdo && !CppFileTypes.GCC_AUTO_PROFILE.matches(fdoZip)) {
requestedFeatures.add(CppRuleClasses.FDO_OPTIMIZE);
}
if (isFdo && CppFileTypes.GCC_AUTO_PROFILE.matches(fdoZip)) {
requestedFeatures.add(CppRuleClasses.AUTOFDO);
}
if (isLipoOptimizationOrInstrumentation()) {
requestedFeatures.add(CppRuleClasses.LIPO);
}
if (ruleContext.getConfiguration().isCodeCoverageEnabled()) {
requestedFeatures.add(CppRuleClasses.COVERAGE);
}
return requestedFeatures.build();
}
}