From c71157f9a826db25e2588a0147de76e97ff30eb2 Mon Sep 17 00:00:00 2001 From: hlopko Date: Thu, 28 Jun 2018 05:38:50 -0700 Subject: Expose Skylark API to the C++ toolchain This is an implementation of the design at https://docs.google.com/document/d/1g91BWJITcYw_X-VxsDC0VgUn5E9g0kRBGoBSpoO41gA/edit>. More thorough documentation will be sent in a separate cl. The api was approved at https://docs.google.com/document/d/1M8JA7kzZnWpLZ3WEX9rp6k2u_nlwE8smsHYgVTSSJ9k/edit?ts=5b292400#. Work towards #4571 (only the docs are missing). RELNOTES: None. PiperOrigin-RevId: 202464331 --- .../devtools/build/lib/rules/cpp/CcModule.java | 167 ++++- .../build/lib/skylarkbuildapi/cpp/CcModuleApi.java | 432 ++++++++++- .../com/google/devtools/build/lib/rules/cpp/BUILD | 1 + .../build/lib/rules/cpp/SkylarkCcCommonTest.java | 824 +++++++++++++++++++++ 4 files changed, 1411 insertions(+), 13 deletions(-) create mode 100644 src/test/java/com/google/devtools/build/lib/rules/cpp/SkylarkCcCommonTest.java diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java index d6b450debd..0e7eca0a51 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java @@ -14,15 +14,180 @@ package com.google.devtools.build.lib.rules.cpp; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.analysis.platform.ToolchainInfo; +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.Provider; +import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcModuleApi; +import com.google.devtools.build.lib.syntax.EvalException; +import com.google.devtools.build.lib.syntax.EvalUtils; +import com.google.devtools.build.lib.syntax.SkylarkDict; +import com.google.devtools.build.lib.syntax.SkylarkList; +import com.google.devtools.build.lib.syntax.SkylarkNestedSet; +import javax.annotation.Nullable; /** A module that contains Skylark utilities for C++ support. */ -public class CcModule implements CcModuleApi { +public class CcModule + implements CcModuleApi { @Override public Provider getCcToolchainProvider() { return ToolchainInfo.PROVIDER; } + + @Override + public FeatureConfiguration configureFeatures( + CcToolchainProvider toolchain, + SkylarkList requestedFeatures, + SkylarkList unsupportedFeatures) + throws EvalException { + return CcCommon.configureFeaturesOrThrowEvalException( + ImmutableSet.copyOf(requestedFeatures), + ImmutableSet.copyOf(unsupportedFeatures), + toolchain); + } + + @Override + public String getToolForAction(FeatureConfiguration featureConfiguration, String actionName) { + return featureConfiguration + .getToolForAction(actionName) + .getToolPathFragment() + .getSafePathString(); + } + + @Override + public boolean isEnabled(FeatureConfiguration featureConfiguration, String featureName) { + return featureConfiguration.isEnabled(featureName); + } + + @Override + public SkylarkList getCommandLine( + FeatureConfiguration featureConfiguration, + String actionName, + CcToolchainVariables variables) { + return SkylarkList.createImmutable(featureConfiguration.getCommandLine(actionName, variables)); + } + + @Override + public SkylarkDict getEnvironmentVariable( + FeatureConfiguration featureConfiguration, + String actionName, + CcToolchainVariables variables) { + return SkylarkDict.copyOf( + null, featureConfiguration.getEnvironmentVariables(actionName, variables)); + } + + @Override + public CcToolchainVariables getCompileBuildVariables( + CcToolchainProvider ccToolchainProvider, + FeatureConfiguration featureConfiguration, + Object sourceFile, + Object outputFile, + Object userCompileFlags, + Object includeDirs, + Object quoteIncludeDirs, + Object systemIncludeDirs, + Object defines, + boolean usePic, + boolean addLegacyCxxOptions) + throws EvalException { + return CompileBuildVariables.setupVariablesOrThrowEvalException( + featureConfiguration, + ccToolchainProvider, + convertFromNoneable(sourceFile, /* defaultValue= */ null), + convertFromNoneable(outputFile, /* defaultValue= */ null), + /* gcnoFile= */ null, + /* dwoFile= */ null, + /* ltoIndexingFile= */ null, + /* includes= */ ImmutableList.of(), + asStringNestedSet(userCompileFlags), + /* cppModuleMap= */ null, + usePic, + /* fakeOutputFile= */ null, + /* fdoStamp= */ null, + /* dotdFileExecPath= */ null, + /* variablesExtensions= */ ImmutableList.of(), + /* additionalBuildVariables= */ ImmutableMap.of(), + /* directModuleMaps= */ ImmutableList.of(), + asStringNestedSet(includeDirs), + asStringNestedSet(quoteIncludeDirs), + asStringNestedSet(systemIncludeDirs), + asStringNestedSet(defines), + addLegacyCxxOptions); + } + + @Override + public CcToolchainVariables getLinkBuildVariables( + CcToolchainProvider ccToolchainProvider, + FeatureConfiguration featureConfiguration, + Object librarySearchDirectories, + Object runtimeLibrarySearchDirectories, + Object userLinkFlags, + Object outputFile, + Object paramFile, + Object defFile, + boolean isUsingLinkerNotArchiver, + boolean isCreatingSharedLibrary, + boolean mustKeepDebug, + boolean useTestOnlyFlags, + boolean isStaticLinkingMode) + throws EvalException { + return LinkBuildVariables.setupVariables( + isUsingLinkerNotArchiver, + /* binDirectoryPath= */ null, + convertFromNoneable(outputFile, /* defaultValue= */ null), + isCreatingSharedLibrary, + convertFromNoneable(paramFile, /* defaultValue= */ null), + /* thinltoParamFile= */ null, + /* thinltoMergedObjectFile= */ null, + mustKeepDebug, + /* symbolCounts= */ null, + ccToolchainProvider, + featureConfiguration, + useTestOnlyFlags, + /* isLtoIndexing= */ false, + asStringNestedSet(userLinkFlags), + /* interfaceLibraryBuilder= */ null, + /* interfaceLibraryOutput= */ null, + /* ltoOutputRootPrefix= */ null, + convertFromNoneable(defFile, /* defaultValue= */ null), + /* fdoSupport= */ null, + asStringNestedSet(runtimeLibrarySearchDirectories), + /* librariesToLink= */ null, + asStringNestedSet(librarySearchDirectories), + /* isLegacyFullyStaticLinkingMode= */ false, + isStaticLinkingMode); + } + + @Override + public CcToolchainVariables getVariables() { + return CcToolchainVariables.EMPTY; + } + + /** + * Converts an object that can be the NoneType to the actual object if it is not or returns the + * default value if none. + */ + @SuppressWarnings("unchecked") + protected static T convertFromNoneable(Object obj, @Nullable T defaultValue) { + if (EvalUtils.isNullOrNone(obj)) { + return defaultValue; + } + return (T) obj; + } + + /** Converts an object that can be the either SkylarkNestedSet or None into NestedSet. */ + protected NestedSet asStringNestedSet(Object o) throws EvalException { + SkylarkNestedSet skylarkNestedSet = convertFromNoneable(o, /* defaultValue= */ null); + if (skylarkNestedSet != null) { + return skylarkNestedSet.getSet(String.class); + } else { + return NestedSetBuilder.emptySet(Order.STABLE_ORDER); + } + } } diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcModuleApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcModuleApi.java index a7732ffa85..73c5e498a8 100644 --- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcModuleApi.java +++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcModuleApi.java @@ -14,24 +14,34 @@ package com.google.devtools.build.lib.skylarkbuildapi.cpp; + import com.google.devtools.build.lib.skylarkbuildapi.ProviderApi; +import com.google.devtools.build.lib.skylarkinterface.Param; +import com.google.devtools.build.lib.skylarkinterface.ParamType; import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; +import com.google.devtools.build.lib.syntax.EvalException; +import com.google.devtools.build.lib.syntax.Runtime.NoneType; +import com.google.devtools.build.lib.syntax.SkylarkDict; +import com.google.devtools.build.lib.syntax.SkylarkList; +import com.google.devtools.build.lib.syntax.SkylarkNestedSet; -/** - * Utilies related to C++ support. - */ -@SkylarkModule(name = "cc_common", doc = "Utilities related to C++ support.") -public interface CcModuleApi { +/** Utilites related to C++ support. */ +@SkylarkModule( + name = "cc_common", + doc = "Utilities for C++ compilation, linking, and command line " + "generation.") +public interface CcModuleApi< + CcToolchainProviderT extends CcToolchainProviderApi, + FeatureConfigurationT extends FeatureConfigurationApi, + CcToolchainVariablesT extends CcToolchainVariablesApi> { @SkylarkCallable( - name = "CcToolchainInfo", - doc = - "The key used to retrieve the provider that contains information about the C++ " - + "toolchain being usCced", - structField = true - ) - public ProviderApi getCcToolchainProvider(); + name = "CcToolchainInfo", + doc = + "The key used to retrieve the provider that contains information about the C++ " + + "toolchain being usCced", + structField = true) + ProviderApi getCcToolchainProvider(); @Deprecated @SkylarkCallable( @@ -42,4 +52,402 @@ public interface CcModuleApi { + "config_settings.flag_values{'@bazel_tools//tools/cpp:compiler'}", structField = true) default void compilerFlagExists() {} + + @SkylarkCallable( + name = "configure_features", + doc = "Creates a feature_configuration instance.", + parameters = { + @Param( + name = "cc_toolchain", + doc = "cc_toolchain for which we configure features.", + positional = false, + named = true, + type = CcToolchainProviderApi.class), + @Param( + name = "requested_features", + doc = "List of features to be enabled.", + positional = false, + named = true, + defaultValue = "[]", + type = SkylarkList.class), + @Param( + name = "unsupported_features", + doc = "List of features that are unsupported by the current rule.", + positional = false, + named = true, + defaultValue = "[]", + type = SkylarkList.class), + }) + FeatureConfigurationT configureFeatures( + CcToolchainProviderT toolchain, + SkylarkList requestedFeatures, + SkylarkList unsupportedFeatures) + throws EvalException; + + @SkylarkCallable( + name = "get_tool_for_action", + doc = "Returns tool path for given action.", + parameters = { + @Param( + name = "feature_configuration", + doc = "Feature configuration to be queried.", + positional = false, + named = true, + type = FeatureConfigurationApi.class), + @Param( + name = "action_name", + doc = + "Name of the action. Has to be one of the names in " + + "@bazel_tools//tools/build_defs/cc:action_names.bzl.", + named = true, + positional = false), + }) + String getToolForAction(FeatureConfigurationT featureConfiguration, String actionName); + + @SkylarkCallable( + name = "is_enabled", + doc = "Returns True if given feature is enabled in the feature configuration.", + parameters = { + @Param( + name = "feature_configuration", + doc = "Feature configuration to be queried.", + positional = false, + named = true, + type = FeatureConfigurationApi.class), + @Param( + name = "feature_name", + doc = "Name of the feature.", + named = true, + positional = false), + }) + boolean isEnabled(FeatureConfigurationT featureConfiguration, String featureName); + + @SkylarkCallable( + name = "get_memory_inefficient_command_line", + doc = + "Returns flattened command line flags for given action, using given variables for " + + "expansion. Flattens nested sets and ideally should not be used, or at least " + + "should not outlive analysis. Work on memory efficient function returning Args is " + + "ongoing.", + parameters = { + @Param( + name = "feature_configuration", + doc = "Feature configuration to be queried.", + positional = false, + named = true, + type = FeatureConfigurationApi.class), + @Param( + name = "action_name", + doc = + "Name of the action. Has to be one of the names in " + + "@bazel_tools//tools/build_defs/cc:action_names.bzl.", + named = true, + positional = false), + @Param( + name = "variables", + doc = "Build variables to be used for template expansions.", + named = true, + positional = false, + type = CcToolchainVariablesApi.class), + }) + SkylarkList getCommandLine( + FeatureConfigurationT featureConfiguration, + String actionName, + CcToolchainVariablesT variables); + + @SkylarkCallable( + name = "get_environment_variables", + doc = "Returns environment variables to be set for given action.", + parameters = { + @Param( + name = "feature_configuration", + doc = "Feature configuration to be queried.", + positional = false, + named = true, + type = FeatureConfigurationApi.class), + @Param( + name = "action_name", + doc = + "Name of the action. Has to be one of the names in " + + "@bazel_tools//tools/build_defs/cc:action_names.bzl.", + named = true, + positional = false), + @Param( + name = "variables", + doc = "Build variables to be used for template expansion.", + positional = false, + named = true, + type = CcToolchainVariablesApi.class), + }) + SkylarkDict getEnvironmentVariable( + FeatureConfigurationT featureConfiguration, + String actionName, + CcToolchainVariablesT variables); + + @SkylarkCallable( + name = "create_compile_variables", + doc = "Returns variables used for compilation actions.", + parameters = { + @Param( + name = "cc_toolchain", + doc = "cc_toolchain for which we are creating build variables.", + positional = false, + named = true, + type = CcToolchainProviderApi.class), + @Param( + name = "feature_configuration", + doc = "Feature configuration to be queried.", + positional = false, + named = true, + type = FeatureConfigurationApi.class), + @Param( + name = "source_file", + doc = + "Optional source file for the compilation. Please prefer passing source_file here " + + "over appending it to the end of the command line generated from " + + "cc_common.get_memory_inefficient_command_line, as then it's in the power of " + + "the toolchain author to properly specify and position compiler flags.", + named = true, + positional = false, + defaultValue = "None", + noneable = true), + @Param( + name = "output_file", + doc = + "Optional output file of the compilation. Please prefer passing output_file here " + + "over appending it to the end of the command line generated from " + + "cc_common.get_memory_inefficient_command_line, as then it's in the power of " + + "the toolchain author to properly specify and position compiler flags.", + named = true, + positional = false, + defaultValue = "None", + noneable = true), + @Param( + name = "user_compile_flags", + doc = "Depset of additional compilation flags (copts).", + positional = false, + named = true, + defaultValue = "None", + noneable = true, + allowedTypes = { + @ParamType(type = NoneType.class), + @ParamType(type = SkylarkNestedSet.class) + }), + @Param( + name = "include_directories", + doc = "Depset of include directories.", + positional = false, + named = true, + defaultValue = "None", + noneable = true, + allowedTypes = { + @ParamType(type = NoneType.class), + @ParamType(type = SkylarkNestedSet.class) + }), + @Param( + name = "quote_include_directories", + doc = "Depset of quote include directories.", + positional = false, + named = true, + defaultValue = "None", + noneable = true, + allowedTypes = { + @ParamType(type = NoneType.class), + @ParamType(type = SkylarkNestedSet.class) + }), + @Param( + name = "system_include_directories", + doc = "Depset of system include directories.", + positional = false, + named = true, + defaultValue = "None", + noneable = true, + allowedTypes = { + @ParamType(type = NoneType.class), + @ParamType(type = SkylarkNestedSet.class) + }), + @Param( + name = "preprocessor_defines", + doc = "Depset of preprocessor defines.", + positional = false, + named = true, + defaultValue = "None", + noneable = true, + allowedTypes = { + @ParamType(type = NoneType.class), + @ParamType(type = SkylarkNestedSet.class) + }), + @Param( + name = "use_pic", + doc = "When true the compilation will generate position independent code.", + positional = false, + named = true, + defaultValue = "False"), + // TODO(b/65151735): Remove once we migrate crosstools to features + @Param( + name = "add_legacy_cxx_options", + doc = + "When true the flags will contain options coming from legacy cxx_flag crosstool " + + "fields.", + named = true, + positional = false, + defaultValue = "False") + }) + CcToolchainVariablesT getCompileBuildVariables( + CcToolchainProviderT ccToolchainProvider, + FeatureConfigurationT featureConfiguration, + Object sourceFile, + Object outputFile, + Object userCompileFlags, + Object includeDirs, + Object quoteIncludeDirs, + Object systemIncludeDirs, + Object defines, + boolean usePic, + boolean addLegacyCxxOptions) + throws EvalException; + + @SkylarkCallable( + name = "create_link_variables", + doc = "Returns link variables used for linking actions.", + parameters = { + @Param( + name = "cc_toolchain", + doc = "cc_toolchain for which we are creating build variables.", + positional = false, + named = true, + type = CcToolchainProviderApi.class), + @Param( + name = "feature_configuration", + doc = "Feature configuration to be queried.", + positional = false, + named = true, + type = FeatureConfigurationApi.class), + @Param( + name = "library_search_directories", + doc = "Depset of directories where linker will look for libraries at link time.", + positional = false, + named = true, + defaultValue = "None", + noneable = true, + allowedTypes = { + @ParamType(type = NoneType.class), + @ParamType(type = SkylarkNestedSet.class) + }), + @Param( + name = "runtime_library_search_directories", + doc = "Depset of directories where loader will look for libraries at runtime.", + positional = false, + named = true, + defaultValue = "None", + noneable = true, + allowedTypes = { + @ParamType(type = NoneType.class), + @ParamType(type = SkylarkNestedSet.class) + }), + @Param( + name = "user_link_flags", + doc = "Depset of additional link flags (linkopts).", + positional = false, + named = true, + defaultValue = "None", + noneable = true, + allowedTypes = { + @ParamType(type = NoneType.class), + @ParamType(type = SkylarkNestedSet.class) + }), + @Param( + name = "output_file", + doc = "Optional output file path.", + named = true, + positional = false, + defaultValue = "None", + noneable = true), + @Param( + name = "param_file", + doc = "Optional param file path.", + named = true, + positional = false, + defaultValue = "None", + noneable = true), + @Param( + name = "def_file", + doc = "Optional .def file path.", + named = true, + positional = false, + defaultValue = "None", + noneable = true), + // TODO(b/65151735): Remove once we migrate crosstools to features + @Param( + name = "is_using_linker", + doc = + "True when using linker, False when archiver. Caller is responsible for keeping " + + "this in sync with action name used (is_using_linker = True for linking " + + "executable or dynamic library, is_using_linker = False for archiving static " + + "library).", + named = true, + positional = false, + defaultValue = "True"), + // TODO(b/65151735): Remove once we migrate crosstools to features + @Param( + name = "is_linking_dynamic_library", + doc = + "True when creating dynamic library, False when executable or static library. " + + "Caller is responsible for keeping this in sync with action name used. " + + "" + + "This field will be removed once b/65151735 is fixed.", + named = true, + positional = false, + defaultValue = "False"), + // TODO(b/65151735): Remove once we migrate crosstools to features + @Param( + name = "must_keep_debug", + doc = + "When set to True, bazel will expose 'strip_debug_symbols' variable, which is " + + "usually used to use the linker to strip debug symbols from the output file.", + named = true, + positional = false, + defaultValue = "True"), + // TODO(b/65151735): Remove once we migrate crosstools to features + @Param( + name = "use_test_only_flags", + doc = + "When set to True flags coming from test_only_linker_flag crosstool fields will" + + " be included." + + "" + + "This field will be removed once b/65151735 is fixed.", + named = true, + positional = false, + defaultValue = "False"), + // TODO(b/65151735): Remove once we migrate crosstools to features + @Param( + name = "is_static_linking_mode", + doc = + "True when using static_linking_mode, False when using dynamic_linking_mode. " + + "Caller is responsible for keeping this in sync with 'static_linking_mode' " + + "and 'dynamic_linking_mode' features enabled on the feature configuration. " + + "" + + "This field will be removed once b/65151735 is fixed.", + named = true, + positional = false, + defaultValue = "True"), + }) + CcToolchainVariablesT getLinkBuildVariables( + CcToolchainProviderT ccToolchainProvider, + FeatureConfigurationT featureConfiguration, + Object librarySearchDirectories, + Object runtimeLibrarySearchDirectories, + Object userLinkFlags, + Object outputFile, + Object paramFile, + Object defFile, + boolean isUsingLinkerNotArchiver, + boolean isCreatingSharedLibrary, + boolean mustKeepDebug, + boolean useTestOnlyFlags, + boolean isStaticLinkingMode) + throws EvalException; + + @SkylarkCallable(name = "empty_variables", documented = false) + CcToolchainVariablesT getVariables(); } diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD b/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD index 035d5eb9a9..fe502fd67e 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD @@ -17,6 +17,7 @@ java_test( exclude = ["CcImportBaseConfiguredTargetTest.java"], ) + ["proto/CcProtoLibraryTest.java"], resources = [ + "//tools/build_defs/cc:action_names.bzl", "//tools/cpp:crosstool_utils", "//tools/cpp:lib_cc_configure", ], diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/SkylarkCcCommonTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/SkylarkCcCommonTest.java new file mode 100644 index 0000000000..5f4fa9c572 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/SkylarkCcCommonTest.java @@ -0,0 +1,824 @@ +// 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 static com.google.common.truth.Truth.assertThat; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableSet; +import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; +import com.google.devtools.build.lib.analysis.util.AnalysisMock; +import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; +import com.google.devtools.build.lib.packages.util.ResourceLoader; +import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; +import com.google.devtools.build.lib.syntax.SkylarkDict; +import com.google.devtools.build.lib.syntax.SkylarkList; +import com.google.devtools.build.lib.testutil.TestConstants; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Unit tests for the {@code cc_common} Skylark module. + */ +@RunWith(JUnit4.class) +public class SkylarkCcCommonTest extends BuildViewTestCase { + @Test + public void testGetToolForAction() throws Exception { + scratch.file( + "a/BUILD", + "load(':rule.bzl', 'crule')", + "cc_toolchain_alias(name='alias')", + "crule(name='r')"); + + scratch.file( + "a/rule.bzl", + "def _impl(ctx):", + " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", + " feature_configuration = cc_common.configure_features(cc_toolchain = toolchain)", + " return struct(", + " action_tool_path = cc_common.get_tool_for_action(", + " feature_configuration = feature_configuration,", + " action_name = 'c++-compile'))", + "crule = rule(", + " _impl,", + " attrs = { ", + " '_cc_toolchain': attr.label(default=Label('//a:alias'))", + " },", + " fragments = ['cpp'],", + ");"); + + ConfiguredTarget r = getConfiguredTarget("//a:r"); + @SuppressWarnings("unchecked") + String actionToolPath = (String) r.get("action_tool_path"); + RuleContext ruleContext = getRuleContext(r); + CcToolchainProvider toolchain = + CppHelper.getToolchain( + ruleContext, ruleContext.getPrerequisite("$cc_toolchain", Mode.TARGET)); + FeatureConfiguration featureConfiguration = + CcCommon.configureFeaturesOrThrowEvalException( + ImmutableSet.of(), ImmutableSet.of(), toolchain); + assertThat(actionToolPath) + .isEqualTo( + featureConfiguration + .getToolForAction(CppActionNames.CPP_COMPILE) + .getToolPathFragment() + .getPathString()); + } + + @Test + public void testFeatureConfigurationWithAdditionalEnabledFeature() throws Exception { + AnalysisMock.get() + .ccSupport() + .setupCrosstool(mockToolsConfig, "feature { name: 'foo_feature' }"); + useConfiguration(); + scratch.file( + "a/BUILD", + "load(':rule.bzl', 'crule')", + "cc_toolchain_alias(name='alias')", + "crule(name='r')"); + + scratch.file( + "a/rule.bzl", + "def _impl(ctx):", + " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", + " feature_configuration = cc_common.configure_features(", + " cc_toolchain = toolchain,", + " requested_features = ['foo_feature'])", + " return struct(", + " foo_feature_enabled = cc_common.is_enabled(", + " feature_configuration = feature_configuration,", + " feature_name = 'foo_feature'))", + "crule = rule(", + " _impl,", + " attrs = { ", + " '_cc_toolchain': attr.label(default=Label('//a:alias'))", + " },", + " fragments = ['cpp'],", + ");"); + + ConfiguredTarget r = getConfiguredTarget("//a:r"); + @SuppressWarnings("unchecked") + boolean fooFeatureEnabled = (boolean) r.get("foo_feature_enabled"); + assertThat(fooFeatureEnabled).isTrue(); + } + + @Test + public void testFeatureConfigurationWithAdditionalUnsupportedFeature() throws Exception { + AnalysisMock.get() + .ccSupport() + .setupCrosstool(mockToolsConfig, "feature { name: 'foo_feature' }"); + useConfiguration("--features=foo_feature"); + scratch.file( + "a/BUILD", + "load(':rule.bzl', 'crule')", + "cc_toolchain_alias(name='alias')", + "crule(name='r')"); + + scratch.file( + "a/rule.bzl", + "def _impl(ctx):", + " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", + " feature_configuration = cc_common.configure_features(", + " cc_toolchain = toolchain,", + " unsupported_features = ['foo_feature'])", + " return struct(", + " foo_feature_enabled = cc_common.is_enabled(", + " feature_configuration = feature_configuration,", + " feature_name = 'foo_feature'))", + "crule = rule(", + " _impl,", + " attrs = { ", + " '_cc_toolchain': attr.label(default=Label('//a:alias'))", + " },", + " fragments = ['cpp'],", + ");"); + + ConfiguredTarget r = getConfiguredTarget("//a:r"); + @SuppressWarnings("unchecked") + boolean fooFeatureEnabled = (boolean) r.get("foo_feature_enabled"); + assertThat(fooFeatureEnabled).isFalse(); + } + + @Test + public void testGetCommandLine() throws Exception { + scratch.file( + "a/BUILD", + "load(':rule.bzl', 'crule')", + "cc_toolchain_alias(name='alias')", + "crule(name='r')"); + + scratch.file( + "a/rule.bzl", + "def _impl(ctx):", + " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", + " feature_configuration = cc_common.configure_features(cc_toolchain = toolchain)", + " return struct(", + " command_line = cc_common.get_memory_inefficient_command_line(", + " feature_configuration = feature_configuration,", + " action_name = 'c++-link-executable',", + " variables = cc_common.empty_variables()))", + "crule = rule(", + " _impl,", + " attrs = { ", + " '_cc_toolchain': attr.label(default=Label('//a:alias'))", + " },", + " fragments = ['cpp'],", + ");"); + + ConfiguredTarget r = getConfiguredTarget("//a:r"); + @SuppressWarnings("unchecked") + SkylarkList commandLine = (SkylarkList) r.get("command_line"); + RuleContext ruleContext = getRuleContext(r); + CcToolchainProvider toolchain = + CppHelper.getToolchain( + ruleContext, ruleContext.getPrerequisite("$cc_toolchain", Mode.TARGET)); + FeatureConfiguration featureConfiguration = + CcCommon.configureFeaturesOrThrowEvalException( + ImmutableSet.of(), ImmutableSet.of(), toolchain); + assertThat(commandLine) + .containsExactlyElementsIn( + featureConfiguration.getCommandLine("c++-link-executable", CcToolchainVariables.EMPTY)); + } + + @Test + public void testGetEnvironment() throws Exception { + scratch.file( + "a/BUILD", + "load(':rule.bzl', 'crule')", + "cc_toolchain_alias(name='alias')", + "crule(name='r')"); + + scratch.file( + "a/rule.bzl", + "def _impl(ctx):", + " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", + " feature_configuration = cc_common.configure_features(cc_toolchain = toolchain)", + " return struct(", + " environment_variables = cc_common.get_environment_variables(", + " feature_configuration = feature_configuration,", + " action_name = 'c++-compile',", + " variables = cc_common.empty_variables()))", + "crule = rule(", + " _impl,", + " attrs = { ", + " '_cc_toolchain': attr.label(default=Label('//a:alias'))", + " },", + " fragments = ['cpp'],", + ");"); + + ConfiguredTarget r = getConfiguredTarget("//a:r"); + @SuppressWarnings("unchecked") + SkylarkDict environmentVariables = + (SkylarkDict) r.get("environment_variables"); + RuleContext ruleContext = getRuleContext(r); + CcToolchainProvider toolchain = + CppHelper.getToolchain( + ruleContext, ruleContext.getPrerequisite("$cc_toolchain", Mode.TARGET)); + FeatureConfiguration featureConfiguration = + CcCommon.configureFeaturesOrThrowEvalException( + ImmutableSet.of(), ImmutableSet.of(), toolchain); + assertThat(environmentVariables) + .containsExactlyEntriesIn( + featureConfiguration.getEnvironmentVariables( + CppActionNames.CPP_COMPILE, CcToolchainVariables.EMPTY)); + } + + @Test + public void testIsEnabled() throws Exception { + scratch.file( + "a/BUILD", + "load(':rule.bzl', 'crule')", + "cc_toolchain_alias(name='alias')", + "crule(name='r')"); + + scratch.file( + "a/rule.bzl", + "def _impl(ctx):", + " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", + " feature_configuration = cc_common.configure_features(cc_toolchain = toolchain)", + " return struct(", + " enabled_feature = cc_common.is_enabled(", + " feature_configuration = feature_configuration,", + " feature_name = 'libraries_to_link'),", + " disabled_feature = cc_common.is_enabled(", + " feature_configuration = feature_configuration,", + " feature_name = 'wololoo'))", + "crule = rule(", + " _impl,", + " attrs = { ", + " '_cc_toolchain': attr.label(default=Label('//a:alias'))", + " },", + " fragments = ['cpp'],", + ");"); + + ConfiguredTarget r = getConfiguredTarget("//a:r"); + @SuppressWarnings("unchecked") + boolean enabledFeatureIsEnabled = (boolean) r.get("enabled_feature"); + @SuppressWarnings("unchecked") + boolean disabledFeatureIsDisabled = (boolean) r.get("disabled_feature"); + assertThat(enabledFeatureIsEnabled).isTrue(); + assertThat(disabledFeatureIsDisabled).isFalse(); + } + + @Test + public void testActionNames() throws Exception { + scratch.file( + "a/BUILD", + "load(':rule.bzl', 'crule')", + "cc_toolchain_alias(name='alias')", + "crule(name='r')"); + scratch.file("tools/build_defs/cc/BUILD"); + scratch.file( + "tools/build_defs/cc/action_names.bzl", + ResourceLoader.readFromResources( + TestConstants.BAZEL_REPO_PATH + "tools/build_defs/cc/action_names.bzl")); + + scratch.file( + "a/rule.bzl", + "load('//tools/build_defs/cc:action_names.bzl',", + " 'C_COMPILE_ACTION_NAME',", + " 'CPP_COMPILE_ACTION_NAME',", + " 'LINKSTAMP_COMPILE_ACTION_NAME',", + " 'CC_FLAGS_MAKE_VARIABLE_ACTION_NAME_ACTION_NAME',", + " 'CPP_MODULE_CODEGEN_ACTION_NAME',", + " 'CPP_HEADER_PARSING_ACTION_NAME',", + " 'CPP_MODULE_COMPILE_ACTION_NAME',", + " 'ASSEMBLE_ACTION_NAME',", + " 'PREPROCESS_ASSEMBLE_ACTION_NAME',", + " 'LTO_INDEXING_ACTION_NAME',", + " 'LTO_BACKEND_ACTION_NAME',", + " 'CPP_LINK_EXECUTABLE_ACTION_NAME',", + " 'CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME',", + " 'CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME',", + " 'CPP_LINK_STATIC_LIBRARY_ACTION_NAME',", + " 'STRIP_ACTION_NAME')", + "def _impl(ctx):", + " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", + " feature_configuration = cc_common.configure_features(cc_toolchain = toolchain)", + " return struct(", + " c_compile_action_name=C_COMPILE_ACTION_NAME,", + " cpp_compile_action_name=CPP_COMPILE_ACTION_NAME,", + " linkstamp_compile_action_name=LINKSTAMP_COMPILE_ACTION_NAME,", + " cc_flags_make_variable_action_name_action_name=CC_FLAGS_MAKE_VARIABLE_ACTION_NAME_ACTION_NAME,", + " cpp_module_codegen_action_name=CPP_MODULE_CODEGEN_ACTION_NAME,", + " cpp_header_parsing_action_name=CPP_HEADER_PARSING_ACTION_NAME,", + " cpp_module_compile_action_name=CPP_MODULE_COMPILE_ACTION_NAME,", + " assemble_action_name=ASSEMBLE_ACTION_NAME,", + " preprocess_assemble_action_name=PREPROCESS_ASSEMBLE_ACTION_NAME,", + " lto_indexing_action_name=LTO_INDEXING_ACTION_NAME,", + " lto_backend_action_name=LTO_BACKEND_ACTION_NAME,", + " cpp_link_executable_action_name=CPP_LINK_EXECUTABLE_ACTION_NAME,", + " cpp_link_dynamic_library_action_name=CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME,", + " cpp_link_nodeps_dynamic_library_action_name=CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME,", + " cpp_link_static_library_action_name=CPP_LINK_STATIC_LIBRARY_ACTION_NAME,", + " strip_action_name=STRIP_ACTION_NAME)", + "crule = rule(", + " _impl,", + " attrs = { ", + " '_cc_toolchain': attr.label(default=Label('//a:alias'))", + " },", + " fragments = ['cpp'],", + ")"); + + assertThat(getTarget("//a:r")).isNotNull(); + ConfiguredTarget r = getConfiguredTarget("//a:r"); + assertThat(r.get("c_compile_action_name")).isEqualTo(CppActionNames.C_COMPILE); + assertThat(r.get("cpp_compile_action_name")).isEqualTo(CppActionNames.CPP_COMPILE); + assertThat(r.get("linkstamp_compile_action_name")).isEqualTo(CppActionNames.LINKSTAMP_COMPILE); + assertThat(r.get("cc_flags_make_variable_action_name_action_name")) + .isEqualTo(CppActionNames.CC_FLAGS_MAKE_VARIABLE); + assertThat(r.get("cpp_module_codegen_action_name")) + .isEqualTo(CppActionNames.CPP_MODULE_CODEGEN); + assertThat(r.get("cpp_header_parsing_action_name")) + .isEqualTo(CppActionNames.CPP_HEADER_PARSING); + assertThat(r.get("cpp_module_compile_action_name")) + .isEqualTo(CppActionNames.CPP_MODULE_COMPILE); + assertThat(r.get("assemble_action_name")).isEqualTo(CppActionNames.ASSEMBLE); + assertThat(r.get("preprocess_assemble_action_name")) + .isEqualTo(CppActionNames.PREPROCESS_ASSEMBLE); + assertThat(r.get("lto_indexing_action_name")).isEqualTo(CppActionNames.LTO_INDEXING); + assertThat(r.get("lto_backend_action_name")).isEqualTo(CppActionNames.LTO_BACKEND); + assertThat(r.get("cpp_link_executable_action_name")) + .isEqualTo(CppActionNames.CPP_LINK_EXECUTABLE); + assertThat(r.get("cpp_link_dynamic_library_action_name")) + .isEqualTo(CppActionNames.CPP_LINK_DYNAMIC_LIBRARY); + assertThat(r.get("cpp_link_nodeps_dynamic_library_action_name")) + .isEqualTo(CppActionNames.CPP_LINK_NODEPS_DYNAMIC_LIBRARY); + assertThat(r.get("cpp_link_static_library_action_name")) + .isEqualTo(CppActionNames.CPP_LINK_STATIC_LIBRARY); + assertThat(r.get("strip_action_name")).isEqualTo(CppActionNames.STRIP); + } + + @Test + public void testEmptyCompileBuildVariables() throws Exception { + AnalysisMock.get() + .ccSupport() + .setupCrosstool(mockToolsConfig, "compiler_flag: '-foo'", "cxx_flag: '-foo_for_cxx_only'"); + useConfiguration(); + SkylarkList commandLine = + commandLineForVariables( + CppActionNames.CPP_COMPILE, + "cc_common.create_compile_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + ")"); + assertThat(commandLine).contains("-foo"); + assertThat(commandLine).doesNotContain("-foo_for_cxx_only"); + } + + @Test + public void testEmptyCompileBuildVariablesForCxx() throws Exception { + AnalysisMock.get() + .ccSupport() + .setupCrosstool(mockToolsConfig, "compiler_flag: '-foo'", "cxx_flag: '-foo_for_cxx_only'"); + useConfiguration(); + assertThat( + commandLineForVariables( + CppActionNames.CPP_COMPILE, + "cc_common.create_compile_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "add_legacy_cxx_options = True", + ")")) + .containsAllOf("-foo", "-foo_for_cxx_only") + .inOrder(); + } + + @Test + public void testCompileBuildVariablesWithSourceFile() throws Exception { + assertThat( + commandLineForVariables( + CppActionNames.CPP_COMPILE, + "cc_common.create_compile_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "source_file = 'foo/bar/hello'", + ")")) + .containsAllOf("-c", "foo/bar/hello") + .inOrder(); + } + + @Test + public void testCompileBuildVariablesWithOutputFile() throws Exception { + assertThat( + commandLineForVariables( + CppActionNames.CPP_COMPILE, + "cc_common.create_compile_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "output_file = 'foo/bar/hello.o'", + ")")) + .containsAllOf("-o", "foo/bar/hello.o") + .inOrder(); + } + + @Test + public void testCompileBuildVariablesForIncludes() throws Exception { + assertThat( + commandLineForVariables( + CppActionNames.CPP_COMPILE, + "cc_common.create_compile_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "include_directories = depset(['foo/bar/include'])", + ")")) + .contains("-Ifoo/bar/include"); + } + + @Test + public void testCompileBuildVariablesForDefines() throws Exception { + assertThat( + commandLineForVariables( + CppActionNames.CPP_COMPILE, + "cc_common.create_compile_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "preprocessor_defines = depset(['DEBUG_FOO'])", + ")")) + .contains("-DDEBUG_FOO"); + } + + @Test + public void testCompileBuildVariablesForPic() throws Exception { + assertThat( + commandLineForVariables( + CppActionNames.CPP_COMPILE, + "cc_common.create_compile_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "use_pic = True", + ")")) + .contains("-fPIC"); + } + + @Test + public void testUserCompileFlags() throws Exception { + assertThat( + commandLineForVariables( + CppActionNames.CPP_COMPILE, + "cc_common.create_compile_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "user_compile_flags = depset(['-foo'])", + ")")) + .contains("-foo"); + } + + @Test + public void testEmptyLinkVariables() throws Exception { + useConfiguration("--linkopt=-foo"); + assertThat( + commandLineForVariables( + CppActionNames.CPP_LINK_EXECUTABLE, + "cc_common.create_link_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + ")")) + .contains("-foo"); + } + + @Test + public void testLibrarySearchDirectoriesLinkVariables() throws Exception { + AnalysisMock.get() + .ccSupport() + .setupCrosstool( + mockToolsConfig, + "feature {", + " name: 'library_search_directories'", + " enabled: true", + " flag_set {", + " action: 'c++-link-executable'", + " flag_group {", + " expand_if_all_available: 'library_search_directories'", + " iterate_over: 'library_search_directories'", + " flag: '--library=%{library_search_directories}'", + " }", + " }", + "}"); + useConfiguration(); + assertThat( + commandLineForVariables( + CppActionNames.CPP_LINK_EXECUTABLE, + "cc_common.create_link_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "library_search_directories = depset([ 'a', 'b', 'c' ]),", + ")")) + .containsAllOf("--library=a", "--library=b", "--library=c") + .inOrder(); + } + + @Test + public void testRuntimeLibrarySearchDirectoriesLinkVariables() throws Exception { + AnalysisMock.get() + .ccSupport() + .setupCrosstool( + mockToolsConfig, + "feature {", + " name: 'runtime_library_search_directories'", + " enabled: true", + " flag_set {", + " action: 'c++-link-executable'", + " flag_group {", + " expand_if_all_available: 'runtime_library_search_directories'", + " iterate_over: 'runtime_library_search_directories'", + " flag: '--runtime_library=%{runtime_library_search_directories}'", + " }", + " }", + "}"); + useConfiguration(); + assertThat( + commandLineForVariables( + CppActionNames.CPP_LINK_EXECUTABLE, + "cc_common.create_link_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "runtime_library_search_directories = depset([ 'a', 'b', 'c' ]),", + ")")) + .containsAllOf("--runtime_library=a", "--runtime_library=b", "--runtime_library=c") + .inOrder(); + } + + @Test + public void testUserLinkFlagsLinkVariables() throws Exception { + assertThat( + commandLineForVariables( + CppActionNames.CPP_LINK_EXECUTABLE, + "cc_common.create_link_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "user_link_flags = depset([ '-avocado' ]),", + ")")) + .contains("-avocado"); + } + + @Test + public void testOutputFileLinkVariables() throws Exception { + assertThat( + commandLineForVariables( + CppActionNames.CPP_LINK_EXECUTABLE, + "cc_common.create_link_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "output_file = 'foo/bar/executable',", + ")")) + .contains("foo/bar/executable"); + } + + @Test + public void testParamFileLinkVariables() throws Exception { + assertThat( + commandLineForVariables( + CppActionNames.CPP_LINK_EXECUTABLE, + "cc_common.create_link_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "param_file = 'foo/bar/params',", + ")")) + .contains("-Wl,@foo/bar/params"); + } + + @Test + public void testDefFileLinkVariables() throws Exception { + AnalysisMock.get() + .ccSupport() + .setupCrosstool( + mockToolsConfig, + "feature {", + " name: 'def'", + " enabled: true", + " flag_set {", + " action: 'c++-link-executable'", + " flag_group {", + " expand_if_all_available: 'def_file_path'", + " flag: '-qux_%{def_file_path}'", + " }", + " }", + "}"); + useConfiguration(); + assertThat( + commandLineForVariables( + CppActionNames.CPP_LINK_EXECUTABLE, + "cc_common.create_link_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "def_file = 'foo/bar/def',", + ")")) + .contains("-qux_foo/bar/def"); + } + + @Test + public void testMustKeepDebugLinkVariables() throws Exception { + AnalysisMock.get() + .ccSupport() + .setupCrosstool( + mockToolsConfig, + "feature {", + " name: 'strip_debug_symbols'", + " enabled: true", + " flag_set {", + " action: 'c++-link-executable'", + " flag_group {", + " expand_if_all_available: 'strip_debug_symbols'", + " flag: '-strip_stuff'", + " }", + " }", + "}"); + useConfiguration(); + assertThat( + commandLineForVariables( + CppActionNames.CPP_LINK_EXECUTABLE, + 0, + "cc_common.create_link_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "must_keep_debug = False,", + ")")) + .contains("-strip_stuff"); + assertThat( + commandLineForVariables( + CppActionNames.CPP_LINK_EXECUTABLE, + 1, + "cc_common.create_link_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "must_keep_debug = True,", + ")")) + .doesNotContain("-strip_stuff"); + } + + @Test + public void testIsLinkingDynamicLibraryLinkVariables() throws Exception { + useConfiguration("--linkopt=-pie"); + assertThat( + commandLineForVariables( + CppActionNames.CPP_LINK_EXECUTABLE, + 0, + "cc_common.create_link_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "is_linking_dynamic_library = True,", + ")")) + .doesNotContain("-pie"); + assertThat( + commandLineForVariables( + CppActionNames.CPP_LINK_EXECUTABLE, + 1, + "cc_common.create_link_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "is_linking_dynamic_library = False,", + ")")) + .contains("-pie"); + } + + @Test + public void testIsUsingLinkerLinkVariables() throws Exception { + useConfiguration("--linkopt=-i_dont_want_to_see_this_on_archiver_command_line"); + assertThat( + commandLineForVariables( + CppActionNames.CPP_LINK_EXECUTABLE, + 0, + "cc_common.create_link_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "is_using_linker = True,", + ")")) + .contains("-i_dont_want_to_see_this_on_archiver_command_line"); + assertThat( + commandLineForVariables( + CppActionNames.CPP_LINK_EXECUTABLE, + 1, + "cc_common.create_link_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "is_using_linker = False,", + ")")) + .doesNotContain("-i_dont_want_to_see_this_on_archiver_command_line"); + } + + @Test + public void testUseTestOnlyFlagsLinkVariables() throws Exception { + AnalysisMock.get() + .ccSupport() + .setupCrosstool(mockToolsConfig, "test_only_linker_flag: '-im_only_testing_flag'"); + useConfiguration(); + assertThat( + commandLineForVariables( + CppActionNames.CPP_LINK_EXECUTABLE, + 0, + "cc_common.create_link_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "use_test_only_flags = False,", + ")")) + .doesNotContain("-im_only_testing_flag"); + assertThat( + commandLineForVariables( + CppActionNames.CPP_LINK_EXECUTABLE, + 1, + "cc_common.create_link_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "use_test_only_flags = True,", + ")")) + .contains("-im_only_testing_flag"); + } + + @Test + public void testIsStaticLinkingModeLinkVariables() throws Exception { + AnalysisMock.get() + .ccSupport() + .setupCrosstool( + mockToolsConfig, + "linking_mode_flags {", + " mode: MOSTLY_STATIC", + " linker_flag: '-static_linking_mode_flag'", + "}", + "linking_mode_flags {", + " mode: DYNAMIC", + " linker_flag: '-dynamic_linking_mode_flag'", + "}"); + useConfiguration(); + SkylarkList staticLinkingModeFlags = + commandLineForVariables( + CppActionNames.CPP_LINK_EXECUTABLE, + 0, + "cc_common.create_link_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "is_static_linking_mode = True,", + ")"); + assertThat(staticLinkingModeFlags).contains("-static_linking_mode_flag"); + assertThat(staticLinkingModeFlags).doesNotContain("-dynamic_linking_mode_flag"); + + SkylarkList dynamicLinkingModeFlags = + commandLineForVariables( + CppActionNames.CPP_LINK_EXECUTABLE, + 1, + "cc_common.create_link_variables(", + "feature_configuration = feature_configuration,", + "cc_toolchain = toolchain,", + "is_static_linking_mode = False,", + ")"); + assertThat(dynamicLinkingModeFlags).doesNotContain("-static_linking_mode_flag"); + assertThat(dynamicLinkingModeFlags).contains("-dynamic_linking_mode_flag"); + } + + private SkylarkList commandLineForVariables(String actionName, String... variables) + throws Exception { + return commandLineForVariables(actionName, 0, variables); + } + + // This method is only there to change the package to fix multiple runs of this method in a single + // test. + // TODO(b/109917616): Remove pkgSuffix argument when bzl files are not cached within single test + private SkylarkList commandLineForVariables( + String actionName, int pkgSuffix, String... variables) throws Exception { + scratch.file( + "a" + pkgSuffix + "/BUILD", + "load(':rule.bzl', 'crule')", + "cc_toolchain_alias(name='alias')", + "crule(name='r')"); + + scratch.file( + "a" + pkgSuffix + "/rule.bzl", + "def _impl(ctx):", + " toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]", + " feature_configuration = cc_common.configure_features(cc_toolchain = toolchain)", + " variables = " + Joiner.on("\n").join(variables), + " return struct(", + " command_line = cc_common.get_memory_inefficient_command_line(", + " feature_configuration = feature_configuration,", + " action_name = '" + actionName + "',", + " variables = variables))", + "crule = rule(", + " _impl,", + " attrs = { ", + " '_cc_toolchain': attr.label(default=Label('//a" + pkgSuffix + ":alias'))", + " },", + " fragments = ['cpp'],", + ");"); + + /** Calling {@link #getTarget} to get loading errors */ + getTarget("//a" + pkgSuffix + ":r"); + ConfiguredTarget r = getConfiguredTarget("//a" + pkgSuffix + ":r"); + @SuppressWarnings("unchecked") + SkylarkList result = (SkylarkList) r.get("command_line"); + return result; + } +} -- cgit v1.2.3