// 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.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap.Builder; import com.google.devtools.build.lib.actions.Artifact; 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.platform.ToolchainInfo; 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.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables; import com.google.devtools.build.lib.rules.cpp.CppConfiguration.Tool; import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain; import java.util.Map; import javax.annotation.Nullable; /** Information about a C++ compiler used by the cc_* rules. */ @SkylarkModule(name = "CcToolchainInfo", doc = "Information about the C++ compiler being used.") @Immutable public final class CcToolchainProvider extends ToolchainInfo { public static final String SKYLARK_NAME = "CcToolchainInfo"; /** An empty toolchain to be returned in the error case (instead of null). */ public static final CcToolchainProvider EMPTY_TOOLCHAIN_IS_ERROR = new CcToolchainProvider( ImmutableMap.of(), null, null, null, null, NestedSetBuilder.emptySet(Order.STABLE_ORDER), NestedSetBuilder.emptySet(Order.STABLE_ORDER), NestedSetBuilder.emptySet(Order.STABLE_ORDER), NestedSetBuilder.emptySet(Order.STABLE_ORDER), NestedSetBuilder.emptySet(Order.STABLE_ORDER), NestedSetBuilder.emptySet(Order.STABLE_ORDER), null, NestedSetBuilder.emptySet(Order.STABLE_ORDER), NestedSetBuilder.emptySet(Order.STABLE_ORDER), NestedSetBuilder.emptySet(Order.STABLE_ORDER), NestedSetBuilder.emptySet(Order.STABLE_ORDER), null, NestedSetBuilder.emptySet(Order.STABLE_ORDER), null, PathFragment.EMPTY_FRAGMENT, CppCompilationContext.EMPTY, false, false, Variables.EMPTY, ImmutableList.of(), NestedSetBuilder.>emptySet(Order.COMPILE_ORDER), null, null, ImmutableMap.of(), ImmutableList.of(), null); @Nullable private final CppConfiguration cppConfiguration; private final CToolchain toolchain; private final CppToolchainInfo toolchainInfo; private final PathFragment crosstoolTopPathFragment; private final NestedSet crosstool; private final NestedSet crosstoolMiddleman; private final NestedSet compile; private final NestedSet strip; private final NestedSet objCopy; private final NestedSet link; private final Artifact interfaceSoBuilder; private final NestedSet dwp; private final NestedSet coverage; private final NestedSet libcLink; private final NestedSet staticRuntimeLinkInputs; @Nullable private final Artifact staticRuntimeLinkMiddleman; private final NestedSet dynamicRuntimeLinkInputs; @Nullable private final Artifact dynamicRuntimeLinkMiddleman; private final PathFragment dynamicRuntimeSolibDir; private final CppCompilationContext cppCompilationContext; private final boolean supportsParamFiles; private final boolean supportsHeaderParsing; private final Variables buildVariables; private final ImmutableList builtinIncludeFiles; private final NestedSet> coverageEnvironment; @Nullable private final Artifact linkDynamicLibraryTool; @Nullable private final Artifact defParser; private final ImmutableMap environment; private final ImmutableList builtInIncludeDirectories; @Nullable private final PathFragment sysroot; public CcToolchainProvider( ImmutableMap skylarkToolchain, @Nullable CppConfiguration cppConfiguration, CToolchain toolchain, CppToolchainInfo toolchainInfo, PathFragment crosstoolTopPathFragment, NestedSet crosstool, NestedSet crosstoolMiddleman, NestedSet compile, NestedSet strip, NestedSet objCopy, NestedSet link, Artifact interfaceSoBuilder, NestedSet dwp, NestedSet coverage, NestedSet libcLink, NestedSet staticRuntimeLinkInputs, @Nullable Artifact staticRuntimeLinkMiddleman, NestedSet dynamicRuntimeLinkInputs, @Nullable Artifact dynamicRuntimeLinkMiddleman, PathFragment dynamicRuntimeSolibDir, CppCompilationContext cppCompilationContext, boolean supportsParamFiles, boolean supportsHeaderParsing, Variables buildVariables, ImmutableList builtinIncludeFiles, NestedSet> coverageEnvironment, Artifact linkDynamicLibraryTool, Artifact defParser, ImmutableMap environment, ImmutableList builtInIncludeDirectories, @Nullable PathFragment sysroot) { super(skylarkToolchain, Location.BUILTIN); this.cppConfiguration = cppConfiguration; this.toolchain = toolchain; this.toolchainInfo = toolchainInfo; this.crosstoolTopPathFragment = crosstoolTopPathFragment; this.crosstool = Preconditions.checkNotNull(crosstool); this.crosstoolMiddleman = Preconditions.checkNotNull(crosstoolMiddleman); this.compile = Preconditions.checkNotNull(compile); this.strip = Preconditions.checkNotNull(strip); this.objCopy = Preconditions.checkNotNull(objCopy); this.link = Preconditions.checkNotNull(link); this.interfaceSoBuilder = interfaceSoBuilder; this.dwp = Preconditions.checkNotNull(dwp); this.coverage = Preconditions.checkNotNull(coverage); this.libcLink = Preconditions.checkNotNull(libcLink); this.staticRuntimeLinkInputs = Preconditions.checkNotNull(staticRuntimeLinkInputs); this.staticRuntimeLinkMiddleman = staticRuntimeLinkMiddleman; this.dynamicRuntimeLinkInputs = Preconditions.checkNotNull(dynamicRuntimeLinkInputs); this.dynamicRuntimeLinkMiddleman = dynamicRuntimeLinkMiddleman; this.dynamicRuntimeSolibDir = Preconditions.checkNotNull(dynamicRuntimeSolibDir); this.cppCompilationContext = Preconditions.checkNotNull(cppCompilationContext); this.supportsParamFiles = supportsParamFiles; this.supportsHeaderParsing = supportsHeaderParsing; this.buildVariables = buildVariables; this.builtinIncludeFiles = builtinIncludeFiles; this.coverageEnvironment = coverageEnvironment; this.linkDynamicLibraryTool = linkDynamicLibraryTool; this.defParser = defParser; this.environment = environment; this.builtInIncludeDirectories = builtInIncludeDirectories; this.sysroot = sysroot; } /** Returns c++ Make variables. */ public static Map getCppBuildVariables( Function getToolPathFragment, String targetLibc, String compiler, String targetCpu, PathFragment crosstoolTopPathFragment, String abiGlibcVersion, String abi, Map additionalMakeVariables) { ImmutableMap.Builder result = ImmutableMap.builder(); // hardcoded CC->gcc setting for unit tests result.put("CC", getToolPathFragment.apply(Tool.GCC).getPathString()); // Make variables provided by crosstool/gcc compiler suite. result.put("AR", getToolPathFragment.apply(Tool.AR).getPathString()); result.put("NM", getToolPathFragment.apply(Tool.NM).getPathString()); result.put("LD", getToolPathFragment.apply(Tool.LD).getPathString()); PathFragment objcopyTool = getToolPathFragment.apply(Tool.OBJCOPY); if (objcopyTool != null) { // objcopy is optional in Crosstool result.put("OBJCOPY", objcopyTool.getPathString()); } result.put("STRIP", getToolPathFragment.apply(Tool.STRIP).getPathString()); PathFragment gcovtool = getToolPathFragment.apply(Tool.GCOVTOOL); if (gcovtool != null) { // gcov-tool is optional in Crosstool result.put("GCOVTOOL", gcovtool.getPathString()); } if (targetLibc.startsWith("glibc-")) { result.put("GLIBC_VERSION", targetLibc.substring("glibc-".length())); } else { result.put("GLIBC_VERSION", targetLibc); } result.put("C_COMPILER", compiler); result.put("TARGET_CPU", targetCpu); // Deprecated variables // TODO(bazel-team): delete all of these. result.put("CROSSTOOLTOP", crosstoolTopPathFragment.getPathString()); // TODO(kmensah): Remove when skylark dependencies can be updated to rely on // CcToolchainProvider. result.putAll(additionalMakeVariables); result.put("ABI_GLIBC_VERSION", abiGlibcVersion); result.put("ABI", abi); return result.build(); } @Override public void addGlobalMakeVariables(Builder globalMakeEnvBuilder) { globalMakeEnvBuilder.putAll( getCppBuildVariables( this::getToolPathFragment, getTargetLibc(), getCompiler(), getTargetCpu(), crosstoolTopPathFragment, getAbiGlibcVersion(), getAbi(), getAdditionalMakeVariables())); } @SkylarkCallable( name = "built_in_include_directories", doc = "Returns the list of built-in directories of the compiler.", structField = true ) public ImmutableList getBuiltInIncludeDirectories() { return builtInIncludeDirectories; } /** Returns the {@link CToolchain} for this toolchain. */ public CToolchain getToolchain() { return toolchain; } /** * Returns all the files in Crosstool. Is not a middleman. */ public NestedSet getCrosstool() { return crosstool; } /** * Returns a middleman for all the files in Crosstool. */ public NestedSet getCrosstoolMiddleman() { return crosstoolMiddleman; } /** * Returns the files necessary for compilation. */ public NestedSet getCompile() { return compile; } /** * Returns the files necessary for a 'strip' invocation. */ public NestedSet getStrip() { return strip; } /** * Returns the files necessary for an 'objcopy' invocation. */ public NestedSet getObjcopy() { return objCopy; } /** * Returns the files necessary for linking, including the files needed for libc. */ public NestedSet getLink() { return link; } public NestedSet getDwp() { return dwp; } /** * Returns the files necessary for capturing code coverage. */ public NestedSet getCoverage() { return coverage; } public NestedSet getLibcLink() { return libcLink; } /** * Returns the static runtime libraries. */ public NestedSet getStaticRuntimeLinkInputs() { return staticRuntimeLinkInputs; } /** * Returns an aggregating middleman that represents the static runtime libraries. */ @Nullable public Artifact getStaticRuntimeLinkMiddleman() { return staticRuntimeLinkMiddleman; } /** * Returns the dynamic runtime libraries. */ public NestedSet getDynamicRuntimeLinkInputs() { return dynamicRuntimeLinkInputs; } /** * Returns an aggregating middleman that represents the dynamic runtime libraries. */ @Nullable public Artifact getDynamicRuntimeLinkMiddleman() { return dynamicRuntimeLinkMiddleman; } /** * Returns the name of the directory where the solib symlinks for the dynamic runtime libraries * live. The directory itself will be under the root of the host configuration in the 'bin' * directory. */ public PathFragment getDynamicRuntimeSolibDir() { return dynamicRuntimeSolibDir; } /** * Returns the C++ compilation context for the toolchain. */ public CppCompilationContext getCppCompilationContext() { return cppCompilationContext; } /** * Whether the toolchains supports parameter files. */ public boolean supportsParamFiles() { return supportsParamFiles; } /** * Whether the toolchains supports header parsing. */ public boolean supportsHeaderParsing() { return supportsHeaderParsing; } /** * Returns the configured features of the toolchain. */ @Nullable public CcToolchainFeatures getFeatures() { return toolchainInfo.getFeatures(); } /** * Returns whether shared libraries must be compiled with position independent code on this * platform. */ public boolean toolchainNeedsPic() { return toolchainInfo.toolchainNeedsPic(); } /** * 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 null. */ public PathFragment getRuntimeSysroot() { return toolchainInfo.getRuntimeSysroot(); } /** * 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_}. */ public String getSolibDirectory() { return toolchainInfo.getSolibDirectory(); } /** * Returns the compilation mode. */ @Nullable public CompilationMode getCompilationMode() { return cppConfiguration == null ? null : cppConfiguration.getCompilationMode(); } /** * Returns whether the toolchain supports the gold linker. */ public boolean supportsGoldLinker() { return toolchainInfo.supportsGoldLinker(); } /** * Returns whether the toolchain supports dynamic linking. */ public boolean supportsDynamicLinker() { return toolchainInfo.supportsDynamicLinker(); } /** * Returns whether the toolchain supports linking C/C++ runtime libraries * supplied inside the toolchain distribution. */ public boolean supportsEmbeddedRuntimes() { return toolchainInfo.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 toolchainInfo.supportsEmbeddedRuntimes(); } @Nullable public CppConfiguration getCppConfiguration() { return cppConfiguration; } /** Returns build variables to be templated into the crosstool. */ public Variables getBuildVariables() { return buildVariables; } /** * Return the set of include files that may be included even if they are not mentioned in the * source file or any of the headers included by it. */ public ImmutableList getBuiltinIncludeFiles() { return builtinIncludeFiles; } /** * Returns the environment variables that need to be added to tests that collect code coverage. */ public NestedSet> getCoverageEnvironment() { return coverageEnvironment; } public ImmutableMap getEnvironment() { return environment; } /** * Returns the tool which should be used for linking dynamic libraries, or in case it's not * specified by the crosstool this will be @tools_repository/tools/cpp:link_dynamic_library */ public Artifact getLinkDynamicLibraryTool() { return linkDynamicLibraryTool; } /** * Returns the tool which should be used to parser object files for generating DEF file on * Windows. The label of this tool is //third_party/def_parser:def_parser. */ public Artifact getDefParserTool() { return defParser; } /** * Returns the tool that builds interface libraries from dynamic libraries. */ public Artifact getInterfaceSoBuilder() { return interfaceSoBuilder; } @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 None." ) public PathFragment getSysroot() { return sysroot; } /** * Returns the path fragment that is either absolute or relative to the execution root that can be * used to execute the given tool. */ public PathFragment getToolPathFragment(CppConfiguration.Tool tool) { return toolchainInfo.getToolPathFragment(tool); } /** * 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 toolchainInfo.getAbi(); } /** * 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 toolchainInfo.getAbiGlibcVersion(); } /** * 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 toolchainInfo.getStaticRuntimeLibsLabel(); } /** * 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 toolchainInfo.getDynamicRuntimeLibsLabel(); } /** Returns the compiler version string (e.g. "gcc-4.1.1"). */ @SkylarkCallable(name = "compiler", structField = true, doc = "C++ compiler.") public String getCompiler() { return toolchainInfo.getCompiler(); } /** Returns the libc version string (e.g. "glibc-2.2.2"). */ @SkylarkCallable(name = "libc", structField = true, doc = "libc version string.") public String getTargetLibc() { return toolchainInfo.getTargetLibc(); } /** 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 toolchainInfo.getTargetCpu(); } /** * 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. * *

