pw_toolchain_bazel: Implement PwActionConfigInfo
This CL makes action configs use labels rather than strings, making it
more type-safe.
It also adds several tests and validation to ensure the toolchain is
valid (eg. disallow multiple action configs for the same name).
Bug: 322872628
Test: bazel build //cc_toolchain/tests/...
Change-Id: Ib775e092a3511169a5e46b4eb355d7e5ef31524f
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/189990
Reviewed-by: Armando Montanez <amontanez@google.com>
Commit-Queue: Matt Stark <msta@google.com>
diff --git a/pw_toolchain_bazel/api.rst b/pw_toolchain_bazel/api.rst
index 192ede2..0718f9c 100644
--- a/pw_toolchain_bazel/api.rst
+++ b/pw_toolchain_bazel/api.rst
@@ -506,8 +506,8 @@
name = "ar",
action_names = ["@pw_toolchain//actions:all_ar_actions"],
implies = [
- "archiver_flags",
- "linker_param_file",
+ "@pw_toolchain//features/legacy:archiver_flags",
+ "@pw_toolchain//features/legacy:linker_param_file",
],
tools = [":ar_tool"],
)
diff --git a/pw_toolchain_bazel/build_external/gcc_arm_none_eabi.BUILD b/pw_toolchain_bazel/build_external/gcc_arm_none_eabi.BUILD
index 72d8b7a..3424342 100644
--- a/pw_toolchain_bazel/build_external/gcc_arm_none_eabi.BUILD
+++ b/pw_toolchain_bazel/build_external/gcc_arm_none_eabi.BUILD
@@ -42,8 +42,8 @@
name = "arm-none-eabi-ar",
action_names = ["@pw_toolchain//actions:all_ar_actions"],
implies = [
- "archiver_flags",
- "linker_param_file",
+ "@pw_toolchain//features/legacy:archiver_flags",
+ "@pw_toolchain//features/legacy:linker_param_file",
],
tools = [":arm-none-eabi-ar_tool"],
)
diff --git a/pw_toolchain_bazel/build_external/llvm_clang.BUILD b/pw_toolchain_bazel/build_external/llvm_clang.BUILD
index cc1743b..87353c3 100644
--- a/pw_toolchain_bazel/build_external/llvm_clang.BUILD
+++ b/pw_toolchain_bazel/build_external/llvm_clang.BUILD
@@ -41,8 +41,8 @@
name = "ar",
action_names = ["@pw_toolchain//actions:all_ar_actions"],
implies = [
- "archiver_flags",
- "linker_param_file",
+ "@pw_toolchain//features/legacy:archiver_flags",
+ "@pw_toolchain//features/legacy:linker_param_file",
],
tools = [":ar_tool"],
)
diff --git a/pw_toolchain_bazel/cc_toolchain/defs.bzl b/pw_toolchain_bazel/cc_toolchain/defs.bzl
index d055a16..d168020 100644
--- a/pw_toolchain_bazel/cc_toolchain/defs.bzl
+++ b/pw_toolchain_bazel/cc_toolchain/defs.bzl
@@ -60,5 +60,5 @@
# TODO(b/322872628): Remove this.
# DO NOT USE. This is a temporary variable to allow users to migrate to
# action_config implies labels without breaking their build.
-ARCHIVER_FLAGS = "archiver_flags"
-LINKER_PARAM_FILE = "linker_param_file"
+ARCHIVER_FLAGS = "@pw_toolchain//features/legacy:archiver_flags"
+LINKER_PARAM_FILE = "@pw_toolchain//features/legacy:linker_param_file"
diff --git a/pw_toolchain_bazel/cc_toolchain/private/action_config.bzl b/pw_toolchain_bazel/cc_toolchain/private/action_config.bzl
index cd4de40..dbc08b5 100644
--- a/pw_toolchain_bazel/cc_toolchain/private/action_config.bzl
+++ b/pw_toolchain_bazel/cc_toolchain/private/action_config.bzl
@@ -15,33 +15,36 @@
load(
"@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
- "action_config",
config_lib_tool = "tool", # This is renamed to reduce name aliasing.
)
load(
":providers.bzl",
- "PwActionConfigListInfo",
+ "PwActionConfigInfo",
+ "PwActionConfigSetInfo",
"PwActionNameSetInfo",
+ "PwFeatureSetInfo",
"PwFlagSetInfo",
"PwToolInfo",
)
-load(":utils.bzl", "actionless_flag_set", "to_untyped_flag_set")
def _pw_cc_tool_impl(ctx):
"""Implementation for pw_cc_tool."""
# Remaps empty strings to `None` to match behavior of the default values.
- tool = ctx.executable.tool if ctx.executable.tool else None
- path = ctx.attr.path if ctx.attr.path else None
+ tool = ctx.executable.tool or None
+ path = ctx.attr.path or None
+
+ files = ctx.files.additional_files
+ if tool != None:
+ files = files + [tool]
+
return [
config_lib_tool(
tool = tool,
path = path,
execution_requirements = ctx.attr.execution_requirements,
),
- DefaultInfo(
- files = depset(ctx.files.additional_files + [tool]),
- ),
+ DefaultInfo(files = depset(files)),
]
pw_cc_tool = rule(
@@ -114,18 +117,17 @@
""",
)
-def _generate_action_config(ctx, action_name):
+def _generate_action_config(ctx, action_name, **kwargs):
flag_sets = []
for fs in ctx.attr.flag_sets:
- provided_fs = to_untyped_flag_set(fs[PwFlagSetInfo])
+ provided_fs = fs[PwFlagSetInfo]
if action_name in provided_fs.actions:
- flag_sets.append(actionless_flag_set(provided_fs))
- return action_config(
+ flag_sets.append(provided_fs)
+
+ return PwActionConfigInfo(
action_name = action_name,
- enabled = ctx.attr.enabled,
- tools = [tool[PwToolInfo] for tool in ctx.attr.tools],
- flag_sets = flag_sets,
- implies = ctx.attr.implies,
+ flag_sets = tuple(flag_sets),
+ **kwargs
)
def _pw_cc_action_config_impl(ctx):
@@ -143,7 +145,7 @@
# Check that the listed flag sets apply to at least one action in this group
# of action configs.
for fs in ctx.attr.flag_sets:
- provided_fs = to_untyped_flag_set(fs[PwFlagSetInfo])
+ provided_fs = fs[PwFlagSetInfo]
flag_set_applies = False
for action in action_names:
if action in provided_fs.actions:
@@ -153,10 +155,27 @@
fs.label,
ctx.label,
))
+ tools = tuple([tool[PwToolInfo] for tool in ctx.attr.tools])
+
+ common_kwargs = dict(
+ label = ctx.label,
+ tools = tools,
+ implies_features = depset(transitive = [
+ ft_set[PwFeatureSetInfo].features
+ for ft_set in ctx.attr.implies
+ ]),
+ implies_action_configs = depset([]),
+ enabled = ctx.attr.enabled,
+ )
+ action_configs = [
+ _generate_action_config(ctx, action, **common_kwargs)
+ for action in action_names
+ ]
return [
- PwActionConfigListInfo(
- action_configs = [_generate_action_config(ctx, action) for action in action_names],
+ PwActionConfigSetInfo(
+ label = ctx.label,
+ action_configs = depset(action_configs),
),
DefaultInfo(
files = depset(None, transitive = [dep[DefaultInfo].files for dep in ctx.attr.tools]),
@@ -202,18 +221,12 @@
`pw_cc_flag_set`'s `actions`, the flag will not be applied to that action.
""",
),
- "implies": attr.string_list(
- doc = """Names of features that should be automatically enabled when
-this tool is used.
-
-WARNING: If this action config implies an unknown feature, this action config
-will silently be disabled. This behavior is native to Bazel itself, and there's
-no way to detect this and emit an error instead. For this reason, be very
-cautious when listing implied features!
-""",
+ "implies": attr.label_list(
+ providers = [PwFeatureSetInfo],
+ doc = "Features that should be enabled when this action is used.",
),
},
- provides = [PwActionConfigListInfo],
+ provides = [PwActionConfigSetInfo],
doc = """Declares the configuration and selection of `pw_cc_tool` rules.
Action configs are bound to a toolchain through `action_configs`, and are the
@@ -231,8 +244,8 @@
name = "ar",
action_names = ["@pw_toolchain//actions:all_ar_actions"],
implies = [
- "archiver_flags",
- "linker_param_file",
+ "@pw_toolchain//features/legacy:archiver_flags",
+ "@pw_toolchain//features/legacy:linker_param_file",
],
tools = [":ar_tool"],
)
diff --git a/pw_toolchain_bazel/cc_toolchain/private/action_config_files.bzl b/pw_toolchain_bazel/cc_toolchain/private/action_config_files.bzl
index ce28f72..61c24d9 100644
--- a/pw_toolchain_bazel/cc_toolchain/private/action_config_files.bzl
+++ b/pw_toolchain_bazel/cc_toolchain/private/action_config_files.bzl
@@ -19,8 +19,7 @@
load(
":providers.bzl",
- "PwActionConfigInfo",
- "PwActionConfigListInfo",
+ "PwActionConfigSetInfo",
"PwActionNameSetInfo",
)
@@ -38,11 +37,7 @@
all_file_depsets = []
for dep in ctx.attr.all_action_configs:
- action_names = []
- if PwActionConfigInfo in dep:
- action_names.append(dep[PwActionConfigInfo].action_name)
- if PwActionConfigListInfo in dep:
- action_names.extend([ac.action_name for ac in dep[PwActionConfigListInfo].action_configs])
+ action_names = [ac.action_name for ac in dep[PwActionConfigSetInfo].action_configs.to_list()]
# NOTE: This intentionally doesn't do a check to ensure that the
# items in `action_names` are `pw_cc_action_config`s because the
@@ -74,7 +69,7 @@
pw_cc_action_config_file_collector = rule(
implementation = _pw_cc_action_config_file_collector_impl,
attrs = {
- "all_action_configs": attr.label_list(default = []),
+ "all_action_configs": attr.label_list(default = [], providers = [PwActionConfigSetInfo]),
"collect_files_from_actions": attr.label_list(
providers = [PwActionNameSetInfo],
doc = """Collects files from tools that apply to the listed action names.
diff --git a/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl b/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl
index 478a27e..b316a2d 100644
--- a/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl
+++ b/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl
@@ -16,7 +16,6 @@
load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
load(
"@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
- "action_config",
"feature",
"flag_group",
"flag_set",
@@ -29,8 +28,7 @@
load("//features:builtin_features.bzl", "BUILTIN_FEATURES")
load(
":providers.bzl",
- "PwActionConfigInfo",
- "PwActionConfigListInfo",
+ "PwActionConfigSetInfo",
"PwFeatureInfo",
"PwFeatureSetInfo",
"PwFlagSetInfo",
@@ -38,9 +36,7 @@
load(
":utils.bzl",
"ALL_FILE_GROUPS",
- "actionless_flag_set",
"to_untyped_config",
- "to_untyped_flag_set",
)
# These attributes of pw_cc_toolchain are deprecated.
@@ -57,7 +53,7 @@
PW_CC_TOOLCHAIN_CONFIG_ATTRS = {
"action_configs": "List of `pw_cc_action_config` labels that bind tools to the appropriate actions",
- "action_config_flag_sets": "List of `pw_cc_flag_set`s to apply to their respective action configs",
+ "unconditional_flag_sets": "List of `pw_cc_flag_set`s to apply to their respective action configs",
"toolchain_features": "List of `pw_cc_feature`s that this toolchain supports",
# Attributes originally part of create_cc_toolchain_config_info.
@@ -157,52 +153,6 @@
],
)
-def _extend_action_set_flags(action, flag_sets_by_action):
- extended_flags = flag_sets_by_action.get(action.action_name, default = [])
- for x in extended_flags:
- for y in action.flag_sets:
- if x == y:
- # TODO: b/311679764 - Propagate labels so we can raise the label
- # as part of the warning.
- fail("Flag set in `action_config_flag_sets` is already bound to the `{}` tool".format(action.action_name))
- return action_config(
- action_name = action.action_name,
- enabled = action.enabled,
- tools = action.tools,
- flag_sets = action.flag_sets + extended_flags,
- implies = action.implies,
- )
-
-def _collect_action_configs(ctx, flag_sets_by_action):
- known_actions = {}
- action_configs = []
- for ac_dep in ctx.attr.action_configs:
- temp_actions = []
- if PwActionConfigInfo in ac_dep:
- temp_actions.append(ac_dep[PwActionConfigInfo])
- if PwActionConfigListInfo in ac_dep:
- temp_actions.extend([ac for ac in ac_dep[PwActionConfigListInfo].action_configs])
- if PwActionConfigListInfo not in ac_dep and PwActionConfigInfo not in ac_dep:
- fail(
- "{} in `action_configs` is not a `pw_cc_action_config`".format(
- ac_dep.label,
- ),
- )
- for action in temp_actions:
- if action.action_name in known_actions:
- fail("In {} both {} and {} implement `{}`".format(
- ctx.label,
- ac_dep.label,
- known_actions[action.action_name],
- action.action_name,
- ))
-
- # Track which labels implement each action name for better error
- # reporting.
- known_actions[action.action_name] = ac_dep.label
- action_configs.append(_extend_action_set_flags(action, flag_sets_by_action))
- return action_configs
-
def _archiver_flags(is_mac):
"""Returns flags for llvm-ar."""
if is_mac:
@@ -210,28 +160,6 @@
else:
return ["rcsD"]
-def _create_action_flag_set_map(flag_sets):
- """Creates a mapping of action names to flag sets.
-
- Args:
- flag_sets: the flag sets to expand.
-
- Returns:
- Dictionary mapping action names to lists of PwFlagSetInfo providers.
- """
- flag_sets_by_action = {}
- for fs in flag_sets:
- handled_actions = {}
- for action in fs.actions:
- if action not in flag_sets_by_action:
- flag_sets_by_action[action] = []
-
- # Dedupe action set list.
- if action not in handled_actions:
- handled_actions[action] = True
- flag_sets_by_action[action].append(actionless_flag_set(fs))
- return flag_sets_by_action
-
def _pw_cc_toolchain_config_impl(ctx):
"""Rule that provides a CcToolchainConfigInfo.
@@ -241,11 +169,6 @@
Returns:
CcToolchainConfigInfo
"""
- flag_sets_by_action = _create_action_flag_set_map([
- to_untyped_flag_set(dep[PwFlagSetInfo], known = {})
- for dep in ctx.attr.action_config_flag_sets
- ])
- all_actions = _collect_action_configs(ctx, flag_sets_by_action)
builtin_include_dirs = ctx.attr.cxx_builtin_include_directories if ctx.attr.cxx_builtin_include_directories else []
sysroot_dir = ctx.attr.builtin_sysroot if ctx.attr.builtin_sysroot else None
@@ -256,14 +179,22 @@
for feature_set in ctx.attr.toolchain_features
],
))
- out = to_untyped_config(feature_set)
+ action_config_set = PwActionConfigSetInfo(
+ label = ctx.label,
+ action_configs = depset(transitive = [
+ acs[PwActionConfigSetInfo].action_configs
+ for acs in ctx.attr.action_configs
+ ]),
+ )
+ flag_sets = [fs[PwFlagSetInfo] for fs in ctx.attr.unconditional_flag_sets]
+ out = to_untyped_config(feature_set, action_config_set, flag_sets)
# TODO: b/297413805 - This could be externalized.
out.features.append(_archiver_flags_feature(ctx.attr.target_libc == "macosx"))
return cc_common.create_cc_toolchain_config_info(
ctx = ctx,
- action_configs = all_actions,
+ action_configs = out.action_configs,
features = out.features,
cxx_builtin_include_directories = builtin_include_dirs,
toolchain_identifier = ctx.attr.toolchain_identifier,
@@ -282,8 +213,8 @@
implementation = _pw_cc_toolchain_config_impl,
attrs = {
# Attributes new to this rule.
- "action_configs": attr.label_list(),
- "action_config_flag_sets": attr.label_list(providers = [PwFlagSetInfo]),
+ "action_configs": attr.label_list(providers = [PwActionConfigSetInfo]),
+ "unconditional_flag_sets": attr.label_list(providers = [PwFlagSetInfo]),
"toolchain_features": attr.label_list(providers = [PwFeatureSetInfo]),
# Attributes from create_cc_toolchain_config_info.
@@ -403,7 +334,7 @@
)
return file_group_name
-def pw_cc_toolchain(**kwargs):
+def pw_cc_toolchain(action_config_flag_sets = None, **kwargs):
"""A suite of cc_toolchain, pw_cc_toolchain_config, and *_files rules.
Generated rules:
@@ -418,9 +349,14 @@
configs not associated with any other *_files group.
Args:
+ action_config_flag_sets: Deprecated. Do not use.
**kwargs: All attributes supported by either cc_toolchain or pw_cc_toolchain_config.
"""
+ # TODO(b/322872628): Remove this once it's no longer in use.
+ if action_config_flag_sets != None:
+ kwargs["unconditional_flag_sets"] = action_config_flag_sets
+
_check_args(native.package_relative_label(kwargs["name"]), kwargs)
# Generate *_files groups.
diff --git a/pw_toolchain_bazel/cc_toolchain/private/providers.bzl b/pw_toolchain_bazel/cc_toolchain/private/providers.bzl
index f52fa61..91deb5a 100644
--- a/pw_toolchain_bazel/cc_toolchain/private/providers.bzl
+++ b/pw_toolchain_bazel/cc_toolchain/private/providers.bzl
@@ -15,7 +15,6 @@
load(
"@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
- "ActionConfigInfo",
"EnvEntryInfo",
"EnvSetInfo",
"FlagGroupInfo",
@@ -30,16 +29,6 @@
# provider, and convert them to a tuple in the constructor to ensure
# immutability.
-# To reduce the number of require pw_cc_action_config rules, a
-# pw_cc_action_config provides a list of ActionConfigInfo providers rather than
-# a simpler 1:1 mapping.
-PwActionConfigListInfo = provider(
- doc = "A provider containing a list of ActionConfigInfo providers.",
- fields = {
- "action_configs": "List[ActionConfigInfo]: A list of ActionConfigInfo providers.",
- },
-)
-
PwActionNameInfo = ActionNameInfo
PwActionNameSetInfo = ActionNameSetInfo
@@ -92,5 +81,24 @@
fields = {},
)
-PwActionConfigInfo = ActionConfigInfo
+PwActionConfigInfo = provider(
+ doc = "A type-safe version of @bazel_tools's ActionConfigInfo",
+ fields = {
+ "label": "Label: The label that defined this action config. Put this in error messages for easy debugging",
+ "action_name": "str: The name of the action",
+ "enabled": "bool: If True, this action is enabled unless a rule type explicitly marks it as unsupported",
+ "tools": "Sequence[ToolInfo]: The tool applied to the action will be the first tool in the sequence with a feature set that matches the feature configuration",
+ "flag_sets": "Sequence[FlagSetInfo]: Set of flag sets the action sets",
+ "implies_features": "depset[FeatureInfo]: Set of features implied by this action config",
+ "implies_action_configs": "depset[ActionConfigInfo]: Set of action configs enabled by this action config",
+ },
+)
+
+PwActionConfigSetInfo = provider(
+ doc = "A set of action configs",
+ fields = {
+ "label": "Label: The label that defined this action config set. Put this in error messages for easy debugging",
+ "action_configs": "depset[ActionConfigInfo]: A set of action configs",
+ },
+)
PwToolInfo = ToolInfo
diff --git a/pw_toolchain_bazel/cc_toolchain/private/utils.bzl b/pw_toolchain_bazel/cc_toolchain/private/utils.bzl
index 3e8a1c3..c0d5624 100644
--- a/pw_toolchain_bazel/cc_toolchain/private/utils.bzl
+++ b/pw_toolchain_bazel/cc_toolchain/private/utils.bzl
@@ -15,11 +15,13 @@
load(
"@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
+ rules_cc_action_config = "action_config",
rules_cc_feature = "feature",
rules_cc_feature_set = "feature_set",
rules_cc_flag_set = "flag_set",
rules_cc_with_feature_set = "with_feature_set",
)
+load(":providers.bzl", "PwFlagSetInfo")
visibility(["//cc_toolchain/tests/..."])
@@ -34,19 +36,6 @@
"strip_files": ["@pw_toolchain//actions:strip"],
}
-def actionless_flag_set(flag_set_to_copy):
- """Copies a flag_set, stripping `actions`.
-
- Args:
- flag_set_to_copy: The base flag_set to copy.
- Returns:
- flag_set with empty `actions` list.
- """
- return rules_cc_flag_set(
- with_features = flag_set_to_copy.with_features,
- flag_groups = flag_set_to_copy.flag_groups,
- )
-
def _ensure_fulfillable(any_of, known, label, fail = fail):
# Requirements can be fulfilled if there are no requirements.
fulfillable = not any_of
@@ -63,7 +52,7 @@
if not fulfillable:
fail("%s cannot possibly be enabled (none of the constraints it requires fully exist). Either remove it from your toolchain, or add the requirements." % label)
-def to_untyped_flag_set(flag_set, known, fail = fail):
+def _to_untyped_flag_set(flag_set, known, fail = fail):
"""Converts a PwFlagSet to rules_cc's flag set."""
_ensure_fulfillable(
any_of = [constraint.all_of for constraint in flag_set.requires_any_of],
@@ -122,7 +111,7 @@
name = feature.name,
enabled = feature.enabled,
flag_sets = [
- to_untyped_flag_set(flag_set, known, fail = fail)
+ _to_untyped_flag_set(flag_set, known, fail = fail)
for flag_set in feature.flag_sets.to_list()
],
env_sets = [
@@ -134,16 +123,51 @@
provides = list(feature.provides),
)
-def to_untyped_config(feature_set, fail = fail):
+def _to_untyped_action_config(action_config, extra_flag_sets, known, fail = fail):
+ # De-dupe, in case the same flag set was specified for both unconditional
+ # and for a specific action config.
+ flag_sets = depset(
+ list(action_config.flag_sets) + extra_flag_sets,
+ order = "preorder",
+ ).to_list()
+ return rules_cc_action_config(
+ action_name = action_config.action_name,
+ enabled = action_config.enabled,
+ tools = list(action_config.tools),
+ flag_sets = [
+ _to_untyped_flag_set(
+ # Make the flag sets actionless.
+ PwFlagSetInfo(
+ label = flag_set.label,
+ actions = tuple(),
+ requires_any_of = flag_set.requires_any_of,
+ flag_groups = flag_set.flag_groups,
+ ),
+ known = known,
+ fail = fail,
+ )
+ for flag_set in flag_sets
+ ],
+ implies = _to_untyped_implies(action_config, known, fail = fail),
+ )
+
+def to_untyped_config(feature_set, action_config_set, flag_sets, fail = fail):
"""Converts Pigweed providers into a format suitable for rules_cc.
Args:
feature_set: PwFeatureSetInfo: Features available in the toolchain
+ action_config_set: ActionConfigSetInfo: Set of defined action configs
+ flag_sets: Flag sets that are unconditionally applied
fail: The fail function. Only change this during testing.
Returns:
A struct containing parameters suitable to pass to
cc_common.create_cc_toolchain_config_info.
"""
+ flag_sets_by_action = {}
+ for flag_set in flag_sets:
+ for action in flag_set.actions:
+ flag_sets_by_action.setdefault(action, []).append(flag_set)
+
known_labels = {}
known_feature_names = {}
feature_list = feature_set.features.to_list()
@@ -160,6 +184,27 @@
untyped_feature = _to_untyped_feature(feature, known = known_labels, fail = fail)
if untyped_feature != None:
untyped_features.append(untyped_feature)
+
+ acs = action_config_set.action_configs.to_list()
+ known_actions = {}
+ untyped_acs = []
+ for ac in acs:
+ if ac.action_name in known_actions:
+ fail("In %s, both %s and %s implement %s" % (
+ action_config_set.label,
+ ac.label,
+ known_actions[ac.action_name],
+ ac.action_name,
+ ))
+ known_actions[ac.action_name] = ac.label
+ untyped_acs.append(_to_untyped_action_config(
+ ac,
+ extra_flag_sets = flag_sets_by_action.get(ac.action_name, []),
+ known = known_labels,
+ fail = fail,
+ ))
+
return struct(
features = untyped_features,
+ action_configs = untyped_acs,
)
diff --git a/pw_toolchain_bazel/cc_toolchain/tests/action_configs/BUILD.bazel b/pw_toolchain_bazel/cc_toolchain/tests/action_configs/BUILD.bazel
new file mode 100644
index 0000000..aae465a
--- /dev/null
+++ b/pw_toolchain_bazel/cc_toolchain/tests/action_configs/BUILD.bazel
@@ -0,0 +1,44 @@
+# Copyright 2024 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+load(
+ "//cc_toolchain:defs.bzl",
+ "pw_cc_action_config",
+ "pw_cc_tool",
+)
+load(":test_action_configs.bzl", "test_action_configs")
+
+package(default_visibility = ["//cc_toolchain/tests:__subpackages__"])
+
+pw_cc_tool(
+ name = "system_clang",
+ path = "/usr/bin/clang",
+)
+
+pw_cc_action_config(
+ name = "all_c_compile",
+ action_names = ["//actions:all_c_compiler_actions"],
+ implies = ["//cc_toolchain/tests/features:foo"],
+ tools = [":system_clang"],
+)
+
+pw_cc_action_config(
+ name = "c_compile",
+ action_names = ["//actions:c_compile"],
+ tools = [":system_clang"],
+)
+
+test_action_configs(
+ name = "test_action_configs",
+)
diff --git a/pw_toolchain_bazel/cc_toolchain/tests/action_configs/test_action_configs.bzl b/pw_toolchain_bazel/cc_toolchain/tests/action_configs/test_action_configs.bzl
new file mode 100644
index 0000000..bbfa3af
--- /dev/null
+++ b/pw_toolchain_bazel/cc_toolchain/tests/action_configs/test_action_configs.bzl
@@ -0,0 +1,68 @@
+# Copyright 2024 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+"""Tests for pw_cc_feature and pw_cc_feature_set."""
+
+load(
+ "//cc_toolchain/tests:utils.bzl",
+ "assert_eq",
+ "assert_fail",
+ "generate_test_rule",
+)
+
+visibility("private")
+
+def _test_action_configs_impl(_ctx, action_configs, features, flag_sets, to_untyped_config, **_):
+ def get_action_configs(**kwargs):
+ action_configs = to_untyped_config(**kwargs).action_configs
+ actions = [(action.action_name, action) for action in action_configs]
+ deduped_actions = dict(actions)
+
+ # Verify no duplicates
+ assert_eq(sorted([x[0] for x in actions]), sorted(deduped_actions))
+ return deduped_actions
+
+ # Verify that we validate that features with duplicate action names are not
+ # permitted
+ assert_fail(
+ to_untyped_config,
+ action_configs = [action_configs.all_c_compile, action_configs.c_compile],
+ features = [features.foo],
+ )
+
+ # Verify that the validation on implied features works (foo must exist for
+ # an action config that implies foo).
+ assert_fail(to_untyped_config, action_configs = [action_configs.all_c_compile])
+ assert_eq(
+ get_action_configs(
+ action_configs = [action_configs.all_c_compile],
+ features = [features.foo],
+ )["c-compile"].implies,
+ ["foo"],
+ )
+
+ # Verify that flag sets get added iff they match the action.
+ acs = get_action_configs(
+ action_configs = [action_configs.all_c_compile],
+ features = [features.foo],
+ flag_sets = [flag_sets.bar],
+ )
+ assert_eq(
+ {k: [fs.flag_groups for fs in v.flag_sets] for k, v in acs.items()},
+ {
+ "c-compile": [list(flag_sets.bar.flag_groups)],
+ "cc-flags-make-variable": [],
+ },
+ )
+
+test_action_configs = generate_test_rule(_test_action_configs_impl)
diff --git a/pw_toolchain_bazel/cc_toolchain/tests/utils.bzl b/pw_toolchain_bazel/cc_toolchain/tests/utils.bzl
index d306231..c856711 100644
--- a/pw_toolchain_bazel/cc_toolchain/tests/utils.bzl
+++ b/pw_toolchain_bazel/cc_toolchain/tests/utils.bzl
@@ -15,6 +15,7 @@
load(
"//cc_toolchain/private:providers.bzl",
+ "PwActionConfigSetInfo",
"PwFeatureConstraintInfo",
"PwFeatureInfo",
"PwFeatureSetInfo",
@@ -92,9 +93,12 @@
PwFeatureConstraintInfo: "feature_constraints",
PwFeatureInfo: "features",
PwFeatureSetInfo: "feature_sets",
+ PwActionConfigSetInfo: "action_configs",
}
_PROVIDERS = {
+ "//cc_toolchain/tests/action_configs:all_c_compile": [PwActionConfigSetInfo],
+ "//cc_toolchain/tests/action_configs:c_compile": [PwActionConfigSetInfo],
"//cc_toolchain/tests/features:bar": [PwFeatureInfo, PwFeatureSetInfo],
"//cc_toolchain/tests/features:baz": [PwFeatureInfo, PwFeatureSetInfo],
"//cc_toolchain/tests/features:conflict": [PwFeatureInfo, PwFeatureSetInfo],
@@ -131,12 +135,19 @@
for provider in _PROVIDERS["//%s:%s" % (pkg, name)]:
providers[_RULES[provider]][name] = target[provider]
- def to_untyped_config(features = [], feature_sets = [], fail = fail):
+ def to_untyped_config(features = [], feature_sets = [], action_configs = [], flag_sets = [], fail = fail):
feature_set = PwFeatureSetInfo(features = depset(
features + [ft[PwFeatureInfo] for ft in ctx.attr.builtin_features],
transitive = [fs.features for fs in feature_sets],
))
- return _to_untyped_config(feature_set, fail = fail)
+ action_config_set = PwActionConfigSetInfo(
+ label = ctx.label,
+ action_configs = depset(transitive = [
+ acs.action_configs
+ for acs in action_configs
+ ]),
+ )
+ return _to_untyped_config(feature_set, action_config_set, flag_sets, fail = fail)
kwargs = {k: struct(**v) for k, v in providers.items()}
return implementation(ctx, to_untyped_config = to_untyped_config, **kwargs)