blob: c62e54ebc3355f0b12dc78e4e5c193899cf5e6d9 [file] [log] [blame]
// Copyright 2018 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.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.CompilationHelper;
import com.google.devtools.build.lib.analysis.FileProvider;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
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.CompilationMode;
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
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.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.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
import com.google.devtools.build.lib.rules.cpp.CcSkyframeSupportFunction.CcSkyframeSupportException;
import com.google.devtools.build.lib.rules.cpp.CppConfiguration.Tool;
import com.google.devtools.build.lib.rules.cpp.FdoContext.BranchFdoMode;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.util.FileType;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.util.StringUtil;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain;
import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CrosstoolRelease;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.protobuf.TextFormat;
import com.google.protobuf.TextFormat.ParseException;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
/** Helper responsible for creating CcToolchainProvider */
public class CcToolchainProviderHelper {
/**
* These files (found under the sysroot) may be unconditionally included in every C/C++
* compilation.
*/
static final ImmutableList<PathFragment> BUILTIN_INCLUDE_FILE_SUFFIXES =
ImmutableList.of(
PathFragment.create("include/stdc-predef.h"),
PathFragment.create("android/_predefined_api_level.h"));
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 = ")%";
/**
* Returns the profile name with the same file name as fdoProfile and an extension that matches
* {@link FileType}.
*/
private static String getLLVMProfileFileName(FdoInputFile fdoProfile, FileType type) {
if (type.matches(fdoProfile)) {
return fdoProfile.getBasename();
} else {
return FileSystemUtils.removeExtension(fdoProfile.getBasename())
+ type.getExtensions().get(0);
}
}
/**
* 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;
}
}
if (!PathFragment.isNormalized(pathString)) {
throw new InvalidConfigurationException("The include path '" + s + "' is not normalized.");
}
PathFragment path = PathFragment.create(pathString);
return pathPrefix.getRelative(path);
}
private static String getSkylarkValueForTool(Tool tool, CppToolchainInfo cppToolchainInfo) {
PathFragment toolPath = cppToolchainInfo.getToolPathFragment(tool);
return toolPath != null ? toolPath.getPathString() : "";
}
private static ImmutableMap<String, Object> getToolchainForSkylark(
CppToolchainInfo cppToolchainInfo) {
return ImmutableMap.<String, Object>builder()
.put("objcopy_executable", getSkylarkValueForTool(Tool.OBJCOPY, cppToolchainInfo))
.put("compiler_executable", getSkylarkValueForTool(Tool.GCC, cppToolchainInfo))
.put("preprocessor_executable", getSkylarkValueForTool(Tool.CPP, cppToolchainInfo))
.put("nm_executable", getSkylarkValueForTool(Tool.NM, cppToolchainInfo))
.put("objdump_executable", getSkylarkValueForTool(Tool.OBJDUMP, cppToolchainInfo))
.put("ar_executable", getSkylarkValueForTool(Tool.AR, cppToolchainInfo))
.put("strip_executable", getSkylarkValueForTool(Tool.STRIP, cppToolchainInfo))
.put("ld_executable", getSkylarkValueForTool(Tool.LD, cppToolchainInfo))
.build();
}
private static PathFragment calculateSysroot(
CcToolchainAttributesProvider attributes, PathFragment defaultSysroot) {
TransitiveInfoCollection sysrootTarget = attributes.getLibcTop();
if (sysrootTarget == null) {
return defaultSysroot;
}
return sysrootTarget.getLabel().getPackageFragment();
}
private static Artifact getPrefetchHintsArtifact(
FdoInputFile prefetchHintsFile, RuleContext ruleContext) {
if (prefetchHintsFile == null) {
return null;
}
Artifact prefetchHintsArtifact = prefetchHintsFile.getArtifact();
if (prefetchHintsArtifact != null) {
return prefetchHintsArtifact;
}
prefetchHintsArtifact =
ruleContext.getUniqueDirectoryArtifact(
"fdo",
prefetchHintsFile.getAbsolutePath().getBaseName(),
ruleContext.getBinOrGenfilesDirectory());
ruleContext.registerAction(SymlinkAction.toAbsolutePath(
ruleContext.getActionOwner(),
PathFragment.create(prefetchHintsFile.getAbsolutePath().getPathString()),
prefetchHintsArtifact,
"Symlinking LLVM Cache Prefetch Hints Profile "
+ prefetchHintsFile.getAbsolutePath().getPathString()));
return prefetchHintsArtifact;
}
private static void symlinkTo(
RuleContext ruleContext,
Artifact symlink,
FdoInputFile fdoInputFile,
String progressMessage) {
if (fdoInputFile.getArtifact() != null) {
ruleContext.registerAction(
SymlinkAction.toArtifact(
ruleContext.getActionOwner(), fdoInputFile.getArtifact(), symlink, progressMessage));
} else {
ruleContext.registerAction(
SymlinkAction.toAbsolutePath(
ruleContext.getActionOwner(),
fdoInputFile.getAbsolutePath(),
symlink,
progressMessage));
}
}
/*
* This function checks the format of the input profile data and converts it to
* the indexed format (.profdata) if necessary.
*/
private static Artifact convertLLVMRawProfileToIndexed(
CcToolchainAttributesProvider attributes,
FdoInputFile fdoProfile,
CppToolchainInfo toolchainInfo,
RuleContext ruleContext) {
Artifact profileArtifact =
ruleContext.getUniqueDirectoryArtifact(
"fdo",
getLLVMProfileFileName(fdoProfile, CppFileTypes.LLVM_PROFILE),
ruleContext.getBinOrGenfilesDirectory());
// If the profile file is already in the desired format, symlink to it and return.
if (CppFileTypes.LLVM_PROFILE.matches(fdoProfile)) {
symlinkTo(
ruleContext,
profileArtifact,
fdoProfile,
"Symlinking LLVM Profile " + fdoProfile.getBasename());
return profileArtifact;
}
Artifact rawProfileArtifact;
if (CppFileTypes.LLVM_PROFILE_ZIP.matches(fdoProfile)) {
// Get the zipper binary for unzipping the profile.
Artifact zipperBinaryArtifact = attributes.getZipper();
if (zipperBinaryArtifact == null) {
ruleContext.ruleError("Cannot find zipper binary to unzip the profile");
return null;
}
// TODO(zhayu): find a way to avoid hard-coding cpu architecture here (b/65582760)
String rawProfileFileName = "fdocontrolz_profile.profraw";
String cpu = toolchainInfo.getTargetCpu();
if (!"k8".equals(cpu)) {
rawProfileFileName = "fdocontrolz_profile-" + cpu + ".profraw";
}
rawProfileArtifact =
ruleContext.getUniqueDirectoryArtifact(
"fdo", rawProfileFileName, ruleContext.getBinOrGenfilesDirectory());
// Symlink to the zipped profile file to extract the contents.
Artifact zipProfileArtifact =
ruleContext.getUniqueDirectoryArtifact(
"fdo", fdoProfile.getBasename(), ruleContext.getBinOrGenfilesDirectory());
symlinkTo(
ruleContext,
zipProfileArtifact,
fdoProfile,
"Symlinking LLVM ZIP Profile " + fdoProfile.getBasename());
// Unzip the profile.
ruleContext.registerAction(
new SpawnAction.Builder()
.addInput(zipProfileArtifact)
.addInput(zipperBinaryArtifact)
.addOutput(rawProfileArtifact)
.useDefaultShellEnvironment()
.setExecutable(zipperBinaryArtifact)
.setProgressMessage(
"LLVMUnzipProfileAction: Generating %s", rawProfileArtifact.prettyPrint())
.setMnemonic("LLVMUnzipProfileAction")
.addCommandLine(
CustomCommandLine.builder()
.addExecPath("xf", zipProfileArtifact)
.add(
"-d",
rawProfileArtifact.getExecPath().getParentDirectory().getSafePathString())
.build())
.build(ruleContext));
} else {
rawProfileArtifact =
ruleContext.getUniqueDirectoryArtifact(
"fdo",
getLLVMProfileFileName(fdoProfile, CppFileTypes.LLVM_PROFILE_RAW),
ruleContext.getBinOrGenfilesDirectory());
symlinkTo(
ruleContext,
rawProfileArtifact,
fdoProfile,
"Symlinking LLVM Raw Profile " + fdoProfile.getBasename());
}
if (toolchainInfo.getToolPathFragment(Tool.LLVM_PROFDATA) == null) {
ruleContext.ruleError(
"llvm-profdata not available with this crosstool, needed for profile conversion");
return null;
}
// Convert LLVM raw profile to indexed format.
ruleContext.registerAction(
new SpawnAction.Builder()
.addInput(rawProfileArtifact)
.addTransitiveInputs(attributes.getAllFilesMiddleman())
.addOutput(profileArtifact)
.useDefaultShellEnvironment()
.setExecutable(toolchainInfo.getToolPathFragment(Tool.LLVM_PROFDATA))
.setProgressMessage("LLVMProfDataAction: Generating %s", profileArtifact.prettyPrint())
.setMnemonic("LLVMProfDataAction")
.addCommandLine(
CustomCommandLine.builder()
.add("merge")
.add("-o")
.addExecPath(profileArtifact)
.addExecPath(rawProfileArtifact)
.build())
.build(ruleContext));
return profileArtifact;
}
static Pair<FdoInputFile, Artifact> getFdoInputs(
RuleContext ruleContext, FdoProfileProvider fdoProfileProvider) {
if (fdoProfileProvider == null) {
ruleContext.ruleError("--fdo_profile/--xbinary_fdo input needs to be an fdo_profile rule");
return null;
}
return Pair.of(fdoProfileProvider.getInputFile(), fdoProfileProvider.getProtoProfileArtifact());
}
static CcToolchainProvider getCcToolchainProvider(
RuleContext ruleContext,
CcToolchainAttributesProvider attributes,
CrosstoolRelease crosstoolFromCcToolchainSuiteProtoAttribute)
throws RuleErrorException, InterruptedException {
BuildConfiguration configuration = Preconditions.checkNotNull(ruleContext.getConfiguration());
CppConfiguration cppConfiguration =
Preconditions.checkNotNull(configuration.getFragment(CppConfiguration.class));
PathFragment fdoZip = null;
FdoInputFile fdoInputFile = null;
FdoInputFile prefetchHints = null;
Artifact protoProfileArtifact = null;
Pair<FdoInputFile, Artifact> fdoInputs = null;
if (configuration.getCompilationMode() == CompilationMode.OPT) {
if (cppConfiguration.getFdoPrefetchHintsLabel() != null) {
FdoPrefetchHintsProvider provider = attributes.getFdoPrefetch();
prefetchHints = provider.getInputFile();
}
if (cppConfiguration.getFdoPath() != null) {
fdoZip = cppConfiguration.getFdoPath();
} else if (cppConfiguration.getFdoOptimizeLabel() != null) {
FdoProfileProvider fdoProfileProvider = attributes.getFdoOptimizeProvider();
if (fdoProfileProvider != null) {
fdoInputs = getFdoInputs(ruleContext, fdoProfileProvider);
} else {
fdoInputFile = fdoInputFileFromArtifacts(ruleContext, attributes);
}
} else if (cppConfiguration.getFdoProfileLabel() != null) {
fdoInputs = getFdoInputs(ruleContext, attributes.getFdoProfileProvider());
} else if (cppConfiguration.getXFdoProfileLabel() != null) {
fdoInputs = getFdoInputs(ruleContext, attributes.getXFdoProfileProvider());
}
}
if (ruleContext.hasErrors()) {
return null;
}
if (fdoInputs != null) {
fdoInputFile = fdoInputs.getFirst();
protoProfileArtifact = fdoInputs.getSecond();
}
CcSkyframeSupportValue ccSkyframeSupportValue = null;
SkyKey ccSupportKey = null;
CToolchain toolchain = null;
if (cppConfiguration.disableCrosstool() && attributes.getCcToolchainConfigInfo() == null) {
ruleContext.ruleError(
"cc_toolchain.toolchain_config attribute must be specified. See "
+ "https://github.com/bazelbuild/bazel/issues/7320 for details.");
}
if (attributes.getCcToolchainConfigInfo() != null) {
if (fdoZip != null) {
ccSupportKey = CcSkyframeSupportValue.key(fdoZip, /* packageWithCrosstoolInIt= */ null);
}
} else {
// Is there a toolchain proto available on the target directly?
toolchain = parseToolchainFromAttributes(ruleContext, attributes);
PackageIdentifier packageWithCrosstoolInIt = null;
if (toolchain == null && crosstoolFromCcToolchainSuiteProtoAttribute == null) {
packageWithCrosstoolInIt = ruleContext.getLabel().getPackageIdentifier();
}
if (packageWithCrosstoolInIt != null || fdoZip != null) {
ccSupportKey = CcSkyframeSupportValue.key(fdoZip, packageWithCrosstoolInIt);
}
}
if (ccSupportKey != null) {
SkyFunction.Environment skyframeEnv = ruleContext.getAnalysisEnvironment().getSkyframeEnv();
try {
ccSkyframeSupportValue =
(CcSkyframeSupportValue)
skyframeEnv.getValueOrThrow(ccSupportKey, CcSkyframeSupportException.class);
} catch (CcSkyframeSupportException e) {
throw ruleContext.throwWithRuleError(e.getMessage());
}
if (skyframeEnv.valuesMissing()) {
return null;
}
}
if (fdoZip != null) {
// fdoZip should be set if the profile is a path, fdoInputFile if it is an artifact, but never
// both
Preconditions.checkState(fdoInputFile == null);
fdoInputFile =
FdoInputFile.fromAbsolutePath(ccSkyframeSupportValue.getFdoZipPath().asFragment());
}
CppToolchainInfo toolchainInfo =
getCppToolchainInfo(
ruleContext,
cppConfiguration,
attributes,
ccSkyframeSupportValue,
toolchain,
crosstoolFromCcToolchainSuiteProtoAttribute);
FdoContext.BranchFdoProfile branchFdoProfile = null;
if (fdoInputFile != null) {
BranchFdoMode branchFdoMode;
if (CppFileTypes.GCC_AUTO_PROFILE.matches(fdoInputFile)) {
branchFdoMode = BranchFdoMode.AUTO_FDO;
} else if (CppFileTypes.XBINARY_PROFILE.matches(fdoInputFile)) {
branchFdoMode = BranchFdoMode.XBINARY_FDO;
} else if (CppFileTypes.LLVM_PROFILE.matches(fdoInputFile)) {
branchFdoMode = BranchFdoMode.LLVM_FDO;
} else if (CppFileTypes.LLVM_PROFILE_RAW.matches(fdoInputFile)) {
branchFdoMode = BranchFdoMode.LLVM_FDO;
} else if (CppFileTypes.LLVM_PROFILE_ZIP.matches(fdoInputFile)) {
branchFdoMode = BranchFdoMode.LLVM_FDO;
} else {
ruleContext.ruleError("invalid extension for FDO profile file.");
return null;
}
if (branchFdoMode != BranchFdoMode.XBINARY_FDO
&& cppConfiguration.getXFdoProfileLabel() != null) {
ruleContext.throwWithRuleError(
"--xbinary_fdo cannot accept profile input other than *.xfdo");
}
if (configuration.isCodeCoverageEnabled()) {
ruleContext.throwWithRuleError("coverage mode is not compatible with FDO optimization");
}
// This tries to convert LLVM profiles to the indexed format if necessary.
Artifact profileArtifact = null;
if (branchFdoMode == BranchFdoMode.LLVM_FDO) {
profileArtifact =
convertLLVMRawProfileToIndexed(attributes, fdoInputFile, toolchainInfo, ruleContext);
if (ruleContext.hasErrors()) {
return null;
}
} else if (branchFdoMode == BranchFdoMode.AUTO_FDO
|| branchFdoMode == BranchFdoMode.XBINARY_FDO) {
profileArtifact =
ruleContext.getUniqueDirectoryArtifact(
"fdo", fdoInputFile.getBasename(), ruleContext.getBinOrGenfilesDirectory());
symlinkTo(
ruleContext,
profileArtifact,
fdoInputFile,
"Symlinking FDO profile " + fdoInputFile.getBasename());
}
branchFdoProfile =
new FdoContext.BranchFdoProfile(branchFdoMode, profileArtifact, protoProfileArtifact);
}
String purposePrefix = attributes.getPurposePrefix();
String runtimeSolibDirBase = attributes.getRuntimeSolibDirBase();
final PathFragment runtimeSolibDir =
configuration.getBinFragment().getRelative(runtimeSolibDirBase);
// Static runtime inputs.
TransitiveInfoCollection staticRuntimeLib = attributes.getStaticRuntimeLib();
final NestedSet<Artifact> staticRuntimeLinkInputs;
final Artifact staticRuntimeLinkMiddleman;
if (staticRuntimeLib != null) {
staticRuntimeLinkInputs = staticRuntimeLib.getProvider(FileProvider.class).getFilesToBuild();
if (!staticRuntimeLinkInputs.isEmpty()) {
NestedSet<Artifact> staticRuntimeLinkMiddlemanSet =
CompilationHelper.getAggregatingMiddleman(
ruleContext, purposePrefix + "static_runtime_link", staticRuntimeLib);
staticRuntimeLinkMiddleman =
staticRuntimeLinkMiddlemanSet.isEmpty()
? null
: Iterables.getOnlyElement(staticRuntimeLinkMiddlemanSet);
} else {
staticRuntimeLinkMiddleman = null;
}
Preconditions.checkState(
(staticRuntimeLinkMiddleman == null) == staticRuntimeLinkInputs.isEmpty());
} else {
staticRuntimeLinkInputs = null;
staticRuntimeLinkMiddleman = null;
}
// Dynamic runtime inputs.
TransitiveInfoCollection dynamicRuntimeLib = attributes.getDynamicRuntimeLib();
NestedSet<Artifact> dynamicRuntimeLinkSymlinks;
List<Artifact> dynamicRuntimeLinkInputs = new ArrayList<>();
Artifact dynamicRuntimeLinkMiddleman;
if (dynamicRuntimeLib != null) {
NestedSetBuilder<Artifact> dynamicRuntimeLinkSymlinksBuilder = NestedSetBuilder.stableOrder();
for (Artifact artifact :
dynamicRuntimeLib.getProvider(FileProvider.class).getFilesToBuild()) {
if (CppHelper.SHARED_LIBRARY_FILETYPES.matches(artifact.getFilename())) {
dynamicRuntimeLinkInputs.add(artifact);
dynamicRuntimeLinkSymlinksBuilder.add(
SolibSymlinkAction.getCppRuntimeSymlink(
ruleContext,
artifact,
toolchainInfo.getSolibDirectory(),
runtimeSolibDirBase,
configuration));
}
}
if (dynamicRuntimeLinkInputs.isEmpty()) {
dynamicRuntimeLinkSymlinks = NestedSetBuilder.emptySet(Order.STABLE_ORDER);
} else {
dynamicRuntimeLinkSymlinks = dynamicRuntimeLinkSymlinksBuilder.build();
}
} else {
dynamicRuntimeLinkSymlinks = null;
}
if (!dynamicRuntimeLinkInputs.isEmpty()) {
List<Artifact> dynamicRuntimeLinkMiddlemanSet =
CppHelper.getAggregatingMiddlemanForCppRuntimes(
ruleContext,
purposePrefix + "dynamic_runtime_link",
dynamicRuntimeLinkInputs,
toolchainInfo.getSolibDirectory(),
runtimeSolibDirBase,
configuration);
dynamicRuntimeLinkMiddleman =
dynamicRuntimeLinkMiddlemanSet.isEmpty()
? null
: Iterables.getOnlyElement(dynamicRuntimeLinkMiddlemanSet);
} else {
dynamicRuntimeLinkMiddleman = null;
}
Preconditions.checkState(
(dynamicRuntimeLinkMiddleman == null)
== (dynamicRuntimeLinkSymlinks == null || dynamicRuntimeLinkSymlinks.isEmpty()));
CcCompilationContext.Builder ccCompilationContextBuilder =
new CcCompilationContext.Builder(
ruleContext, ruleContext.getConfiguration(), ruleContext.getLabel());
CppModuleMap moduleMap = createCrosstoolModuleMap(attributes);
if (moduleMap != null) {
ccCompilationContextBuilder.setCppModuleMap(moduleMap);
}
final CcCompilationContext ccCompilationContext = ccCompilationContextBuilder.build();
NestedSetBuilder<Pair<String, String>> coverageEnvironment = NestedSetBuilder.compileOrder();
PathFragment sysroot = calculateSysroot(attributes, toolchainInfo.getDefaultSysroot());
ImmutableList.Builder<PathFragment> builtInIncludeDirectoriesBuilder = ImmutableList.builder();
for (String s : toolchainInfo.getRawBuiltInIncludeDirectories()) {
try {
builtInIncludeDirectoriesBuilder.add(
resolveIncludeDir(s, sysroot, toolchainInfo.getToolsDirectory()));
} catch (InvalidConfigurationException e) {
ruleContext.ruleError(e.getMessage());
}
}
ImmutableList<PathFragment> builtInIncludeDirectories =
builtInIncludeDirectoriesBuilder.build();
coverageEnvironment.add(
Pair.of(
"COVERAGE_GCOV_PATH", toolchainInfo.getToolPathFragment(Tool.GCOV).getPathString()));
if (cppConfiguration.getFdoInstrument() != null) {
coverageEnvironment.add(Pair.of("FDO_DIR", cppConfiguration.getFdoInstrument()));
}
Artifact prefetchHintsArtifact = getPrefetchHintsArtifact(prefetchHints, ruleContext);
reportInvalidOptions(ruleContext, toolchainInfo);
return new CcToolchainProvider(
getToolchainForSkylark(toolchainInfo),
cppConfiguration,
toolchainInfo,
toolchainInfo.getToolsDirectory(),
attributes.getAllFiles(),
attributes.getFullInputsForCrosstool(),
attributes.getCompilerFiles(),
attributes.getCompilerFilesWithoutIncludes(),
attributes.getStripFiles(),
attributes.getObjcopyFiles(),
attributes.getAsFiles(),
attributes.getArFiles(),
attributes.getFullInputsForLink(),
attributes.getIfsoBuilder(),
attributes.getDwpFiles(),
attributes.getCoverage(),
attributes.getLibc(),
staticRuntimeLinkInputs,
staticRuntimeLinkMiddleman,
dynamicRuntimeLinkSymlinks,
dynamicRuntimeLinkMiddleman,
runtimeSolibDir,
ccCompilationContext,
attributes.isSupportsParamFiles(),
attributes.isSupportsHeaderParsing(),
getBuildVariables(
ruleContext,
attributes,
toolchainInfo.getDefaultSysroot(),
attributes.getAdditionalBuildVariables()),
getBuiltinIncludes(attributes.getLibc()),
coverageEnvironment.build(),
attributes.getLinkDynamicLibraryTool(),
builtInIncludeDirectories,
sysroot,
new FdoContext(branchFdoProfile, prefetchHintsArtifact),
cppConfiguration.useLLVMCoverageMapFormat(),
configuration.isCodeCoverageEnabled(),
configuration.isHostConfiguration(),
attributes.getLicensesProvider());
}
private static FdoInputFile fdoInputFileFromArtifacts(
RuleContext ruleContext, CcToolchainAttributesProvider attributes) {
ImmutableList<Artifact> fdoArtifacts = attributes.getFdoOptimizeArtifacts();
if (fdoArtifacts.size() != 1) {
ruleContext.ruleError("--fdo_optimize does not point to a single target");
return null;
}
Artifact fdoArtifact = fdoArtifacts.get(0);
if (!fdoArtifact.isSourceArtifact()) {
ruleContext.ruleError("--fdo_optimize points to a target that is not an input file");
return null;
}
Label fdoLabel = attributes.getFdoOptimize().getLabel();
if (!fdoLabel
.getPackageIdentifier()
.getPathUnderExecRoot()
.getRelative(fdoLabel.getName())
.equals(fdoArtifact.getExecPath())) {
ruleContext.ruleError("--fdo_optimize points to a target that is not an input file");
return null;
}
return FdoInputFile.fromArtifact(fdoArtifact);
}
/** Finds an appropriate {@link CppToolchainInfo} for this target. */
private static CppToolchainInfo getCppToolchainInfo(
RuleContext ruleContext,
CppConfiguration cppConfiguration,
CcToolchainAttributesProvider attributes,
CcSkyframeSupportValue ccSkyframeSupportValue,
CToolchain toolchainFromCcToolchainAttribute,
CrosstoolRelease crosstoolFromCcToolchainSuiteProtoAttribute)
throws RuleErrorException {
CcToolchainConfigInfo configInfo = attributes.getCcToolchainConfigInfo();
if (configInfo != null) {
try {
return CppToolchainInfo.create(
ruleContext.getLabel(),
configInfo,
cppConfiguration.disableLegacyCrosstoolFields(),
cppConfiguration.disableGenruleCcToolchainDependency());
} catch (EvalException e) {
throw ruleContext.throwWithRuleError(e.getMessage());
}
}
// Attempt to find a toolchain based on the target attributes, not the configuration.
CToolchain toolchain = toolchainFromCcToolchainAttribute;
if (toolchain == null) {
toolchain =
getToolchainFromAttributes(
ruleContext,
attributes,
cppConfiguration,
crosstoolFromCcToolchainSuiteProtoAttribute,
ccSkyframeSupportValue);
}
// If we found a toolchain, use it.
try {
toolchain =
CppToolchainInfo.addLegacyFeatures(
toolchain, CppToolchainInfo.getToolsDirectory(attributes.getCcToolchainLabel()));
CcToolchainConfigInfo ccToolchainConfigInfo =
CcToolchainConfigInfo.fromToolchain(ruleContext, toolchain);
return CppToolchainInfo.create(
attributes.getCcToolchainLabel(),
ccToolchainConfigInfo,
cppConfiguration.disableLegacyCrosstoolFields(),
cppConfiguration.disableGenruleCcToolchainDependency());
} catch (EvalException e) {
throw ruleContext.throwWithRuleError(e.getMessage());
}
}
@Nullable
private static CToolchain parseToolchainFromAttributes(
RuleContext ruleContext, CcToolchainAttributesProvider attributes) throws RuleErrorException {
String protoAttribute = StringUtil.emptyToNull(attributes.getProto());
if (protoAttribute == null) {
return null;
}
CToolchain.Builder builder = CToolchain.newBuilder();
try {
TextFormat.merge(protoAttribute, builder);
return builder.build();
} catch (ParseException e) {
throw ruleContext.throwWithAttributeError("proto", "Could not parse CToolchain data");
}
}
private static void reportInvalidOptions(RuleContext ruleContext, CppToolchainInfo toolchain) {
CppOptions options = ruleContext.getConfiguration().getOptions().get(CppOptions.class);
CppConfiguration config = ruleContext.getFragment(CppConfiguration.class);
if (options.fissionModes.contains(config.getCompilationMode())
&& !toolchain.supportsFission()) {
ruleContext.ruleWarning(
"Fission is not supported by this crosstool. Please use a "
+ "supporting crosstool to enable fission");
}
if (options.buildTestDwp
&& !(toolchain.supportsFission() && config.fissionIsActiveForCurrentCompilationMode())) {
ruleContext.ruleWarning(
"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 to build statically.");
}
if (config.getLibcTopLabel() != null && toolchain.getDefaultSysroot() == null) {
ruleContext.ruleError(
"The selected toolchain "
+ toolchain.getToolchainIdentifier()
+ " does not support setting --grte_top (it doesn't specify builtin_sysroot).");
}
}
@Nullable
private static CToolchain getToolchainFromAttributes(
RuleContext ruleContext,
CcToolchainAttributesProvider attributes,
CppConfiguration cppConfiguration,
CrosstoolRelease crosstoolFromCcToolchainSuiteProtoAttribute,
CcSkyframeSupportValue ccSkyframeSupportValue)
throws RuleErrorException {
try {
CrosstoolRelease crosstoolRelease;
if (crosstoolFromCcToolchainSuiteProtoAttribute != null) {
// We have cc_toolchain_suite.proto attribute set, let's use it
crosstoolRelease = crosstoolFromCcToolchainSuiteProtoAttribute;
} else {
// We use the proto from the CROSSTOOL file
crosstoolRelease = ccSkyframeSupportValue.getCrosstoolRelease();
}
return CToolchainSelectionUtils.selectCToolchain(
attributes.getToolchainIdentifier(),
attributes.getCpu(),
attributes.getCompiler(),
cppConfiguration.getTransformedCpuFromOptions(),
cppConfiguration.getCompilerFromOptions(),
crosstoolRelease);
} catch (InvalidConfigurationException e) {
ruleContext.throwWithRuleError(
String.format("Error while selecting cc_toolchain: %s", e.getMessage()));
return null;
}
}
private static ImmutableList<Artifact> getBuiltinIncludes(NestedSet<Artifact> libc) {
ImmutableList.Builder<Artifact> result = ImmutableList.builder();
for (Artifact artifact : libc) {
for (PathFragment suffix : BUILTIN_INCLUDE_FILE_SUFFIXES) {
if (artifact.getExecPath().endsWith(suffix)) {
result.add(artifact);
break;
}
}
}
return result.build();
}
private static CppModuleMap createCrosstoolModuleMap(CcToolchainAttributesProvider attributes) {
if (attributes.getModuleMap() == null) {
return null;
}
Artifact moduleMapArtifact = attributes.getModuleMapArtifact();
if (moduleMapArtifact == null) {
return null;
}
return new CppModuleMap(moduleMapArtifact, "crosstool");
}
static TransitiveInfoCollection selectDep(
ImmutableList<? extends TransitiveInfoCollection> deps, Label label) {
if (deps.isEmpty()) {
return null;
}
for (TransitiveInfoCollection dep : deps) {
if (dep.getLabel().equals(label)) {
return dep;
}
}
return deps.get(0);
}
/**
* Returns {@link CcToolchainVariables} instance with build variables that only depend on the
* toolchain.
*
* @param ruleContext the rule context
* @param defaultSysroot the default sysroot
* @param additionalBuildVariables
* @throws RuleErrorException if there are configuration errors making it impossible to resolve
* certain build variables of this toolchain
*/
private static final CcToolchainVariables getBuildVariables(
RuleContext ruleContext,
CcToolchainAttributesProvider attributes,
PathFragment defaultSysroot,
CcToolchainVariables additionalBuildVariables) {
CcToolchainVariables.Builder variables = new CcToolchainVariables.Builder();
CppConfiguration cppConfiguration =
Preconditions.checkNotNull(ruleContext.getFragment(CppConfiguration.class));
String minOsVersion = cppConfiguration.getMinimumOsVersion();
if (minOsVersion != null) {
variables.addStringVariable(CcCommon.MINIMUM_OS_VERSION_VARIABLE_NAME, minOsVersion);
}
PathFragment sysroot = calculateSysroot(attributes, defaultSysroot);
if (sysroot != null) {
variables.addStringVariable(CcCommon.SYSROOT_VARIABLE_NAME, sysroot.getPathString());
}
variables.addAllNonTransitive(additionalBuildVariables);
return variables.build();
}
}