The returned map must contain an entry for {@code STACK_FRAME_UNLIMITED}, though the entry * may be an empty string. */ public ImmutableMap getAdditionalMakeVariables() { return toolchainInfo.getAdditionalMakeVariables(); } @SkylarkCallable( name = "unfiltered_compiler_options_do_not_use", 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 getUnfilteredCompilerOptionsWithSysroot(Iterable features) { return cppConfiguration.getUnfilteredCompilerOptionsDoNotUse(features, sysroot); } public ImmutableList getUnfilteredCompilerOptions(Iterable features) { return cppConfiguration.getUnfilteredCompilerOptionsDoNotUse(features, /* sysroot= */ null); } @SkylarkCallable( name = "link_options_do_not_use", structField = true, doc = "Returns the set of command-line linker options, including any flags " + "inferred from the command-line options." ) public ImmutableList getLinkOptionsWithSysroot() { return cppConfiguration.getLinkOptionsDoNotUse(sysroot); } public ImmutableList getLinkOptions() { return cppConfiguration.getLinkOptionsDoNotUse(/* sysroot= */ null); } /** * Returns test-only link options such that certain test-specific features can be configured * separately (e.g. lazy binding). */ public ImmutableList getTestOnlyLinkOptions() { return toolchainInfo.getTestOnlyLinkOptions(); } /** Returns the system name which is required by the toolchain to run. */ public String getHostSystemName() { return toolchainInfo.getHostSystemName(); } /** * 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 getObjCopyOptionsForEmbedding() { return toolchainInfo.getObjCopyOptionsForEmbedding(); } /** * 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 getLdOptionsForEmbedding() { return toolchainInfo.getLdOptionsForEmbedding(); } /** Returns the GNU System Name */ @SkylarkCallable( name = "target_gnu_system_name", structField = true, doc = "The GNU System Name." ) public String getTargetGnuSystemName() { return toolchainInfo.getTargetGnuSystemName(); } /** Returns the architecture component of the GNU System Name */ public String getGnuSystemArch() { return toolchainInfo.getGnuSystemArch(); } public final boolean isLLVMCompiler() { return toolchainInfo.isLLVMCompiler(); } // Not all of CcToolchainProvider is exposed to Skylark, which makes implementing deep equality // impossible: if Java-only parts are considered, the behavior is surprising in Skylark, if they // are not, the behavior is surprising in Java. Thus, object identity it is. @Override public boolean equals(Object other) { return other == this; } @Override public int hashCode() { return System.identityHashCode(this); } }