aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmando Montanez <amontanez@google.com>2023-11-15 03:20:51 +0000
committerCQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com>2023-11-15 03:20:51 +0000
commit3c3dc003812d05bb31a47b93ff1df5666ad3db7f (patch)
treec59b9ba131df562f19581320098b3f83a229a6cf
parent6372cdef44d0338d47108c90c55ad38debcf6c30 (diff)
downloadpigweed-3c3dc003812d05bb31a47b93ff1df5666ad3db7f.tar.gz
pw_toolchain_bazel: Introduce pw_cc_flag_set and pw_cc_flag_group
As the first step of implementing toolchains as proposed in SEED-0113, this CL introduces pw_cc_flag_set and pw_cc_flag_group. These will be the core building blocks for specifying flags that will be bound to toolchains. As an illustrative example, moves the warning "features" to be pw_cc_flag_set rules instead. Bug: b/309533028 Change-Id: I3eaa6ac92511d2973bd5120432878d6953d14793 Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/179932 Reviewed-by: Ted Pudlik <tpudlik@google.com> Commit-Queue: Armando Montanez <amontanez@google.com> Reviewed-by: Kayce Basques <kayce@google.com>
-rw-r--r--pw_toolchain/host_clang/BUILD.bazel27
-rw-r--r--pw_toolchain_bazel/BUILD.gn5
-rw-r--r--pw_toolchain_bazel/api.rst298
-rw-r--r--pw_toolchain_bazel/cc_toolchain/defs.bzl23
-rw-r--r--pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl63
-rw-r--r--pw_toolchain_bazel/cc_toolchain/private/flag_set.bzl225
-rw-r--r--pw_toolchain_bazel/cc_toolchain/private/utils.bzl24
-rw-r--r--pw_toolchain_bazel/docs.rst51
8 files changed, 687 insertions, 29 deletions
diff --git a/pw_toolchain/host_clang/BUILD.bazel b/pw_toolchain/host_clang/BUILD.bazel
index bc2fb1ff5..7f8009787 100644
--- a/pw_toolchain/host_clang/BUILD.bazel
+++ b/pw_toolchain/host_clang/BUILD.bazel
@@ -14,6 +14,9 @@
load(
"@pw_toolchain//cc_toolchain:defs.bzl",
+ "ALL_CPP_COMPILER_ACTIONS",
+ "ALL_C_COMPILER_ACTIONS",
+ "pw_cc_flag_set",
"pw_cc_toolchain",
"pw_cc_toolchain_feature",
)
@@ -56,9 +59,10 @@ pw_cc_toolchain_feature(
# Although we use similar warnings for clang and arm_gcc, we don't have one
# centralized list, since we might want to use different warnings based on the
# compiler in the future.
-pw_cc_toolchain_feature(
+pw_cc_flag_set(
name = "warnings",
- copts = [
+ actions = ALL_C_COMPILER_ACTIONS + ALL_CPP_COMPILER_ACTIONS,
+ flags = [
"-Wall",
"-Wextra",
# Make all warnings errors, except for the exemptions below.
@@ -68,9 +72,10 @@ pw_cc_toolchain_feature(
],
)
-pw_cc_toolchain_feature(
+pw_cc_flag_set(
name = "no_unknown_warning_option",
- copts = [
+ actions = ALL_C_COMPILER_ACTIONS + ALL_CPP_COMPILER_ACTIONS,
+ flags = [
"-Wno-unknown-warning-option",
],
)
@@ -87,6 +92,9 @@ pw_cc_toolchain(
name = "host_toolchain_macos",
abi_libc_version = "unknown",
abi_version = "unknown",
+ action_config_flag_sets = [
+ ":warnings",
+ ],
all_files = "@llvm_toolchain//:all",
ar = "@llvm_toolchain//:bin/llvm-ar",
ar_files = "@llvm_toolchain//:all",
@@ -97,7 +105,6 @@ pw_cc_toolchain(
cpp = "@llvm_toolchain//:bin/clang++",
dwp_files = "@llvm_toolchain//:all",
feature_deps = [
- ":warnings",
"@pw_toolchain//features:no_default_cpp_stdlib",
":macos_stdlib",
"@pw_toolchain//features/macos:macos_sysroot",
@@ -145,6 +152,9 @@ pw_cc_toolchain(
name = "host_toolchain_linux",
abi_libc_version = "unknown",
abi_version = "unknown",
+ action_config_flag_sets = [
+ ":warnings",
+ ],
all_files = ":all_linux_files",
ar = "@llvm_toolchain//:bin/llvm-ar",
ar_files = ":all_linux_files",
@@ -155,7 +165,6 @@ pw_cc_toolchain(
cpp = "@llvm_toolchain//:bin/clang++",
dwp_files = ":all_linux_files",
feature_deps = [
- ":warnings",
":linux_sysroot",
"@pw_toolchain//features:c++17",
"@pw_toolchain//features:debugging",
@@ -188,6 +197,10 @@ pw_cc_toolchain(
name = "host_toolchain_linux_kythe",
abi_libc_version = "unknown",
abi_version = "unknown",
+ action_config_flag_sets = [
+ ":warnings",
+ ":no_unknown_warning_option",
+ ],
all_files = ":all_linux_files",
ar = "@llvm_toolchain//:bin/llvm-ar",
ar_files = ":all_linux_files",
@@ -198,8 +211,6 @@ pw_cc_toolchain(
cpp = "@llvm_toolchain//:bin/clang++",
dwp_files = ":all_linux_files",
feature_deps = [
- ":warnings",
- ":no_unknown_warning_option",
":linux_sysroot",
"@pw_toolchain//features:c++17",
"@pw_toolchain//features:debugging",
diff --git a/pw_toolchain_bazel/BUILD.gn b/pw_toolchain_bazel/BUILD.gn
index 4537bcc74..bf6234783 100644
--- a/pw_toolchain_bazel/BUILD.gn
+++ b/pw_toolchain_bazel/BUILD.gn
@@ -24,5 +24,8 @@ pw_test_group("tests") {
}
pw_doc_group("docs") {
- sources = [ "docs.rst" ]
+ sources = [
+ "api.rst",
+ "docs.rst",
+ ]
}
diff --git a/pw_toolchain_bazel/api.rst b/pw_toolchain_bazel/api.rst
new file mode 100644
index 000000000..e0e61d33f
--- /dev/null
+++ b/pw_toolchain_bazel/api.rst
@@ -0,0 +1,298 @@
+.. _module-pw_toolchain_bazel-api:
+
+=============
+API reference
+=============
+
+.. py:class:: pw_cc_toolchain
+
+ This rule is the core of a C/C++ toolchain definition. Critically, it is
+ intended to fully specify the following:
+
+ * Which tools to use for various compile/link actions.
+ * Which `well-known features <https://bazel.build/docs/cc-toolchain-config-reference#wellknown-features>`_
+ are supported.
+ * Which flags to apply to various actions.
+
+ .. py:attribute:: feature_deps
+ :type: List[label]
+
+ ``pw_cc_toolchain_feature`` labels that provide features for this toolchain.
+
+ .. py:attribute:: ar
+ :type: File
+
+ Path to the tool to use for ``ar`` (static link) actions.
+
+ .. py:attribute:: cpp
+ :type: File
+
+ Path to the tool to use for C++ compile actions.
+
+ .. py:attribute:: gcc
+ :type: File
+
+ Path to the tool to use for C compile actions.
+
+ .. py:attribute:: gcov
+ :type: File
+
+ Path to the tool to use for generating code coverage data.
+
+ .. py:attribute:: ld
+ :type: File
+
+ Path to the tool to use for link actions.
+
+ .. py:attribute:: strip
+ :type: File
+
+ Path to the tool to use for strip actions.
+
+ .. py:attribute:: objcopy
+ :type: File
+
+ Path to the tool to use for objcopy actions.
+
+ .. py:attribute:: objdump
+ :type: File
+
+ Path to the tool to use for objdump actions.
+
+ .. py:attribute:: action_config_flag_sets
+ :type: List[label]
+
+ List of flag sets to apply to the respective ``action_config``\s. The vast
+ majority of labels listed here will point to :py:class:`pw_cc_flag_set`
+ rules.
+
+ .. py:attribute:: toolchain_identifier
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+ .. py:attribute:: host_system_name
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+ .. py:attribute:: target_system_name
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+ .. py:attribute:: target_cpu
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+ .. py:attribute:: target_libc
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+ .. py:attribute:: compiler
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+ .. py:attribute:: abi_version
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+ .. py:attribute:: abi_libc_version
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+ .. py:attribute:: cc_target_os
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+.. py:class:: pw_cc_flag_set
+
+ Declares an ordered set of flags bound to a set of actions.
+
+ Flag sets can be attached to a :py:class:`pw_cc_toolchain` via
+ :py:attr:`pw_cc_toolchain.action_config_flag_sets`\.
+
+ Examples:
+
+ .. code-block:: py
+
+ pw_cc_flag_set(
+ name = "warnings_as_errors",
+ flags = ["-Werror"],
+ )
+
+ pw_cc_flag_set(
+ name = "layering_check",
+ flag_groups = [
+ ":strict_module_headers",
+ ":dependent_module_map_files",
+ ],
+ )
+
+ .. inclusive-language: disable
+
+ Note: In the vast majority of cases, alphabetical sorting is not desirable
+ for the :py:attr:`pw_cc_flag_set.flags` and
+ :py:attr:`pw_cc_flag_set.flag_groups` attributes.
+ `Buildifier <https://github.com/bazelbuild/buildtools/blob/master/buildifier/README.md>`_
+ shouldn't ever try to sort these, but in the off chance it starts to these
+ members should be listed as exceptions in the ``SortableDenylist``.
+
+ .. inclusive-language: enable
+
+ .. py:attribute:: actions
+ :type: List[str]
+
+ A list of action names that this flag set applies to.
+
+ .. inclusive-language: disable
+
+ Valid choices are listed at
+ `@rules_cc//cc:action_names.bzl <https://github.com/bazelbuild/bazel/blob/master/tools/build_defs/cc/action_names.bzl>`_\.
+
+ .. inclusive-language: enable
+
+ It is possible for some needed action names to not be enumerated in this list,
+ so there is not rigid validation for these strings. Prefer using constants
+ rather than manually typing action names.
+
+ .. py:attribute:: flags
+ :type: List[str]
+
+ Flags that should be applied to the specified actions.
+
+ These are evaluated in order, with earlier flags appearing earlier in the
+ invocation of the underlying tool. If you need expansion logic, prefer
+ enumerating flags in a :py:class:`pw_cc_flag_group` or create a custom
+ rule that provides ``FlagGroupInfo``.
+
+ Note: :py:attr:`pw_cc_flag_set.flags` and
+ :py:attr:`pw_cc_flag_set.flag_groups` are mutually exclusive.
+
+ .. py:attribute:: flag_groups
+ :type: List[label]
+
+ Labels pointing to :py:class:`pw_cc_flag_group` rules.
+
+ This is intended to be compatible with any other rules that provide
+ ``FlagGroupInfo``. These are evaluated in order, with earlier flag groups
+ appearing earlier in the invocation of the underlying tool.
+
+ Note: :py:attr:`pw_cc_flag_set.flag_groups` and
+ :py:attr:`pw_cc_flag_set.flags` are mutually exclusive.
+
+.. py:class:: pw_cc_flag_group
+
+ Declares an (optionally parametric) ordered set of flags.
+
+ :py:class:`pw_cc_flag_group` rules are expected to be consumed exclusively by
+ :py:class:`pw_cc_flag_set` rules. Though simple lists of flags can be
+ expressed by populating ``flags`` on a :py:class:`pw_cc_flag_set`,
+ :py:class:`pw_cc_flag_group` provides additional power in the following two
+ ways:
+
+ 1. Iteration and conditional expansion. Using
+ :py:attr:`pw_cc_flag_group.iterate_over`,
+ :py:attr:`pw_cc_flag_group.expand_if_available`\, and
+ :py:attr:`pw_cc_flag_group.expand_if_not_available`\, more complex
+ flag expressions can be made. This is critical for implementing things
+ like the ``libraries_to_link`` feature, where library names are
+ transformed into flags that end up in the final link invocation.
+
+ Note: ``expand_if_equal``, ``expand_if_true``, and ``expand_if_false``
+ are not yet supported.
+
+ 2. Flags are tool-independent. A :py:class:`pw_cc_flag_group` expresses
+ ordered flags that may be reused across various
+ :py:class:`pw_cc_flag_set` rules. This is useful for cases where multiple
+ :py:class:`pw_cc_flag_set` rules must be created to implement a feature
+ for which flags are slightly different depending on the action (e.g.
+ compile vs link). Common flags can be expressed in a shared
+ :py:class:`pw_cc_flag_group`, and the differences can be relegated to
+ separate :py:class:`pw_cc_flag_group` instances.
+
+ Examples:
+
+ .. code-block:: py
+
+ pw_cc_flag_group(
+ name = "user_compile_flag_expansion",
+ flags = ["%{user_compile_flags}"],
+ iterate_over = "user_compile_flags",
+ expand_if_available = "user_compile_flags",
+ )
+
+ # This flag_group might be referenced from various FDO-related
+ # `pw_cc_flag_set` rules. More importantly, the flag sets pulling this in
+ # may apply to different sets of actions.
+ pw_cc_flag_group(
+ name = "fdo_profile_correction",
+ flags = ["-fprofile-correction"],
+ expand_if_available = "fdo_profile_path",
+ )
+
+ .. py:attribute:: flags
+ :type: List[str]
+
+ List of flags provided by this rule.
+
+ For extremely complex expressions of flags that require nested flag groups
+ with multiple layers of expansion, prefer creating a custom rule in
+ `Starlark <https://bazel.build/rules/language>`_ that provides
+ ``FlagGroupInfo`` or ``FlagSetInfo``.
+
+
+ .. py:attribute:: iterate_over
+ :type: str
+
+ Expands :py:attr:`pw_cc_flag_group.flags` for items in the named list.
+
+ Toolchain actions have various variables accessible as names that can be
+ used to guide flag expansions. For variables that are lists,
+ :py:attr:`pw_cc_flag_group.iterate_over` must be used to expand the list into a series of flags.
+
+ Note that :py:attr:`pw_cc_flag_group.iterate_over` is the string name of a
+ build variable, and not an actual list. Valid options are listed in the
+ `C++ Toolchain Configuration <https://bazel.build/docs/cc-toolchain-config-reference#cctoolchainconfiginfo-build-variables>`_
+ reference.
+
+
+
+ Note that the flag expansion stamps out the entire list of flags in
+ :py:attr:`pw_cc_flag_group.flags` once for each item in the list.
+
+ Example:
+
+ .. code-block:: py
+
+ # Expands each path in ``system_include_paths`` to a series of
+ # ``-isystem`` includes.
+ #
+ # Example input:
+ # system_include_paths = ["/usr/local/include", "/usr/include"]
+ #
+ # Expected result:
+ # "-isystem /usr/local/include -isystem /usr/include"
+ pw_cc_flag_group(
+ name = "system_include_paths",
+ flags = ["-isystem", "%{system_include_paths}"],
+ iterate_over = "system_include_paths",
+ )
+
+ .. py:attribute:: expand_if_available
+ :type: str
+
+ Expands the expression in :py:attr:`pw_cc_flag_group.flags` if the
+ specified build variable is set.
+
+ .. py:attribute:: expand_if_not_available
+ :type: str
+
+ Expands the expression in :py:attr:`pw_cc_flag_group.flags` if the
+ specified build variable is **NOT** set.
diff --git a/pw_toolchain_bazel/cc_toolchain/defs.bzl b/pw_toolchain_bazel/cc_toolchain/defs.bzl
index 7847c4eb2..b5340c54b 100644
--- a/pw_toolchain_bazel/cc_toolchain/defs.bzl
+++ b/pw_toolchain_bazel/cc_toolchain/defs.bzl
@@ -20,15 +20,38 @@ load(
_pw_cc_toolchain = "pw_cc_toolchain",
)
load(
+ "//cc_toolchain/private:flag_set.bzl",
+ _pw_cc_flag_group = "pw_cc_flag_group",
+ _pw_cc_flag_set = "pw_cc_flag_set",
+)
+load(
"//cc_toolchain/private:toolchain_feature.bzl",
_pw_cc_toolchain_feature = "pw_cc_toolchain_feature",
)
+load(
+ "//cc_toolchain/private:utils.bzl",
+ _ALL_AR_ACTIONS = "ALL_AR_ACTIONS",
+ _ALL_ASM_ACTIONS = "ALL_ASM_ACTIONS",
+ _ALL_CPP_COMPILER_ACTIONS = "ALL_CPP_COMPILER_ACTIONS",
+ _ALL_C_COMPILER_ACTIONS = "ALL_C_COMPILER_ACTIONS",
+ _ALL_LINK_ACTIONS = "ALL_LINK_ACTIONS",
+)
+
+ALL_ASM_ACTIONS = _ALL_ASM_ACTIONS
+ALL_C_COMPILER_ACTIONS = _ALL_C_COMPILER_ACTIONS
+ALL_CPP_COMPILER_ACTIONS = _ALL_CPP_COMPILER_ACTIONS
+ALL_LINK_ACTIONS = _ALL_LINK_ACTIONS
+ALL_AR_ACTIONS = _ALL_AR_ACTIONS
# TODO(b/301004620): Remove when bazel 7 is released and this constant exists in
# ACTION_NAMES
OBJ_COPY_ACTION_NAME = _OBJ_COPY_ACTION_NAME
OBJ_DUMP_ACTION_NAME = _OBJ_DUMP_ACTION_NAME
+pw_cc_flag_group = _pw_cc_flag_group
+pw_cc_flag_set = _pw_cc_flag_set
+
pw_cc_toolchain = _pw_cc_toolchain
+# TODO: b/309533028 - This is deprecated, and will soon be removed.
pw_cc_toolchain_feature = _pw_cc_toolchain_feature
diff --git a/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl b/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl
index 85c6787bd..d36ec8e96 100644
--- a/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl
+++ b/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl
@@ -16,6 +16,7 @@
load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
load(
"@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
+ "FlagSetInfo",
"action_config",
"feature",
"flag_group",
@@ -42,15 +43,16 @@ OBJ_COPY_ACTION_NAME = "objcopy_embed_data"
OBJ_DUMP_ACTION_NAME = "objdump_embed_data"
PW_CC_TOOLCHAIN_CONFIG_ATTRS = {
- "feature_deps": "pw_cc_toolchain_feature labels that provide features for this toolchain",
- "ar": "Path to the tool to use for ar (static link) actions",
+ "feature_deps": "`pw_cc_toolchain_feature` labels that provide features for this toolchain",
+ "ar": "Path to the tool to use for `ar` (static link) actions",
"cpp": "Path to the tool to use for C++ compile actions",
"gcc": "Path to the tool to use for C compile actions",
- "gcov": "Pah to the tool to use for generating code coverag data",
+ "gcov": "Path to the tool to use for generating code coverage data",
"ld": "Path to the tool to use for link actions",
"strip": "Path to the tool to use for strip actions",
"objcopy": "Path to the tool to use for objcopy actions",
"objdump": "Path to the tool to use for objdump actions",
+ "action_config_flag_sets": "List of flag sets to apply to the respective `action_config`s",
# Attributes originally part of create_cc_toolchain_config_info.
"toolchain_identifier": "See documentation for cc_common.create_cc_toolchain_config_info()",
@@ -77,12 +79,13 @@ PW_CC_TOOLCHAIN_BLOCKED_ATTRS = {
"builtin_sysroot": "Use a pw_cc_toolchain_feature to add a builtin_sysroot",
}
-def _action_configs(action_tool, action_list):
+def _action_configs(action_tool, action_list, flag_sets_by_action):
"""Binds a tool to an action.
Args:
action_tool (File): Tool to bind to the specified actions.
action_list (List[str]): List of actions to bind to the specified tool.
+ flag_sets_by_action: Dictionary mapping action names to lists of applicable flag sets.
Returns:
action_config: A action_config binding the provided tool to the
@@ -96,6 +99,7 @@ def _action_configs(action_tool, action_list):
tool = action_tool,
),
],
+ flag_sets = flag_sets_by_action.get(action, default = []),
)
for action in action_list
]
@@ -180,6 +184,40 @@ def _archiver_flags(is_mac):
else:
return ["rcsD"]
+def _strip_actions(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 flag_set(
+ with_features = flag_set_to_copy.with_features,
+ flag_groups = flag_set_to_copy.flag_groups,
+ )
+
+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 FlagSetInfo 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(_strip_actions(fs))
+ return flag_sets_by_action
+
def _pw_cc_toolchain_config_impl(ctx):
"""Rule that provides a CcToolchainConfigInfo.
@@ -190,11 +228,14 @@ def _pw_cc_toolchain_config_impl(ctx):
CcToolchainConfigInfo
"""
check_deps(ctx)
+
+ flag_sets_by_action = _create_action_flag_set_map([dep[FlagSetInfo] for dep in ctx.attr.action_config_flag_sets])
+
all_actions = []
- all_actions += _action_configs(ctx.executable.gcc, ALL_ASM_ACTIONS)
- all_actions += _action_configs(ctx.executable.gcc, ALL_C_COMPILER_ACTIONS)
- all_actions += _action_configs(ctx.executable.cpp, ALL_CPP_COMPILER_ACTIONS)
- all_actions += _action_configs(ctx.executable.cpp, ALL_LINK_ACTIONS)
+ all_actions += _action_configs(ctx.executable.gcc, ALL_ASM_ACTIONS, flag_sets_by_action)
+ all_actions += _action_configs(ctx.executable.gcc, ALL_C_COMPILER_ACTIONS, flag_sets_by_action)
+ all_actions += _action_configs(ctx.executable.cpp, ALL_CPP_COMPILER_ACTIONS, flag_sets_by_action)
+ all_actions += _action_configs(ctx.executable.cpp, ALL_LINK_ACTIONS, flag_sets_by_action)
all_actions += [
action_config(
@@ -205,6 +246,7 @@ def _pw_cc_toolchain_config_impl(ctx):
tool = ctx.executable.ar,
),
],
+ flag_sets = flag_sets_by_action.get(ACTION_NAMES.cpp_link_static_library, default = []),
),
action_config(
action_name = ACTION_NAMES.llvm_cov,
@@ -213,6 +255,7 @@ def _pw_cc_toolchain_config_impl(ctx):
tool = ctx.executable.gcov,
),
],
+ flag_sets = flag_sets_by_action.get(ACTION_NAMES.llvm_cov, default = []),
),
action_config(
action_name = OBJ_COPY_ACTION_NAME,
@@ -221,6 +264,7 @@ def _pw_cc_toolchain_config_impl(ctx):
tool = ctx.executable.objcopy,
),
],
+ flag_sets = flag_sets_by_action.get(OBJ_COPY_ACTION_NAME, default = []),
),
action_config(
action_name = OBJ_DUMP_ACTION_NAME,
@@ -229,6 +273,7 @@ def _pw_cc_toolchain_config_impl(ctx):
tool = ctx.executable.objdump,
),
],
+ flag_sets = flag_sets_by_action.get(OBJ_DUMP_ACTION_NAME, default = []),
),
action_config(
action_name = ACTION_NAMES.strip,
@@ -237,6 +282,7 @@ def _pw_cc_toolchain_config_impl(ctx):
tool = ctx.executable.strip,
),
],
+ flag_sets = flag_sets_by_action.get(ACTION_NAMES.strip, default = []),
),
]
@@ -284,6 +330,7 @@ pw_cc_toolchain_config = rule(
"objcopy": attr.label(allow_single_file = True, executable = True, cfg = "exec"),
"objdump": attr.label(allow_single_file = True, executable = True, cfg = "exec"),
"strip": attr.label(allow_single_file = True, executable = True, cfg = "exec"),
+ "action_config_flag_sets": attr.label_list(),
# Attributes from create_cc_toolchain_config_info.
"toolchain_identifier": attr.string(),
diff --git a/pw_toolchain_bazel/cc_toolchain/private/flag_set.bzl b/pw_toolchain_bazel/cc_toolchain/private/flag_set.bzl
new file mode 100644
index 000000000..23094e864
--- /dev/null
+++ b/pw_toolchain_bazel/cc_toolchain/private/flag_set.bzl
@@ -0,0 +1,225 @@
+# Copyright 2023 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.
+"""Implementation of pw_cc_flag_set and pw_cc_flag_group."""
+
+load(
+ "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
+ "FlagGroupInfo",
+ "FlagSetInfo",
+ "flag_group",
+ "flag_set",
+)
+
+def _pw_cc_flag_group_impl(ctx):
+ """Implementation for pw_cc_flag_group."""
+
+ # If these are empty strings, they are handled differently than if they
+ # are None. Rather than an explicit error or breakage, there's just silent
+ # behavioral differences. Ideally, these attributes default to `None`, but
+ # that is not supported with string types. Since these have no practical
+ # meaning if they are empty strings, just remap empty strings to `None`.
+ #
+ # A minimal reproducer of this behavior with some useful analysis is
+ # provided here:
+ #
+ # https://github.com/armandomontanez/bazel_reproducers/tree/main/flag_group_with_empty_strings
+ iterate_over = ctx.attr.iterate_over if ctx.attr.iterate_over else None
+ expand_if = ctx.attr.expand_if_available if ctx.attr.expand_if_available else None
+ expand_if_not = ctx.attr.expand_if_not_available if ctx.attr.expand_if_not_available else None
+ return flag_group(
+ flags = ctx.attr.flags,
+ iterate_over = iterate_over,
+ expand_if_available = expand_if,
+ expand_if_not_available = expand_if_not,
+ )
+
+pw_cc_flag_group = rule(
+ implementation = _pw_cc_flag_group_impl,
+ attrs = {
+ "flags": attr.string_list(
+ doc = """List of flags provided by this rule.
+
+For extremely complex expressions of flags that require nested flag groups with
+multiple layers of expansion, prefer creating a custom rule in Starlark that
+provides `FlagGroupInfo` or `FlagSetInfo`.
+""",
+ ),
+ "iterate_over": attr.string(
+ doc = """Expands `flags` for items in the named list.
+
+Toolchain actions have various variables accessible as names that can be used
+to guide flag expansions. For variables that are lists, `iterate_over` must be
+used to expand the list into a series of flags.
+
+Note that `iterate_over` is the string name of a build variable, and not an
+actual list. Valid options are listed at:
+
+ https://bazel.build/docs/cc-toolchain-config-reference#cctoolchainconfiginfo-build-variables
+
+Note that the flag expansion stamps out the entire list of flags in `flags`
+once for each item in the list.
+
+Example:
+
+ # Expands each path in `system_include_paths` to a series of `-isystem`
+ # includes.
+ #
+ # Example input:
+ # system_include_paths = ["/usr/local/include", "/usr/include"]
+ #
+ # Expected result:
+ # "-isystem /usr/local/include -isystem /usr/include"
+ pw_cc_flag_group(
+ name = "system_include_paths",
+ flags = ["-isystem", "%{system_include_paths}"],
+ iterate_over = "system_include_paths",
+ )
+""",
+ ),
+ "expand_if_available": attr.string(
+ doc = "Expands the expression in `flags` if the specified build variable is set.",
+ ),
+ "expand_if_not_available": attr.string(
+ doc = "Expands the expression in `flags` if the specified build variable is NOT set.",
+ ),
+ },
+ provides = [FlagGroupInfo],
+ doc = """Declares an (optionally parametric) ordered set of flags.
+
+`pw_cc_flag_group` rules are expected to be consumed exclusively by
+`pw_cc_flag_set` rules. Though simple lists of flags can be expressed by
+populating `flags` on a `pw_cc_flag_set`, `pw_cc_flag_group` provides additional
+power in the following two ways:
+
+ 1. Iteration and conditional expansion. Using `iterate_over`,
+ `expand_if_available`, and `expand_if_not_available`, more complex flag
+ expressions can be made. This is critical for implementing things like
+ the `libraries_to_link` feature, where library names are transformed
+ into flags that end up in the final link invocation.
+
+ Note: `expand_if_equal`, `expand_if_true`, and `expand_if_false` are not
+ yet supported.
+
+ 2. Flags are tool-independent. A `pw_cc_flag_group` expresses ordered flags
+ that may be reused across various `pw_cc_flag_set` rules. This is useful
+ for cases where multiple `pw_cc_flag_set` rules must be created to
+ implement a feature for which flags are slightly different depending on
+ the action (e.g. compile vs link). Common flags can be expressed in a
+ shared `pw_cc_flag_group`, and the differences can be relegated to
+ separate `pw_cc_flag_group` instances.
+
+Examples:
+
+ pw_cc_flag_group(
+ name = "user_compile_flag_expansion",
+ flags = ["%{user_compile_flags}"],
+ iterate_over = "user_compile_flags",
+ expand_if_available = "user_compile_flags",
+ )
+
+ # This flag_group might be referenced from various FDO-related
+ # `pw_cc_flag_set` rules. More importantly, the flag sets pulling this in
+ # may apply to different sets of actions.
+ pw_cc_flag_group(
+ name = "fdo_profile_correction",
+ flags = ["-fprofile-correction"],
+ expand_if_available = "fdo_profile_path",
+ )
+""",
+)
+
+def _pw_cc_flag_set_impl(ctx):
+ """Implementation for pw_cc_flag_set."""
+ if ctx.attr.flags and ctx.attr.flag_groups:
+ fail("{} specifies both `flag_groups` and `flags`, but only one can be specified. Consider splitting into two `pw_cc_flag_set` rules to make the intended order clearer.".format(ctx.label))
+ flag_groups = []
+ if ctx.attr.flags:
+ flag_groups.append(flag_group(flags = ctx.attr.flags))
+ elif ctx.attr.flag_groups:
+ for dep in ctx.attr.flag_groups:
+ if not dep[FlagGroupInfo]:
+ fail("{} in `flag_groups` of {} does not provide FlagGroupInfo".format(dep.label, ctx.label))
+
+ flag_groups = [dep[FlagGroupInfo] for dep in ctx.attr.flag_groups]
+ return flag_set(
+ actions = ctx.attr.actions,
+ flag_groups = flag_groups,
+ )
+
+pw_cc_flag_set = rule(
+ implementation = _pw_cc_flag_set_impl,
+ attrs = {
+ "actions": attr.string_list(
+ mandatory = True,
+ # inclusive-language: disable
+ doc = """A list of action names that this flag set applies to.
+
+Valid choices are listed here:
+
+ https://github.com/bazelbuild/bazel/blob/master/tools/build_defs/cc/action_names.bzl
+
+It is possible for some needed action names to not be enumerated in this list,
+so there is not rigid validation for these strings. Prefer using constants
+rather than manually typing action names.
+""",
+ # inclusive-language: enable
+ ),
+ "flag_groups": attr.label_list(
+ doc = """Labels pointing to `pw_cc_flag_group` rules.
+
+This is intended to be compatible with any other rules that provide
+`FlagGroupInfo`. These are evaluated in order, with earlier flag groups
+appearing earlier in the invocation of the underlying tool.
+
+Note: `flag_groups` and `flags` are mutually exclusive.
+""",
+ ),
+ "flags": attr.string_list(
+ doc = """Flags that should be applied to the specified actions.
+
+These are evaluated in order, with earlier flags appearing earlier in the
+invocation of the underlying tool. If you need expansion logic, prefer
+enumerating flags in a `pw_cc_flag_group` or create a custom rule that provides
+`FlagGroupInfo`.
+
+Note: `flags` and `flag_groups` are mutually exclusive.
+""",
+ ),
+ },
+ provides = [FlagSetInfo],
+ doc = """Declares an ordered set of flags bound to a set of actions.
+
+Flag sets can be attached to a `pw_cc_toolchain` via `action_config_flag_sets`.
+
+Examples:
+
+ pw_cc_flag_set(
+ name = "warnings_as_errors",
+ flags = ["-Werror"],
+ )
+
+ pw_cc_flag_set(
+ name = "layering_check",
+ flag_groups = [
+ ":strict_module_headers",
+ ":dependent_module_map_files",
+ ],
+ )
+
+Note: In the vast majority of cases, alphabetical sorting is not desirable for
+the `flags` and `flag_groups` attributes. Buildifier shouldn't ever try to sort
+these, but in the off chance it starts to these members should be listed as
+exceptions in the `SortableDenylist`.
+""",
+)
diff --git a/pw_toolchain_bazel/cc_toolchain/private/utils.bzl b/pw_toolchain_bazel/cc_toolchain/private/utils.bzl
index 321cafad4..7030f2498 100644
--- a/pw_toolchain_bazel/cc_toolchain/private/utils.bzl
+++ b/pw_toolchain_bazel/cc_toolchain/private/utils.bzl
@@ -15,6 +15,10 @@
load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
load(
+ "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
+ "FlagSetInfo",
+)
+load(
"//cc_toolchain/private:providers.bzl",
"ToolchainFeatureInfo",
)
@@ -52,12 +56,18 @@ ACTION_MAP = {
"linkopts": ALL_LINK_ACTIONS,
}
+def _check_dep_provides(ctx_label, dep, provider, what_provides):
+ if provider not in dep:
+ fail(
+ "{} listed as a dependency of {}, but it's not a {}".format(
+ dep.label,
+ ctx_label,
+ what_provides,
+ ),
+ )
+
def check_deps(ctx):
for dep in ctx.attr.feature_deps:
- if ToolchainFeatureInfo not in dep:
- fail(
- "{} listed as a dependency of {}, but it's not a pw_cc_toolchain_feature".format(
- dep.label,
- ctx.label,
- ),
- )
+ _check_dep_provides(ctx.label, dep, ToolchainFeatureInfo, "pw_cc_toolchain_feature")
+ for dep in ctx.attr.action_config_flag_sets:
+ _check_dep_provides(ctx.label, dep, FlagSetInfo, "pw_cc_flag_set")
diff --git a/pw_toolchain_bazel/docs.rst b/pw_toolchain_bazel/docs.rst
index 9c885c14f..075dc3f7b 100644
--- a/pw_toolchain_bazel/docs.rst
+++ b/pw_toolchain_bazel/docs.rst
@@ -3,16 +3,57 @@
==================
pw_toolchain_bazel
==================
-This module provides building blocks for Bazel's ``cc_toolchain`` API in a way
-that increases modularity and reusability. While this module does NOT provide a
-hermetic toolchain, Pigweed does provide fully instantiated and supported
-toolchains as part of ``pw_toolchain``.
+
+.. pigweed-module::
+ :name: pw_toolchain_bazel
+ :tagline: Modular Bazel C/C++ toolchain API
+ :status: unstable
+ :languages: Starlark
+
+Assembling a complete, hermetic toolchain with Bazel using the native primitives
+can be quite challenging. Additionally, Bazel's native API for declaring C/C++
+toolchains doesn't inherently encourage modularity or reusability.
+
+``pw_toolchain_bazel`` provides a suite of building blocks that make the process
+of assembling a complete, hermetic toolchain significantly easier. The Bazel
+rules introduced by this module push the vast majority of a toolchain's
+declaration into build files, and encourages reusability through sharing of
+flag groups, tools, and toolchain feature implementations.
+
+While this module does **not** provide a hermetic toolchain, Pigweed provides
+`fully instantiated and supported toolchains <https://cs.opensource.google/pigweed/pigweed/+/main:pw_toolchain/host_clang/BUILD.bazel>`_
+that are a useful reference for building your own toolchain.
.. warning::
- This module is under construction and is subject to major breaking changes.
+ `b/309533028 <https://issues.pigweed.dev/309533028>`_\: This module is under
+ construction and is subject to major breaking changes.
+
+.. grid:: 1
+
+ .. grid-item-card:: :octicon:`info` API reference
+ :link: module-pw_toolchain_bazel-api
+ :link-type: ref
+ :class-item: sales-pitch-cta-primary
+
+ Detailed reference information about the pw_toolchain_bazel API.
+
+.. grid:: 1
+
+ .. grid-item-card:: :octicon:`file` Original SEED
+ :link: seed-0113
+ :link-type: ref
+ :class-item: sales-pitch-cta-secondary
+
+ SEED-0113: Add modular Bazel C/C++ toolchain API
------------
Dependencies
------------
This module is not permitted to have dependencies on other modules. When this
module stabilizes, it will be broken out into a separate repository.
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ API reference <api>