// Copyright 2014 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.rules.cpp; import static com.google.devtools.build.lib.syntax.Type.BOOLEAN; 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.Actions; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.AnalysisUtils; import com.google.devtools.build.lib.analysis.CompilationHelper; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.FileProvider; import com.google.devtools.build.lib.analysis.LicensesProvider; import com.google.devtools.build.lib.analysis.LicensesProvider.TargetLicense; import com.google.devtools.build.lib.analysis.MiddlemanProvider; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.Runfiles; import com.google.devtools.build.lib.analysis.RunfilesProvider; import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; import com.google.devtools.build.lib.analysis.config.CompilationMode; import com.google.devtools.build.lib.cmdline.Label; 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.License; import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; import com.google.devtools.build.lib.rules.cpp.FdoSupport.FdoException; 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.skyframe.SkyFunction; import com.google.devtools.build.skyframe.SkyKey; import java.io.IOException; import java.util.List; import java.util.Map; /** * Implementation for the cc_toolchain rule. */ public class CcToolchain implements RuleConfiguredTargetFactory { /** * This file (found under the sysroot) may be unconditionally included in every C/C++ compilation. */ private static final PathFragment BUILTIN_INCLUDE_FILE_SUFFIX = new PathFragment("include/stdc-predef.h"); @Override public ConfiguredTarget create(RuleContext ruleContext) { CppConfiguration cppConfiguration = ruleContext.getFragment(CppConfiguration.class); Path fdoZip = ruleContext.getConfiguration().getCompilationMode() == CompilationMode.OPT ? cppConfiguration.getFdoZip() : null; SkyKey fdoKey = FdoSupportValue.key( cppConfiguration.getLipoMode(), fdoZip, cppConfiguration.getFdoInstrument()); SkyFunction.Environment skyframeEnv = ruleContext.getAnalysisEnvironment().getSkyframeEnv(); FdoSupportValue fdoSupport; try { fdoSupport = (FdoSupportValue) skyframeEnv.getValueOrThrow( fdoKey, FdoException.class, IOException.class); } catch (FdoException | IOException e) { ruleContext.ruleError("cannot initialize FDO: " + e.getMessage()); return null; } if (skyframeEnv.valuesMissing()) { return null; } final Label label = ruleContext.getLabel(); final NestedSet crosstool = ruleContext.getPrerequisite("all_files", Mode.HOST) .getProvider(FileProvider.class).getFilesToBuild(); final NestedSet crosstoolMiddleman = getFiles(ruleContext, "all_files"); final NestedSet compile = getFiles(ruleContext, "compiler_files"); final NestedSet strip = getFiles(ruleContext, "strip_files"); final NestedSet objcopy = getFiles(ruleContext, "objcopy_files"); final NestedSet link = getFiles(ruleContext, "linker_files"); final NestedSet dwp = getFiles(ruleContext, "dwp_files"); final NestedSet libcLink = inputsForLibc(ruleContext); String purposePrefix = Actions.escapeLabel(label) + "_"; String runtimeSolibDirBase = "_solib_" + "_" + Actions.escapeLabel(label); final PathFragment runtimeSolibDir = ruleContext.getConfiguration() .getBinFragment().getRelative(runtimeSolibDirBase); // Static runtime inputs. TransitiveInfoCollection staticRuntimeLibDep = selectDep(ruleContext, "static_runtime_libs", cppConfiguration.getStaticRuntimeLibsLabel()); final NestedSet staticRuntimeLinkInputs; final Artifact staticRuntimeLinkMiddleman; if (cppConfiguration.supportsEmbeddedRuntimes()) { staticRuntimeLinkInputs = staticRuntimeLibDep .getProvider(FileProvider.class) .getFilesToBuild(); } else { staticRuntimeLinkInputs = NestedSetBuilder.emptySet(Order.STABLE_ORDER); } if (!staticRuntimeLinkInputs.isEmpty()) { NestedSet staticRuntimeLinkMiddlemanSet = CompilationHelper.getAggregatingMiddleman( ruleContext, purposePrefix + "static_runtime_link", staticRuntimeLibDep); staticRuntimeLinkMiddleman = staticRuntimeLinkMiddlemanSet.isEmpty() ? null : Iterables.getOnlyElement(staticRuntimeLinkMiddlemanSet); } else { staticRuntimeLinkMiddleman = null; } Preconditions.checkState( (staticRuntimeLinkMiddleman == null) == staticRuntimeLinkInputs.isEmpty()); // Dynamic runtime inputs. TransitiveInfoCollection dynamicRuntimeLibDep = selectDep(ruleContext, "dynamic_runtime_libs", cppConfiguration.getDynamicRuntimeLibsLabel()); final NestedSet dynamicRuntimeLinkInputs; final Artifact dynamicRuntimeLinkMiddleman; if (cppConfiguration.supportsEmbeddedRuntimes()) { NestedSetBuilder dynamicRuntimeLinkInputsBuilder = NestedSetBuilder.stableOrder(); for (Artifact artifact : dynamicRuntimeLibDep .getProvider(FileProvider.class).getFilesToBuild()) { if (CppHelper.SHARED_LIBRARY_FILETYPES.matches(artifact.getFilename())) { dynamicRuntimeLinkInputsBuilder.add(SolibSymlinkAction.getCppRuntimeSymlink( ruleContext, artifact, runtimeSolibDirBase, ruleContext.getConfiguration()).getArtifact()); } else { dynamicRuntimeLinkInputsBuilder.add(artifact); } } dynamicRuntimeLinkInputs = dynamicRuntimeLinkInputsBuilder.build(); } else { dynamicRuntimeLinkInputs = NestedSetBuilder.emptySet(Order.STABLE_ORDER); } if (!dynamicRuntimeLinkInputs.isEmpty()) { List dynamicRuntimeLinkMiddlemanSet = CppHelper.getAggregatingMiddlemanForCppRuntimes( ruleContext, purposePrefix + "dynamic_runtime_link", dynamicRuntimeLibDep, runtimeSolibDirBase, ruleContext.getConfiguration()); dynamicRuntimeLinkMiddleman = dynamicRuntimeLinkMiddlemanSet.isEmpty() ? null : Iterables.getOnlyElement(dynamicRuntimeLinkMiddlemanSet); } else { dynamicRuntimeLinkMiddleman = null; } Preconditions.checkState( (dynamicRuntimeLinkMiddleman == null) == dynamicRuntimeLinkInputs.isEmpty()); CppCompilationContext.Builder contextBuilder = new CppCompilationContext.Builder(ruleContext); CppModuleMap moduleMap = createCrosstoolModuleMap(ruleContext); if (moduleMap != null) { contextBuilder.setCppModuleMap(moduleMap); } final CppCompilationContext context = contextBuilder.build(); boolean supportsParamFiles = ruleContext.attributes().get("supports_param_files", BOOLEAN); boolean supportsHeaderParsing = ruleContext.attributes().get("supports_header_parsing", BOOLEAN); CcToolchainProvider provider = new CcToolchainProvider( Preconditions.checkNotNull(ruleContext.getFragment(CppConfiguration.class)), crosstool, fullInputsForCrosstool(ruleContext, crosstoolMiddleman), compile, strip, objcopy, fullInputsForLink(ruleContext, link), dwp, libcLink, staticRuntimeLinkInputs, staticRuntimeLinkMiddleman, dynamicRuntimeLinkInputs, dynamicRuntimeLinkMiddleman, runtimeSolibDir, context, supportsParamFiles, supportsHeaderParsing, getBuildVariables(ruleContext), getBuiltinIncludes(ruleContext)); RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext) .add(CcToolchainProvider.class, provider) .add(FdoSupportProvider.class, new FdoSupportProvider(fdoSupport.getFdoSupport())) .setFilesToBuild(new NestedSetBuilder(Order.STABLE_ORDER).build()) .add(RunfilesProvider.class, RunfilesProvider.simple(Runfiles.EMPTY)); // If output_license is specified on the cc_toolchain rule, override the transitive licenses // with that one. This is necessary because cc_toolchain is used in the target configuration, // but it is sort-of-kind-of a tool, but various parts of it are linked into the output... // ...so we trust the judgment of the author of the cc_toolchain rule to figure out what // licenses should be propagated to C++ targets. License outputLicense = ruleContext.getRule().getToolOutputLicense(ruleContext.attributes()); if (outputLicense != null && outputLicense != License.NO_LICENSE) { final NestedSet license = NestedSetBuilder.create(Order.STABLE_ORDER, new TargetLicense(ruleContext.getLabel(), outputLicense)); LicensesProvider licensesProvider = new LicensesProvider() { @Override public NestedSet getTransitiveLicenses() { return license; } }; builder.add(LicensesProvider.class, licensesProvider); } return builder.build(); } private ImmutableList getBuiltinIncludes(RuleContext ruleContext) { ImmutableList.Builder result = ImmutableList.builder(); for (Artifact artifact : inputsForLibc(ruleContext)) { if (artifact.getExecPath().endsWith(BUILTIN_INCLUDE_FILE_SUFFIX)) { result.add(artifact); } } return result.build(); } private NestedSet inputsForLibc(RuleContext ruleContext) { TransitiveInfoCollection libc = ruleContext.getPrerequisite(":libc_top", Mode.HOST); return libc != null ? libc.getProvider(FileProvider.class).getFilesToBuild() : NestedSetBuilder.emptySet(Order.STABLE_ORDER); } private NestedSet fullInputsForCrosstool(RuleContext ruleContext, NestedSet crosstoolMiddleman) { return NestedSetBuilder.stableOrder() .addTransitive(crosstoolMiddleman) .addTransitive(AnalysisUtils.getMiddlemanFor(ruleContext, ":libc_top")) .build(); } private NestedSet fullInputsForLink(RuleContext ruleContext, NestedSet link) { return NestedSetBuilder.stableOrder() .addTransitive(link) .addTransitive(AnalysisUtils.getMiddlemanFor(ruleContext, ":libc_top")) .add(ruleContext.getAnalysisEnvironment().getEmbeddedToolArtifact( CppRuleClasses.BUILD_INTERFACE_SO)) .build(); } private CppModuleMap createCrosstoolModuleMap(RuleContext ruleContext) { if (ruleContext.getPrerequisite("module_map", Mode.HOST) == null) { return null; } Artifact moduleMapArtifact = ruleContext.getPrerequisiteArtifact("module_map", Mode.HOST); if (moduleMapArtifact == null) { return null; } return new CppModuleMap(moduleMapArtifact, "crosstool"); } private TransitiveInfoCollection selectDep( RuleContext ruleContext, String attribute, Label label) { for (TransitiveInfoCollection dep : ruleContext.getPrerequisites(attribute, Mode.TARGET)) { if (dep.getLabel().equals(label)) { return dep; } } return ruleContext.getPrerequisites(attribute, Mode.TARGET).get(0); } private NestedSet getFiles(RuleContext context, String attribute) { TransitiveInfoCollection dep = context.getPrerequisite(attribute, Mode.HOST); MiddlemanProvider middlemanProvider = dep.getProvider(MiddlemanProvider.class); // We use the middleman if we can (if the dep is a filegroup), otherwise, just the regular // filesToBuild (e.g. if it is a simple input file) return middlemanProvider != null ? middlemanProvider.getMiddlemanArtifact() : dep.getProvider(FileProvider.class).getFilesToBuild(); } /** * Returns a map that should be templated into the crosstool as build variables. Is meant to * be overridden by subclasses of CcToolchain. */ protected Map getBuildVariables(RuleContext ruleContext) { return ImmutableMap.of(); } }