| // 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.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.analysis.OutputGroupInfo; |
| import com.google.devtools.build.lib.analysis.PackageSpecificationProvider; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.collect.nestedset.Depset; |
| import com.google.devtools.build.lib.collect.nestedset.Depset.TypeException; |
| 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.packages.Info; |
| import com.google.devtools.build.lib.packages.Provider; |
| import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; |
| import com.google.devtools.build.lib.packages.StarlarkInfo; |
| import com.google.devtools.build.lib.packages.StarlarkInfoWithSchema; |
| import com.google.devtools.build.lib.packages.StarlarkProviderWrapper; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; |
| import com.google.devtools.build.lib.rules.cpp.CppConfiguration.Tool; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import javax.annotation.Nullable; |
| import net.starlark.java.eval.Dict; |
| import net.starlark.java.eval.EvalException; |
| import net.starlark.java.eval.Sequence; |
| import net.starlark.java.eval.Starlark; |
| import net.starlark.java.syntax.Location; |
| |
| /** Information about a C++ compiler used by the <code>cc_*</code> rules. */ |
| @Immutable |
| public final class CcToolchainProvider { |
| |
| public static final String STARLARK_NAME = "CcToolchainInfo"; |
| public static final CcToolchainInfoProvider PROVIDER = new CcToolchainInfoProvider(); |
| |
| /** Provider class for {@link CcToolchainProvider} objects. */ |
| public static class CcToolchainInfoProvider extends StarlarkProviderWrapper<CcToolchainProvider> |
| implements Provider { |
| public CcToolchainInfoProvider() { |
| super( |
| Label.parseCanonicalUnchecked("@_builtins//:common/cc/cc_toolchain_info.bzl"), |
| STARLARK_NAME); |
| } |
| |
| public CcToolchainProvider wrapOrThrowEvalException(Info value) throws EvalException { |
| if (value instanceof StarlarkInfoWithSchema |
| && value.getProvider().getKey().equals(getKey())) { |
| return new CcToolchainProvider((StarlarkInfo) value); |
| } else { |
| throw new EvalException( |
| String.format("got value of type '%s', want 'CcToolchainInfo'", Starlark.type(value))); |
| } |
| } |
| |
| @Override |
| public CcToolchainProvider wrap(Info value) throws RuleErrorException { |
| if (value instanceof StarlarkInfoWithSchema |
| && value.getProvider().getKey().equals(getKey())) { |
| return new CcToolchainProvider((StarlarkInfo) value); |
| } else { |
| throw new RuleErrorException( |
| "got value of type '" + Starlark.type(value) + "', want 'CcToolchainInfo'"); |
| } |
| } |
| |
| @Override |
| public boolean isExported() { |
| return true; |
| } |
| |
| @Override |
| public String getPrintableName() { |
| return STARLARK_NAME; |
| } |
| |
| @Override |
| public Location getLocation() { |
| return Location.BUILTIN; |
| } |
| } |
| |
| @Nullable |
| private static final NestedSet<Artifact> nullOrDepset(StarlarkInfo value, String key) |
| throws EvalException, TypeException { |
| if (value.getValue(key) == null || value.getValue(key) == Starlark.NONE) { |
| return null; |
| } |
| return value.getValue(key, Depset.class).getSet(Artifact.class); |
| } |
| |
| @Nullable |
| private static final PathFragment nullOrPathFragment(StarlarkInfo value, String key) |
| throws EvalException { |
| if (value.getValue(key) == null || value.getValue(key) == Starlark.NONE) { |
| return null; |
| } |
| return PathFragment.create(value.getValue(key, String.class)); |
| } |
| |
| private static final ImmutableList<PathFragment> convertStarlarkListToPathFragments( |
| StarlarkInfo value, String key) throws EvalException { |
| ImmutableList.Builder<PathFragment> pathFragments = ImmutableList.builder(); |
| for (String pathString : |
| Sequence.cast(value.getValue(key, Sequence.class), String.class, key)) { |
| pathFragments.add(PathFragment.create(pathString)); |
| } |
| return pathFragments.build(); |
| } |
| |
| private final StarlarkInfo value; |
| |
| private CcToolchainProvider(StarlarkInfo value) { |
| this.value = value; |
| } |
| |
| @VisibleForTesting |
| public StarlarkInfo getValue() { |
| return value; |
| } |
| |
| public static CcToolchainProvider create(StarlarkInfo value) { |
| return new CcToolchainProvider(value); |
| } |
| |
| /** |
| * Determines if we should apply -fPIC for this rule's C++ compilations. This determination is |
| * generally made by the global C++ configuration settings "needsPic" and "usePicForBinaries". |
| * However, an individual rule may override these settings by applying -fPIC" to its "nocopts" |
| * attribute. This allows incompatible rules to "opt out" of global PIC settings (see bug: |
| * "Provide a way to turn off -fPIC for targets that can't be built that way"). |
| * |
| * @return true if this rule's compilations should apply -fPIC, false otherwise |
| */ |
| public static boolean usePicForDynamicLibraries( |
| CppConfiguration cppConfiguration, FeatureConfiguration featureConfiguration) { |
| return cppConfiguration.forcePic() |
| || featureConfiguration.isEnabled(CppRuleClasses.SUPPORTS_PIC); |
| } |
| |
| /** |
| * Returns true if PER_OBJECT_DEBUG_INFO are specified and supported by the CROSSTOOL for the |
| * build implied by the given configuration, toolchain and feature configuration. |
| */ |
| public static boolean shouldCreatePerObjectDebugInfo( |
| FeatureConfiguration featureConfiguration, CppConfiguration cppConfiguration) { |
| return cppConfiguration.fissionIsActiveForCurrentCompilationMode() |
| && featureConfiguration.isEnabled(CppRuleClasses.PER_OBJECT_DEBUG_INFO); |
| } |
| |
| /** Whether the toolchains supports header parsing. */ |
| public boolean supportsHeaderParsing() throws EvalException { |
| return value.getValue("_supports_header_parsing", Boolean.class); |
| } |
| |
| /** |
| * Returns true if headers should be parsed in this build. |
| * |
| * <p>This means headers in 'srcs' and 'hdrs' will be "compiled" using {@link CppCompileAction}). |
| * It will run compiler's parser to ensure the header is self-contained. This is required for |
| * layering_check to work. |
| */ |
| public static boolean shouldProcessHeaders( |
| FeatureConfiguration featureConfiguration, CppConfiguration cppConfiguration) { |
| return featureConfiguration.isEnabled(CppRuleClasses.PARSE_HEADERS); |
| } |
| |
| /** |
| * Returns the path String that is either absolute or relative to the execution root that can be |
| * used to execute the given tool. |
| * |
| * @throws RuleErrorException when the tool is not specified by the toolchain. |
| */ |
| public static String getToolPathString( |
| ImmutableMap<String, String> toolPaths, |
| CppConfiguration.Tool tool, |
| Label ccToolchainLabel, |
| String toolchainIdentifier) |
| throws EvalException { |
| String toolPath = getToolPathStringOrNull(toolPaths, tool); |
| if (toolPath == null) { |
| throw Starlark.errorf( |
| "cc_toolchain '%s' with identifier '%s' doesn't define a tool path for '%s'", |
| ccToolchainLabel, toolchainIdentifier, tool.getNamePart()); |
| } |
| return toolPath; |
| } |
| |
| /** |
| * Returns the path string that is either absolute or relative to the execution root that can be |
| * used to execute the given tool. |
| */ |
| public static String getToolPathStringOrNull(ImmutableMap<String, String> toolPaths, Tool tool) { |
| return toolPaths.get(tool.getNamePart()); |
| } |
| |
| public ImmutableMap<String, String> getToolPaths() throws EvalException { |
| return ImmutableMap.copyOf( |
| Dict.cast( |
| value.getValue("_tool_paths", Dict.class), String.class, String.class, "_tool_paths")); |
| } |
| |
| public ImmutableList<PathFragment> getBuiltInIncludeDirectories() throws EvalException { |
| return convertStarlarkListToPathFragments(value, "built_in_include_directories"); |
| } |
| |
| /** Returns the identifier of the toolchain as specified in the {@code CToolchain} proto. */ |
| public String getToolchainIdentifier() throws EvalException { |
| return value.getValue("toolchain_id", String.class); |
| } |
| |
| /** Returns all the files in Crosstool. */ |
| public NestedSet<Artifact> getAllFiles() throws EvalException { |
| try { |
| return value.getValue("all_files", Depset.class).getSet(Artifact.class); |
| } catch (TypeException e) { |
| throw new EvalException(e); |
| } |
| } |
| |
| /** Returns all the files in Crosstool + libc. */ |
| public NestedSet<Artifact> getAllFilesIncludingLibc() throws EvalException { |
| try { |
| return value.getValue("_all_files_including_libc", Depset.class).getSet(Artifact.class); |
| } catch (TypeException e) { |
| throw new EvalException(e); |
| } |
| } |
| |
| /** Returns the files necessary for compilation. */ |
| public NestedSet<Artifact> getCompilerFiles() throws EvalException { |
| try { |
| return value.getValue("_compiler_files", Depset.class).getSet(Artifact.class); |
| } catch (TypeException e) { |
| throw new EvalException(e); |
| } |
| } |
| |
| /** |
| * Returns the files necessary for compilation excluding headers, assuming that included files |
| * will be discovered by input discovery. |
| */ |
| public NestedSet<Artifact> getCompilerFilesWithoutIncludes() throws EvalException { |
| try { |
| return value |
| .getValue("_compiler_files_without_includes", Depset.class) |
| .getSet(Artifact.class); |
| } catch (TypeException e) { |
| throw new EvalException(e); |
| } |
| } |
| |
| /** |
| * Returns the files necessary for an 'as' invocation. May be empty if the CROSSTOOL file does not |
| * define as_files. |
| */ |
| public NestedSet<Artifact> getAsFiles() throws EvalException { |
| try { |
| return value.getValue("_as_files", Depset.class).getSet(Artifact.class); |
| } catch (TypeException e) { |
| throw new EvalException(e); |
| } |
| } |
| |
| /** |
| * Returns the files necessary for an 'ar' invocation. May be empty if the CROSSTOOL file does not |
| * define ar_files. |
| */ |
| public NestedSet<Artifact> getArFiles() throws EvalException, TypeException { |
| return value.getValue("_ar_files", Depset.class).getSet(Artifact.class); |
| } |
| |
| /** Returns the files necessary for linking, including the files needed for libc. */ |
| public NestedSet<Artifact> getLinkerFiles() throws EvalException, TypeException { |
| return value.getValue("_linker_files", Depset.class).getSet(Artifact.class); |
| } |
| |
| /** Returns the files necessary for capturing code coverage. */ |
| @VisibleForTesting |
| public NestedSet<Artifact> getCoverageFiles() throws EvalException, TypeException { |
| return value.getValue("_coverage_files", Depset.class).getSet(Artifact.class); |
| } |
| |
| /** |
| * Returns true if the featureConfiguration includes statically linking the cpp runtimes. |
| * |
| * @param featureConfiguration the relevant FeatureConfiguration. |
| */ |
| private static boolean shouldStaticallyLinkCppRuntimes( |
| FeatureConfiguration featureConfiguration) { |
| return featureConfiguration.isEnabled(CppRuleClasses.STATIC_LINK_CPP_RUNTIMES); |
| } |
| |
| @Nullable |
| public NestedSet<Artifact> getStaticRuntimeLinkInputs() throws EvalException, TypeException { |
| return nullOrDepset(value, "_static_runtime_lib_depset"); |
| } |
| |
| /** Returns the static runtime libraries. */ |
| public static NestedSet<Artifact> getStaticRuntimeLinkInputsOrThrowError( |
| NestedSet<Artifact> staticRuntimeLinkInputs, FeatureConfiguration featureConfiguration) |
| throws EvalException { |
| if (shouldStaticallyLinkCppRuntimes(featureConfiguration)) { |
| if (staticRuntimeLinkInputs == null) { |
| throw Starlark.errorf( |
| "Toolchain supports embedded runtimes, but didn't provide static_runtime_lib" |
| + " attribute."); |
| } |
| return staticRuntimeLinkInputs; |
| } else { |
| return NestedSetBuilder.emptySet(Order.STABLE_ORDER); |
| } |
| } |
| |
| @Nullable |
| public NestedSet<Artifact> getDynamicRuntimeLinkInputs() throws EvalException, TypeException { |
| return nullOrDepset(value, "_dynamic_runtime_lib_depset"); |
| } |
| |
| /** Returns the dynamic runtime libraries. */ |
| public static NestedSet<Artifact> getDynamicRuntimeLinkInputsOrThrowError( |
| NestedSet<Artifact> dynamicRuntimeLinkInputs, FeatureConfiguration featureConfiguration) |
| throws EvalException { |
| if (shouldStaticallyLinkCppRuntimes(featureConfiguration)) { |
| if (dynamicRuntimeLinkInputs == null) { |
| throw new EvalException( |
| "Toolchain supports embedded runtimes, but didn't provide dynamic_runtime_lib" |
| + " attribute."); |
| } |
| return dynamicRuntimeLinkInputs; |
| } else { |
| return NestedSetBuilder.emptySet(Order.STABLE_ORDER); |
| } |
| } |
| |
| /** |
| * 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 exec configuration in the 'bin' |
| * directory. |
| */ |
| public PathFragment getDynamicRuntimeSolibDir() throws EvalException { |
| return PathFragment.create(value.getValue("dynamic_runtime_solib_dir", String.class)); |
| } |
| |
| /** Returns the {@code CcInfo} for the toolchain. */ |
| public CcInfo getCcInfo() throws EvalException { |
| return value.getValue("_cc_info", CcInfo.class); |
| } |
| |
| /** Whether the toolchains supports parameter files. */ |
| public boolean supportsParamFiles() throws EvalException { |
| return value.getValue("_supports_param_files", Boolean.class); |
| } |
| |
| /** Returns the configured features of the toolchain. */ |
| @Nullable |
| public CcToolchainFeatures getFeatures() throws EvalException { |
| return value.getValue("_toolchain_features", CcToolchainFeatures.class); |
| } |
| |
| public Label getCcToolchainLabel() throws EvalException { |
| return value.getValue("_toolchain_label", Label.class); |
| } |
| |
| /** |
| * 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() throws EvalException { |
| return value.getValue("_solib_dir", String.class); |
| } |
| |
| /** Returns whether this toolchain supports interface shared libraries. */ |
| // TODO(gnish): Move this to FeatureConfiguration. |
| public static boolean supportsInterfaceSharedLibraries( |
| FeatureConfiguration featureConfiguration) { |
| return featureConfiguration.isEnabled(CppRuleClasses.SUPPORTS_INTERFACE_SHARED_LIBRARIES); |
| } |
| |
| /** Return context-sensitive fdo instrumentation path. */ |
| public String getCSFdoInstrument() throws EvalException { |
| CppConfiguration cppConfiguration = |
| value.getValue("_cpp_configuration", CppConfiguration.class); |
| return cppConfiguration.getCSFdoInstrument(); |
| } |
| |
| public CcToolchainVariables getBuildVars() throws EvalException { |
| return getValue().getValue("_build_variables", CcToolchainVariables.class); |
| } |
| |
| /** |
| * 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<Artifact> getBuiltinIncludeFiles() throws EvalException { |
| return Sequence.cast( |
| value.getValue("_builtin_include_files", Sequence.class), |
| Artifact.class, |
| "_builtin_include_files") |
| .getImmutableList(); |
| } |
| |
| /** |
| * 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() throws EvalException { |
| return value.getValue("_link_dynamic_library_tool", Artifact.class); |
| } |
| |
| /** Returns the grep-includes tool which is needing during linking because of linkstamping. */ |
| @Nullable |
| public Artifact getGrepIncludes() throws EvalException { |
| return value.getNoneableValue("_grep_includes", Artifact.class); |
| } |
| |
| /** Returns the tool that builds interface libraries from dynamic libraries. */ |
| public Artifact getInterfaceSoBuilder() throws EvalException { |
| return value.getValue("_if_so_builder", Artifact.class); |
| } |
| |
| @Nullable |
| public String getSysroot() throws EvalException { |
| PathFragment sysroot = nullOrPathFragment(value, "sysroot"); |
| return sysroot != null ? sysroot.getPathString() : null; |
| } |
| |
| @Nullable |
| public PathFragment getSysrootPathFragment() throws EvalException { |
| return nullOrPathFragment(value, "sysroot"); |
| } |
| |
| /** |
| * 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. |
| @VisibleForTesting |
| public String getAbi() throws EvalException { |
| return value.getValue("_abi", String.class); |
| } |
| |
| /** Returns the target architecture using blaze-specific constants (e.g. "piii"). */ |
| public String getTargetCpu() throws EvalException { |
| return value.getValue("cpu", String.class); |
| } |
| |
| /** |
| * Returns the legacy value of the CC_FLAGS Make variable. |
| * |
| * @deprecated Use the CC_FLAGS from feature configuration instead. |
| */ |
| // TODO(b/65151735): Remove when cc_flags is entirely from features. |
| @Deprecated |
| public String getLegacyCcFlagsMakeVariable() throws EvalException { |
| return value.getValue("_legacy_cc_flags_make_variable", String.class); |
| } |
| |
| public FdoContext getFdoContext() throws EvalException { |
| return value.getValue("_fdo_context", FdoContext.class); |
| } |
| |
| // Not all of CcToolchainProvider is exposed to Starlark, which makes implementing deep equality |
| // impossible: if Java-only parts are considered, the behavior is surprising in Starlark, 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); |
| } |
| |
| public boolean isToolConfiguration() throws EvalException { |
| return value.getValue("_is_tool_configuration", Boolean.class); |
| } |
| |
| public PackageSpecificationProvider getAllowlistForLayeringCheck() throws EvalException { |
| return value.getValue("_allowlist_for_layering_check", PackageSpecificationProvider.class); |
| } |
| |
| public OutputGroupInfo getCcBuildInfoTranslator() throws EvalException { |
| return value.getValue("_build_info_files", OutputGroupInfo.class); |
| } |
| |
| public CppConfiguration getCppConfiguration() throws EvalException { |
| return value.getValue("_cpp_configuration", CppConfiguration.class); |
| } |
| } |