aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar hlopko <hlopko@google.com>2018-06-28 05:38:50 -0700
committerGravatar Copybara-Service <copybara-piper@google.com>2018-06-28 05:39:55 -0700
commitc71157f9a826db25e2588a0147de76e97ff30eb2 (patch)
treedeeea287e7e47c4db04b7e8f99f5e133900ea181
parent74a9f015270083d51e0e32e8390a33c3aecb9a2c (diff)
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
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java167
-rw-r--r--src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp/CcModuleApi.java432
-rw-r--r--src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD1
-rw-r--r--src/test/java/com/google/devtools/build/lib/rules/cpp/SkylarkCcCommonTest.java824
4 files changed, 1411 insertions, 13 deletions
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<CcToolchainProvider, FeatureConfiguration, CcToolchainVariables> {
@Override
public Provider getCcToolchainProvider() {
return ToolchainInfo.PROVIDER;
}
+
+ @Override
+ public FeatureConfiguration configureFeatures(
+ CcToolchainProvider toolchain,
+ SkylarkList<String> requestedFeatures,
+ SkylarkList<String> 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<String> getCommandLine(
+ FeatureConfiguration featureConfiguration,
+ String actionName,
+ CcToolchainVariables variables) {
+ return SkylarkList.createImmutable(featureConfiguration.getCommandLine(actionName, variables));
+ }
+
+ @Override
+ public SkylarkDict<String, String> 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> 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<String> 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<String> requestedFeatures,
+ SkylarkList<String> 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<String> 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<String, String> 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<String> commandLine = (SkylarkList<String>) 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<String, String> environmentVariables =
+ (SkylarkDict<String, String>) 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> result = (SkylarkList<String>) r.get("command_line");
+ return result;
+ }
+}