diff --git a/cc/toolchains/args.bzl b/cc/toolchains/args.bzl
index 95cdf5217..70c111447 100644
--- a/cc/toolchains/args.bzl
+++ b/cc/toolchains/args.bzl
@@ -44,11 +44,17 @@ visibility("public")
def _cc_args_impl(ctx):
actions = collect_action_types(ctx.attr.actions)
+ format_targets = {k: v for v, k in ctx.attr.format.items()}
formatted_env, used_format_vars = format_dict_values(
env = ctx.attr.env,
must_use = [], # checking for unused variables in done when formatting `args`.
- format = {k: v for v, k in ctx.attr.format.items()},
+ format = format_targets,
)
+ used_env_variables = [
+ var
+ for var in used_format_vars
+ if var in format_targets and VariableInfo in format_targets[var]
+ ]
for path in ctx.attr.allowlist_absolute_include_directories:
if not is_path_absolute(path):
@@ -80,7 +86,7 @@ def _cc_args_impl(ctx):
actions = actions.to_list(),
env = env,
variables = ctx.attr._variables[BuiltinVariablesInfo].variables,
- used_format_vars = used_format_vars,
+ used_format_vars = used_env_variables,
)
args = ArgsInfo(
@@ -280,9 +286,10 @@ def cc_args(
arguments to work as intended.
env: (Dict[str, str]) Environment variables that should be set when the tool is invoked.
format: (Dict[str, Label]) A mapping of format strings to the label of a corresponding
- target. This target can be a `directory`, `subdirectory`, `cc_variable`, or a single
- file that the value should be pulled from. All instances of `{variable_name}` in the
- `args` list will be replaced with the expanded value in this dictionary.
+ target. This target can be a `directory`, `subdirectory`, `cc_variable`, a build setting
+ (a target that provides `BuildSettingInfo`), or a single file that the value should be
+ pulled from. All instances of `{variable_name}` in the `args` list will be replaced with
+ the expanded value in this dictionary.
The complete list of possible variables can be found in
https://github.com/bazelbuild/rules_cc/tree/main/cc/toolchains/variables/BUILD.
It is not possible to declare custom variables--these are inherent to Bazel itself.
diff --git a/cc/toolchains/impl/BUILD b/cc/toolchains/impl/BUILD
index 7ee93fdbe..c5f95a6e4 100644
--- a/cc/toolchains/impl/BUILD
+++ b/cc/toolchains/impl/BUILD
@@ -16,7 +16,10 @@ bzl_library(
name = "toolchain_impl_rules",
srcs = glob(["*.bzl"]),
visibility = ["//cc/toolchains:__subpackages__"],
- deps = ["//cc/common"],
+ deps = [
+ "//cc/common",
+ "@bazel_skylib//rules:common_settings",
+ ],
)
config_setting(
diff --git a/cc/toolchains/impl/nested_args.bzl b/cc/toolchains/impl/nested_args.bzl
index e59711d5a..0396fd55a 100644
--- a/cc/toolchains/impl/nested_args.bzl
+++ b/cc/toolchains/impl/nested_args.bzl
@@ -13,6 +13,7 @@
# limitations under the License.
"""Helper functions for working with args."""
+load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
load("@bazel_skylib//rules/directory:providers.bzl", "DirectoryInfo")
load("//cc:cc_toolchain_config_lib.bzl", "flag_group", "variable_with_value")
load("//cc/toolchains:cc_toolchain_info.bzl", "NestedArgsInfo", "VariableInfo")
@@ -291,9 +292,23 @@ def nested_args_provider(
def _escape(s):
return s.replace("%", "%%")
+_SUPPORTED_BUILD_SETTING_TYPES = ["string", "bool", "int", "Label"]
+
+def _format_build_setting(value, label, fail = fail):
+ if type(value) in _SUPPORTED_BUILD_SETTING_TYPES:
+ return _escape(str(value))
+
+ fail("%s had an unsupported build setting type %s. Only string, bool, int, or Label values may be formatted." % (label, type(value)))
+
def _format_target(target, fail = fail):
if VariableInfo in target:
return "%%{%s}" % target[VariableInfo].name
+ elif BuildSettingInfo in target:
+ return _format_build_setting(
+ target[BuildSettingInfo].value,
+ target.label,
+ fail = fail,
+ )
elif DirectoryInfo in target:
return _escape(target[DirectoryInfo].path)
@@ -301,7 +316,7 @@ def _format_target(target, fail = fail):
if len(files) == 1:
return _escape(files[0].path)
- fail("%s should be either a variable, a directory, or a single file." % target.label)
+ fail("%s should be either a variable, a build setting, a directory, or a single file." % target.label)
def _format_string(arg, format, used_vars, fail = fail):
upto = 0
diff --git a/docs/toolchain_api.md b/docs/toolchain_api.md
index eee9241f1..3dfe886f6 100755
--- a/docs/toolchain_api.md
+++ b/docs/toolchain_api.md
@@ -663,7 +663,7 @@ For more extensive examples, see the usages here:
| args | (List[str]) The command-line arguments that are applied by using this rule. This is mutually exclusive with [nested](#cc_args-nested). | `None` |
| data | (List[Label]) A list of runtime data dependencies that are required for these arguments to work as intended. | `None` |
| env | (Dict[str, str]) Environment variables that should be set when the tool is invoked. | `None` |
-| format | (Dict[str, Label]) A mapping of format strings to the label of a corresponding target. This target can be a `directory`, `subdirectory`, [`cc_variable`](#cc_variable), or a single file that the value should be pulled from. All instances of `{variable_name}` in the `args` list will be replaced with the expanded value in this dictionary. The complete list of possible variables can be found in https://github.com/bazelbuild/rules_cc/tree/main/cc/toolchains/variables/BUILD. It is not possible to declare custom variables--these are inherent to Bazel itself. | `{}` |
+| format | (Dict[str, Label]) A mapping of format strings to the label of a corresponding target. This target can be a `directory`, `subdirectory`, [`cc_variable`](#cc_variable), a build setting (a target that provides `BuildSettingInfo`), or a single file that the value should be pulled from. All instances of `{variable_name}` in the `args` list will be replaced with the expanded value in this dictionary. The complete list of possible variables can be found in https://github.com/bazelbuild/rules_cc/tree/main/cc/toolchains/variables/BUILD. It is not possible to declare custom variables--these are inherent to Bazel itself. | `{}` |
| iterate_over | (Label) The label of a [`cc_variable`](#cc_variable) that should be iterated over. This is intended for use with built-in variables that are lists. | `None` |
| nested | (List[Label]) A list of [`cc_nested_args`](#cc_nested_args) rules that should be expanded to command-line arguments when this rule is used. This is mutually exclusive with [args](#cc_args-args). | `None` |
| requires_not_none | (Label) The label of a [`cc_variable`](#cc_variable) that should be checked for existence before expanding this rule. If the variable is None, this rule will be ignored. | `None` |
diff --git a/tests/rule_based_toolchain/analysis_test_suite.bzl b/tests/rule_based_toolchain/analysis_test_suite.bzl
index 01ba4e298..a67c4e753 100644
--- a/tests/rule_based_toolchain/analysis_test_suite.bzl
+++ b/tests/rule_based_toolchain/analysis_test_suite.bzl
@@ -33,15 +33,25 @@ def analysis_test_suite(name, tests, targets = [_DEFAULT_TARGET]):
targets = [native.package_relative_label(target) for target in targets]
test_case_names = []
- for test_name, impl in tests.items():
+ for test_name, test in tests.items():
if not test_name.endswith("_test"):
fail("Expected test keys to end with '_test', got test case %r" % test_name)
test_case_names.append(":" + test_name)
+ config_settings = {}
+ if type(test) == "struct":
+ if not hasattr(test, "impl"):
+ fail("Expected struct tests to define an 'impl' field, got %r" % test_name)
+ impl = test.impl
+ if hasattr(test, "config_settings"):
+ config_settings = test.config_settings
+ else:
+ impl = test
analysis_test(
name = test_name,
impl = impl,
provider_subject_factories = FACTORIES,
targets = {label.name: label for label in targets},
+ config_settings = config_settings,
)
native.test_suite(
diff --git a/tests/rule_based_toolchain/args/BUILD b/tests/rule_based_toolchain/args/BUILD
index 7f47073c4..35899647c 100644
--- a/tests/rule_based_toolchain/args/BUILD
+++ b/tests/rule_based_toolchain/args/BUILD
@@ -1,15 +1,22 @@
+load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
load("@rules_testing//lib:util.bzl", "util")
load("//cc/toolchains:args.bzl", "cc_args")
load("//cc/toolchains/impl:variables.bzl", "cc_variable", "types")
load("//tests/rule_based_toolchain:analysis_test_suite.bzl", "analysis_test_suite")
load("//tests/rule_based_toolchain:testing_rules.bzl", "expect_failure_test")
load(":args_test.bzl", "TARGETS", "TESTS")
+load(":override_args.bzl", "override_args")
cc_variable(
name = "some_variable",
type = types.string,
)
+string_flag(
+ name = "macos_min_os_flag",
+ build_setting_default = "-mmacosx-version-min=12.0",
+)
+
util.helper_target(
cc_args,
name = "simple",
@@ -55,6 +62,21 @@ util.helper_target(
requires_not_none = "//cc/toolchains/variables:user_compile_flags",
)
+util.helper_target(
+ cc_args,
+ name = "build_setting_format",
+ actions = ["//tests/rule_based_toolchain/actions:all_compile"],
+ args = ["{min_os_flag}"],
+ env = {"APPLE_MIN_OS": "{min_os_flag}"},
+ format = {"min_os_flag": ":macos_min_os_flag"},
+)
+
+override_args(
+ name = "build_setting_format_override",
+ target = ":build_setting_format",
+ value = "-mmacosx-version-min=13.0",
+)
+
util.helper_target(
cc_args,
name = "with_dir",
diff --git a/tests/rule_based_toolchain/args/args_test.bzl b/tests/rule_based_toolchain/args/args_test.bzl
index 2adc82b0b..535e5db88 100644
--- a/tests/rule_based_toolchain/args/args_test.bzl
+++ b/tests/rule_based_toolchain/args/args_test.bzl
@@ -50,6 +50,7 @@ _SIMPLE_FILES = [
"tests/rule_based_toolchain/testdata/multiple2",
]
_TOOL_DIRECTORY = "tests/rule_based_toolchain/testdata"
+_OVERRIDDEN_MIN_OS = "-mmacosx-version-min=13.0"
_CONVERTED_ARGS = subjects.struct(
flag_sets = subjects.collection,
@@ -169,11 +170,55 @@ def _with_dir_and_data_test(env, targets):
)
c_compile.files().contains_at_least(_SIMPLE_FILES)
+def _build_setting_format_test(env, targets):
+ build_setting = env.expect.that_target(targets.build_setting_format).provider(ArgsInfo)
+ build_setting.actions().contains_exactly([
+ targets.c_compile.label,
+ targets.cpp_compile.label,
+ ])
+ build_setting.env().entries().contains_exactly({"APPLE_MIN_OS": "-mmacosx-version-min=12.0"})
+
+ converted = env.expect.that_value(
+ convert_args(targets.build_setting_format[ArgsInfo]),
+ factory = _CONVERTED_ARGS,
+ )
+ converted.env_sets().contains_exactly([env_set(
+ actions = ["c_compile", "cpp_compile"],
+ env_entries = [env_entry(key = "APPLE_MIN_OS", value = "-mmacosx-version-min=12.0")],
+ )])
+ converted.flag_sets().contains_exactly([flag_set(
+ actions = ["c_compile", "cpp_compile"],
+ flag_groups = [flag_group(flags = ["-mmacosx-version-min=12.0"])],
+ )])
+
+def _build_setting_format_override_test(env, targets):
+ build_setting = env.expect.that_target(targets.build_setting_format_override).provider(ArgsInfo)
+ build_setting.actions().contains_exactly([
+ targets.c_compile.label,
+ targets.cpp_compile.label,
+ ])
+ build_setting.env().entries().contains_exactly({"APPLE_MIN_OS": _OVERRIDDEN_MIN_OS})
+
+ converted = env.expect.that_value(
+ convert_args(targets.build_setting_format_override[ArgsInfo]),
+ factory = _CONVERTED_ARGS,
+ )
+ converted.env_sets().contains_exactly([env_set(
+ actions = ["c_compile", "cpp_compile"],
+ env_entries = [env_entry(key = "APPLE_MIN_OS", value = _OVERRIDDEN_MIN_OS)],
+ )])
+ converted.flag_sets().contains_exactly([flag_set(
+ actions = ["c_compile", "cpp_compile"],
+ flag_groups = [flag_group(flags = [_OVERRIDDEN_MIN_OS])],
+ )])
+
TARGETS = [
":simple",
":some_variable",
":env_only",
":env_only_requires",
+ ":build_setting_format",
+ ":build_setting_format_override",
":with_dir",
":with_dir_and_data",
":iterate_over_optional",
@@ -361,6 +406,8 @@ TESTS = {
"env_only_requires_test": _env_only_requires_test,
"with_dir_test": _with_dir_test,
"with_dir_and_data_test": _with_dir_and_data_test,
+ "build_setting_format_test": _build_setting_format_test,
+ "build_setting_format_override_test": _build_setting_format_override_test,
"good_env_format_test": _good_env_format_test,
"good_env_format_optional_test": _good_env_format_optional_test,
}
diff --git a/tests/rule_based_toolchain/args/override_args.bzl b/tests/rule_based_toolchain/args/override_args.bzl
new file mode 100644
index 000000000..fd294afcb
--- /dev/null
+++ b/tests/rule_based_toolchain/args/override_args.bzl
@@ -0,0 +1,51 @@
+# Copyright 2024 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.
+"""Test-only helper to apply a build setting override."""
+
+load("//cc/toolchains:cc_toolchain_info.bzl", "ArgsInfo", "ArgsListInfo")
+
+_MIN_OS_FLAG = "//tests/rule_based_toolchain/args:macos_min_os_flag"
+
+def _override_min_os_transition_impl(_settings, attr):
+ return {_MIN_OS_FLAG: attr.value}
+
+_override_min_os_transition = transition(
+ implementation = _override_min_os_transition_impl,
+ inputs = [],
+ outputs = [_MIN_OS_FLAG],
+)
+
+def _override_args_impl(ctx):
+ target = ctx.attr.target
+ if type(target) == "list":
+ target = target[0]
+ return [
+ target[ArgsInfo],
+ target[ArgsListInfo],
+ ]
+
+override_args = rule(
+ implementation = _override_args_impl,
+ attrs = {
+ "_allowlist_function_transition": attr.label(
+ default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
+ ),
+ "target": attr.label(
+ cfg = _override_min_os_transition,
+ providers = [ArgsInfo],
+ mandatory = True,
+ ),
+ "value": attr.string(mandatory = True),
+ },
+)