SEED-0113: Add modular Bazel C/C++ toolchain API

This SEED proposes new build rules for declaring Bazel toolchains.

Change-Id: Ief884f3cc4dbb9aebb15e5175f65bac7d0d21cee
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173453
Commit-Queue: Armando Montanez <amontanez@google.com>
Reviewed-by: Ted Pudlik <tpudlik@google.com>
diff --git a/seed/0000-index.rst b/seed/0000-index.rst
index 25b2fd3..218a70e 100644
--- a/seed/0000-index.rst
+++ b/seed/0000-index.rst
@@ -22,7 +22,7 @@
   0110-memory-allocation-interfaces
   0111-build-systems
   0112-async-poll
-  0113: Modular Bazel C/C++ toolchain API <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173453>
+  0113-bazel-cc-toolchain-api
   0114: Channels <http://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175471>
   0115: pw_sensor Sensors <http://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176760>
   0116: pw_net Sockets <http://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177696>
diff --git a/seed/0113-bazel-cc-toolchain-api.rst b/seed/0113-bazel-cc-toolchain-api.rst
new file mode 100644
index 0000000..758e323
--- /dev/null
+++ b/seed/0113-bazel-cc-toolchain-api.rst
@@ -0,0 +1,724 @@
+.. _seed-0113:
+
+===========================================
+0113: Add modular Bazel C/C++ toolchain API
+===========================================
+.. seed::
+   :number: 0113
+   :name: Add modular Bazel C/C++ toolchain API
+   :status: Accepted
+   :proposal_date: 2023-09-28
+   :cl: 173453
+
+-------
+Summary
+-------
+This SEED proposes custom Starlark rules for declaring C/C++ toolchains in
+Bazel, and scalable patterns for sharing modular components of C/C++ toolchain
+definitions.
+
+----------
+Motivation
+----------
+There is a lot of boilerplate involved in standing up a Bazel C/C++ toolchain.
+While a good portion of the verbosity of specifying a new toolchain is
+important, necessary machinery, nearly as much suffers from one or more of the
+following problems:
+
+- **Underdocumented patterns**: The
+  `create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_
+  method has an attribute called ``target_cpu`` that doesn't have an associated
+  list of expected values, which suggests the argument is for bookkeeping
+  purposes and doesn't affect Bazel behavior. The reality is this argument
+  *does* have expected values that change behavior, but these are undocumented
+  (see `this GitHub issue <https://github.com/bazelbuild/bazel/issues/19353>`_
+  for more information).
+
+- **Not inherently modular**: Bazel expects the overwhelming majority of a
+  C/C++ toolchain to be specified as part of a call to
+  ``create_cc_toolchain_config_info()``. Because this is a Starlark method,
+  there's a lot of flexibility with how you construct a toolchain config, but
+  very little by way of existing patterns for creating something that is
+  testable, sharable, or in other ways modular. The existing
+  `tutorial for creating a C/C++ toolchain <https://bazel.build/tutorials/ccp-toolchain-config#configuring_the_c_toolchain>`_
+  illustrates expanding out the toolchain definition as a no-argument Starlark
+  rule.
+
+As Pigweed fully embraces multi-platform builds, it is critical for both
+Pigweed and users of Pigweed that it is easy to stand up custom toolchain
+definitions that work for the relevant hardware project-specific code/libraries.
+
+This SEED seeks to address the shortcomings in Bazel C/C++ toolchain
+declaration by establishing patterns and providing custom build rules that
+are owned and maintained by Pigweed.
+
+--------
+Proposal
+--------
+1. Introduce rules for defining C/C++ toolchains
+================================================
+In an effort to improve the experience of defining a C/C++ toolchain, Pigweed
+will introduce new Bazel rules that allow toolchains to share common boilerplate
+without hindering power-user functionality.
+
+While this work centers around an improved experience for defining a toolchain
+via ``create_cc_toolchain_config_info()``, other build rules will be introduced
+for closely related aspects of the toolchain definition process.
+
+An example of what these rules would look like in practice is as follows:
+
+.. code-block::
+
+   # A tool that can be used by various build actions.
+   pw_cc_tool(
+       name = "clang_tool",
+       path = "@cipd_llvm_toolchain//:bin/clang",
+       additional_files = [
+          "@cipd_llvm_toolchain//:all",
+       ],
+   )
+
+   # A mapping of a tool to actions, with flag sets that define behaviors.
+   pw_cc_action_config(
+       name = "clang",
+       actions = ALL_ASM_ACTIONS + ALL_C_COMPILER_ACTIONS,
+       tools = [
+           ":clang_tool",
+       ],
+       flag_sets = [
+           # Most flags should NOT end up here. Only unconditional flags that
+           # should ALWAYS be bound to this tool (e.g. static library
+           # workaround fix for macOS).
+           "@pw_toolchain//flag_sets:generate_depfile",
+       ],
+   )
+
+   # A trivial flag to be consumed by a C/C++ toolchain.
+   pw_cc_flag_set(
+       name = "werror",
+       actions = ALL_COMPILE_ACTIONS,
+       flags = [
+           "-Werror",
+       ],
+   )
+
+   # A list of flags that can be added to a toolchain configuration.
+   pw_cc_flag_set(
+       name = "user_compile_options",
+       actions = ALL_COMPILE_ACTIONS,
+       flag_groups = [
+           ":user_compile_options_flags",
+       ]
+   )
+
+   # A more complex compiler flag that requires template expansion.
+   pw_cc_flag_group(
+       name = "user_compile_options_flags",
+       flags = ["%{user_compile_flags}"],
+       iterate_over = "user_compile_flags",
+       expand_if_available = "user_compile_flags",
+   )
+
+   # The underlying definition of a complete C/C++ toolchain.
+   pw_cc_toolchain(
+       name = "host_toolchain_linux",
+       action_configs = [
+           ":clang",
+           ":clang++",
+           # ...
+       ],
+       additional_files = ":linux_sysroot_files",
+       action_config_flag_sets = [
+           "@pw_toolchain//flag_sets:no_canonical_prefixes",
+           ":user_compile_options",
+           ":werror",
+       ],
+       features = [
+           "@pw_toolchain//features:c++17",
+       ],
+       target_cpu = "x86_64",
+       target_system_name = "x86_64-unknown-linux-gnu",
+       toolchain_identifier = "host-toolchain-linux",
+   )
+
+   # Toolchain resolution parameters for the above C/C++ toolchain.
+   toolchain(
+       name = "host_cc_toolchain_linux",
+       exec_compatible_with = [
+           "@platforms//os:linux",
+       ],
+       target_compatible_with = [
+           "@platforms//os:linux",
+       ],
+       toolchain = ":host_toolchain_linux",
+       toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+   )
+
+2. Provide standard toolchain building-blocks
+=============================================
+Pigweed will build out a repository of sharable instantiations of the
+aforementioned custom rules to give projects the resources they need to quickly
+and easily assemble toolchains for desktop and embedded targets. This includes,
+but is not limited to:
+
+- Rules that define tool sets for common toolchains (LLVM/clang, GNU/gcc).
+- Fully specified, modular
+  `features <https://bazel.build/docs/cc-toolchain-config-reference#features>`_.
+- Common flag sets that users may want to apply directly to their toolchains.
+  (enabling/disabling warnings, C++ standard version, etc.)
+- Platform/architecture support rules, including host OS SDK integrations
+  (Xcode, Windows SDK) and architecture-specific flag sets.
+
+These components will help establish patterns that will make it significantly
+easier for Pigweed users (and Bazel users at large) to define their own
+toolchains.
+
+---------------------
+Problem investigation
+---------------------
+This section explores previous work, and details why existing solutions don't
+meet Pigweed's needs.
+
+bazelembedded/rules_cc_toolchain
+================================
+The `rules_cc_toolchain <https://github.com/bazelembedded/rules_cc_toolchain>`_
+as part of the larger bazelembedded suite was actually the initial foundation
+of Pigweed's Bazel build. While this served as a very good initial foundation,
+it didn't provide the flexibility needed to easily stand up additional
+toolchains in ways that gave downstream projects sufficient control over the
+flags, libraries, tools, and sysroot.
+
+To work around the limited configurability of toolchain flags, Pigweed employed
+the following workarounds:
+
+#. Place ``copts`` and ``linkopts`` in ``.bazelrc``: This was problematic
+   because ``.bazelrc`` is not intrinsically shared with or propagated to
+   downstream users of Pigweed. Also, flags here are unilaterally applied
+   without OS-specific considerations.
+#. Attach flags to build targets with custom wrappers: This approach
+   intrinsically requires the existence of the ``pw_cc_library``, which
+   introduces difficulty around consistent interoperability with other Bazel
+   projects (among other issues detailed in
+   `b/267498492 <https://issues.pigweed.dev/issues/267498492>`_).
+
+Some other issues encountered when working with this solution include:
+
+- These rules intended to be modular, but in practice were relatively tightly
+  coupled.
+- Transitive dependencies throughout the toolchain definition process resulted
+  in some hard-to-debug issues (see
+  `this pull request <https://github.com/bazelembedded/rules_cc_toolchain/pull/39>`_
+  and `b/254518544 <https://issues.pigweed.dev/issues/254518544>`_.
+
+bazelembedded/modular_cc_toolchains
+===================================
+The `modular_cc_toolchains <https://github.com/bazelembedded/modular_cc_toolchains>`_
+repository is a new attempt as part of the bazelembedded suite at providing
+truly modular toolchain rules. The proposed direction is much more in-line
+with the needs of Pigweed, but at the moment the repository exists as an
+initial draft of ideas rather than a complete implementation.
+
+This repository greatly inspired Pigweed's initial prototype for modular
+toolchains, but diverges significantly from the underlying Bazel C/C++
+toolchain building-blocks. If this work was already complete and
+well-established, it probably would have satisfied some of Pigweed's key needs.
+
+lowRISC/crt
+===========
+The `compiler repository toolkit <https://github.com/lowRISC/crt>`_ is another
+scalable approach at toolchains. This repository strives to be an all-in-one
+repository for embedded toolchains, and does a very good job at providing
+scalable models for establishing toolchains. This repository is relatively
+monolithic, though, and doesn't necessarily address the concern of quickly
+and easily standing up custom toolchains. Instead, it's more suited towards
+contributing new one-size-fits-all toolchains to ``crt`` directly.
+
+Android's toolchain
+===================
+Android's Bazel-based build has invested heavily in toolchains, but they're
+very tightly coupled to the use cases of Android. For example,
+`this <https://cs.android.com/android/platform/superproject/main/+/main:build/bazel/toolchains/clang/host/linux-x86/cc_toolchain_features.bzl;l=375-389;drc=097d710c349758fc2732497fe5c3d1b0a32fa4a8>`_ binds ``-fstrict-aliasing`` to a condition based on the target architecture.
+These toolchains scale for the purpose of Android, but unfortunately are
+inherently not modular or reusable outside of that context.
+
+Due to the sheer amount of investment in these toolchains, though, they serve
+as a good reference for building out a complete toolchain in Bazel.
+
+Pigweed's modular Bazel toolchain prototype
+===========================================
+As part of an exploratory phase of getting toolchains set up for Linux and
+macOS,
+`an initial prototype <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/157634>`_
+for modular Bazel toolchains was drafted and deployed to Pigweed. This work
+introduced two key build rules: ``pw_cc_toolchain_feature`` and
+``pw_cc_toolchain``. With both of these rules, it’s possible to instantiate a
+vast array of toolchain variants without writing a single line of Starlark. A
+few examples of these building blocks in action are provided below.
+
+.. code-block::
+
+   # pw_cc_toolchain example taken from https://cs.opensource.google/pigweed/pigweed/+/main:pw_toolchain/host_clang/BUILD.bazel;l=113-143;drc=7df1768d915fe11dae05751f70f143e60acfb17a.
+
+   pw_cc_toolchain(
+       name = "host_toolchain_linux",
+       abi_libc_version = "unknown",
+       abi_version = "unknown",
+       all_files = ":all_linux_files",
+       ar = "@llvm_toolchain//:bin/llvm-ar",
+
+       # TODO: b/305737273 - Globbing all files for every action has a
+       # performance hit, make these more granular.
+       ar_files = ":all_linux_files",
+       as_files = ":all_linux_files",
+       compiler = "unknown",
+       compiler_files = ":all_linux_files",
+       coverage_files = ":all_linux_files",
+       cpp = "@llvm_toolchain//:bin/clang++",
+       dwp_files = ":all_linux_files",
+       feature_deps = [
+           ":linux_sysroot",
+            "@pw_toolchain//features:no_canonical_prefixes",
+       ],
+       gcc = "@llvm_toolchain//:bin/clang",
+       gcov = "@llvm_toolchain//:bin/llvm-cov",
+       host_system_name = "unknown",
+       ld = "@llvm_toolchain//:bin/clang++",
+       linker_files = ":all_linux_files",
+       objcopy_files = ":all_linux_files",
+       strip = "@llvm_toolchain//:bin/llvm-strip",
+       strip_files = ":all_linux_files",
+       supports_param_files = 0,
+       target_cpu = "unknown",
+       target_libc = "unknown",
+       target_system_name = "unknown",
+       toolchain_identifier = "host-toolchain-linux",
+   )
+
+   # pw_cc_toolchain_feature examples taken from https://cs.opensource.google/pigweed/pigweed/+/main:pw_toolchain_bazel/features/BUILD.bazel;l=21-34;drc=f96fd31675d136bd37a7f3840102cb256d555cea.
+
+   # Disables linking of the default C++ standard library to allow linking of a
+   # different version.
+   pw_cc_toolchain_feature(
+       name = "no_default_cpp_stdlib",
+       linkopts = ["-nostdlib++"],
+   )
+
+   # Prevent relative paths from being converted to absolute paths.
+   # Note: This initial prototype made this a feature, but it should instead
+   # exist as a flag_set.
+   pw_cc_toolchain_feature(
+       name = "no_canonical_prefixes",
+       copts = [
+           "-no-canonical-prefixes",
+       ],
+   )
+
+What’s worth noting is that the ``pw_cc_toolchain_feature`` build rule looks
+very similar to a GN ``config``. This was no mistake, and was an attempt to
+substantially reduce the boiler plate for creating new sharable compiler flag
+groups.
+
+Unfortunately, it quickly became apparent that this approach limited control
+over the underlying toolchain definition creation process. In order to support
+``always_link`` on macOS, a custom logic and flags had to be directly baked into
+the rule used to declare toolchains
+(`relevant change <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/168614/17/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl>`_).
+While workarounds like this should be possible, the fact that this had to be
+upstreamed internally to ``pw_cc_toolchain`` exposed limitations in the
+abstraction patterns that were established. Such limitations could preclude
+some project from using ``pw_cc_toolchain`` at all.
+
+---------------
+Detailed design
+---------------
+The core design proposal is to transform the providers used by
+``cc_common.create_cc_toolchain_config_info()`` into build rules. The approach
+has been prototyped
+`here <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/168351/1>`_,
+and retains API compatibility with the initial prototype as a proof-of-concept.
+
+One core pattern established by this design is transforming content that would
+typically live as Starlark to instead live in build files. This is done to
+make it easier to read and reference existing work.
+
+Implementation requirements
+===========================
+Compatibility with native C/C++ rules
+-------------------------------------
+The core of Pigweed's toolchain build rules will rely on the providers
+defined as part of Bazel's
+`rules_cc <https://github.com/bazelbuild/rules_cc/blob/main/cc/cc_toolchain_config_lib.bzl>`_. This means that the new rules can interop with
+existing work that directly uses these toolchain primitives. It also provides
+a clear path for migrating existing toolchains piece-by-piece (which may be
+written completely in Starlark).
+
+Any extensions beyond the existing providers (e.g. specifying
+``additional_files`` on a ``pw_cc_tool``) must happen parallel to existing
+providers so that rules that consume the ``cc_toolchain_config_lib`` providers
+can work with vanilla providers.
+
+Compatibility with Bazel rules ecosystem
+----------------------------------------
+In following with the larger Bazel rules ecosystem, the toolchain building
+blocks will be designed such that they can be used independently from Pigweed.
+This allows this work to be used for non-embedded projects, and reduces the
+overhead for standing up a custom Bazel C/C++ toolchain in any arbitrary
+project.
+
+Initially, the work will live as ``pw_toolchain_bazel`` in the main Pigweed
+repository to facilitate testing. This module must not depend on any other
+aspects of Pigweed. As the toolchain rules mature, they will eventually be
+available as a separate repository to match the modularity patterns used by
+the larger Bazel rules ecosystem.
+
+Introduce ``pw_cc_flag_set`` and ``pw_cc_flag_group``
+=====================================================
+The majority of build flags would be expressed as ``pw_cc_flag_set`` and
+``pw_cc_flag_group`` pairs.
+
+.. code-block::
+
+   # A simple flag_set with a single flag.
+   pw_cc_flag_set(
+       name = "werror",
+       # Only applies to C/C++ compile actions (i.e. no assemble/link/ar).
+       actions = ALL_CPP_COMPILER_ACTIONS + ALL_C_COMPILER_ACTIONS,
+       flags = [
+           "-Werror",
+       ],
+   )
+
+   # A flag_group that potentially expands to multiple flags.
+   pw_cc_flag_group(
+       name = "user_compile_options_flags",
+       flags = ["%{user_compile_flags}"],
+       iterate_over = "user_compile_flags",
+       expand_if_available = "user_compile_flags",
+   )
+
+   # A flag_set that relies on a non-trivial or non-constant expression of
+   # flags.
+   pw_cc_flag_set(
+       name = "user_compile_options",
+       actions = ALL_COMPILE_ACTIONS,
+       flag_groups = [
+           ":user_compile_options_flags",
+       ]
+   )
+
+These closely mimic the API of ``cc_toolchain_config_lib.flag_set()`` and
+``cc_toolchain_config_lib.flag_group()``, with the following exceptions:
+
+**pw_cc_flag_set**
+
+*Added*
+
+- ``flags`` (added): Express a constant, trivial list of flags. If this is
+  specified, ``flag_groups`` may not be specified. This eliminates the need
+  for specifying a corresponding ``pw_cc_flag_group`` for every
+  ``pw_cc_flag_set`` for most flags.
+
+**pw_cc_flag_group**
+
+*Removed*
+
+- ``expand_if_true``\, ``expand_if_false``\, ``expand_if_equal``\: More complex
+  rules that rely on these should live as custom Starlark rules that provide a
+  ``FlagGroupInfo``\, or ``FlagSetInfo`` (depending on which is more ergonomic
+  to express the intent). See :ref:`pw_cc_flag_set-exceptions` below for an
+  example that illustrates how express more complex ``flag_group``\s that rely
+  on these attributes.
+
+Application of flags
+--------------------
+Flags can be applied to a toolchain in three ways. This section attempts to
+provide initial guidance for where flags should be applied, though it's likely
+better practices will evolve as this work sees more use. For the latest
+guidance, please consult the official documentation when it rolls out to
+``pw_toolchain_bazel``.
+
+Flags unconditionally applied to a toolchain
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The majority of flags fall into this category. Architecture flags,
+globally-applied warnings, global defines, and other similar flags should be
+applied in the ``action_config_flag_sets`` attribute of a ``pw_cc_toolchain``
+(see :ref:`pw_cc_toolchain-toolchain-declarations` for more information). Each
+``pw_cc_flag_set`` (or other rule that provides a ``FlagSetInfo`` provider)
+listed in ``action_config_flag_sets`` is unconditionally applied to every tool
+that matches the ``actions`` listed in the flag set.
+
+.. _application-of-flags-feature-flags:
+
+Feature flags
+~~~~~~~~~~~~~
+Flag sets applied as features may or may not be enabled even if they are listed
+in the ``features`` attribute of a ``pw_cc_toolchain``. The
+`official Bazel documentation on features <https://bazel.build/docs/cc-toolchain-config-reference#features>`_
+provides some good guidance on when features should be employed. To summarize,
+features should be used when either they should be controllable by users
+invoking the build, or if they affect build behavior beyond simply
+adding/removing flags (e.g. by introducing additional build actions).
+
+Flags unconditionally applied to a tool
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+These flags are flags that are bound to a particular tool. These are not
+expressed as part of a ``pw_cc_toolchain``, and are instead bound to a
+``pw_cc_action_config``. This means that the flag set is unconditionally
+applied to every user of that action config. These kinds of flag applications
+should be reserved for flags required to assemble a working set of tools (such
+as generating a depfile, or adding support for static library link handling
+:ref:`as illustrated below <pw_cc_flag_set-exceptions>`).
+
+Flag application order
+~~~~~~~~~~~~~~~~~~~~~~
+When invoking the underlying tools, the intended order of flags is as follows:
+
+#. Flags listed in the ``flag_sets`` list of a ``pw_cc_action_config``.
+#. Flags listed in ``action_config_flag_sets`` of a ``pw_cc_toolchain``.
+#. Flags listed in ``features`` of a ``pw_cc_toolchain``.
+
+These lists are intended to be sensitive to ordering, earlier items in the lists
+should appear in the final tool invocation flags before later items in the list.
+
+As transitive dependencies between features/flags are not supported as part of
+this proposal, exact traversal of transitive flag dependencies will be left
+to be decided if/when that feature is introduced. This proposal suggests
+postorder handling of flags as the most intuitive order.
+
+.. _pw_cc_flag_set-exceptions:
+
+Exceptions
+----------
+Some flags are too complex to be nicely expressed in a Bazel build file. These
+flag sets or flag groups will need to be expressed in Starlark as custom rules.
+Fortunately, this will interop well with simpler flag sets since the underlying
+providers are all the same.
+
+**Example**
+
+In a Starlark file (e.g. ``//tools/llvm/llvm_ar_patch.bzl``), the required
+``flag_set`` can be defined:
+
+.. code-block::
+
+   # Starlark rules in a .bzl file for a relatively complicated workaround for
+   # what would normally be inherently managed by Bazel internally.
+   # TODO: b/297413805 - Remove this implementation.
+
+   def _pw_cc_static_libs_to_link_impl():
+      """Returns a flag_set provider that sets up static libraries to link."""
+      return flag_set(
+               actions = [
+                   ACTION_NAMES.cpp_link_static_library,
+               ],
+               flag_groups = [
+                   flag_group(
+                       expand_if_available = "libraries_to_link",
+                       iterate_over = "libraries_to_link",
+                       flag_groups = [
+                           flag_group(
+                               expand_if_equal = variable_with_value(
+                                   name = "libraries_to_link.type",
+                                   value = "object_file",
+                               ),
+                               flags = ["%{libraries_to_link.name}"],
+                           ),
+                           flag_group(
+                               expand_if_equal = variable_with_value(
+                                   name = "libraries_to_link.type",
+                                   value = "object_file_group",
+                               ),
+                               flags = ["%{libraries_to_link.object_files}"],
+                               iterate_over = "libraries_to_link.object_files",
+                           ),
+                       ],
+                   ),
+               ],
+           )
+
+   pw_cc_static_libs_to_link = rule(
+       implementation = _pw_cc_static_libs_to_link_impl,
+       provides = [FlagSetInfo],
+   )
+
+And then in the ``BUILD.bazel`` file, the rules would be used as if they
+were a ``pw_cc_flag_set``:
+
+.. code-block::
+
+   load(
+       "@pw_toolchain//tools/llvm:llvm_ar_patch.bzl",
+       "pw_cc_static_libs_to_link"
+   )
+
+   pw_cc_static_libs_to_link(
+       name = "static_library_action_flags",
+   )
+
+   pw_cc_action_config(
+       name = "llvm_ar",
+       actions = ACTION_NAMES.cpp_link_static_library,
+       tools = [
+           ":llvm_ar_tool",
+       ],
+       flag_sets = [
+           ":static_library_action_flags",
+       ],
+   )
+
+Introduce ``pw_cc_feature`` and ``pw_cc_feature_set``
+=====================================================
+These types are just permutations of the ``cc_toolchain_config_lib.feature()``
+and ``cc_toolchain_config_lib.with_feature_set()`` API. For guidance on when
+these should be used, see
+:ref:`application of feature flags <application-of-flags-feature-flags>`.
+
+.. code-block::
+
+   pw_cc_feature_set(
+       name = "static_pie_requirements",
+       with_features = ["pie"],
+       # If this doesn't work when certain features are enabled, they should
+       # be specified as ``without_features``.
+   )
+
+   pw_cc_feature(
+       name = "static_pie",
+       flag_sets = [
+           "//flag_sets:static_pie",
+       ],
+       implies = ["static_link_flag"],
+       requires = [
+           ":static_pie_requirements",
+       ],
+   )
+
+Introduce ``pw_cc_action_config`` and ``pw_cc_tool``
+====================================================
+These are closely related to the ``ActionConfigInfo`` and ``ToolInfo``
+providers, but allow additional files to be attached and a list of actions to
+be attached rather than a single action.
+
+.. code-block::
+
+   pw_cc_tool(
+       name = "clang_tool",
+       path = "@llvm_toolchain//:bin/clang",
+       additional_files = [
+           "@llvm_toolchain//:all",
+       ],
+   )
+
+   pw_cc_action_config(
+       name = "clang",
+       actions = ALL_ASM_ACTIONS + ALL_C_COMPILER_ACTIONS,
+       tools = [
+           ":clang_tool",
+       ],
+       flag_sets = [
+           # Most flags should NOT end up here. Only unconditional flags that
+           # should ALWAYS be bound to this tool (e.g. static library
+           # workaround fix for macOS).
+           "//flag_sets:generate_depfile",
+       ],
+   )
+
+.. _pw_cc_toolchain-toolchain-declarations:
+
+Toolchain declarations
+======================
+In following with the other proposed rules, ``pw_cc_toolchain`` largely
+follows the API of ``cc_common.create_cc_toolchain_config_info()``. Most of the
+attributes are logically passed through, with the following exceptions:
+
+- **action_config_flag_sets**: Flag sets to apply to action configs. Since flag
+  sets are intrinsically bound to actions, there’s no need to divide them at
+  this level.
+- **additional_files**: Now that tools can spec out required files, those
+  should be propagated and mostly managed internally. The ``\*_files`` members
+  will still be available, but shouldn’t see much use. additional_files is like
+  “all_files”, but applies to all action_configs.
+
+.. code-block::
+
+   pw_cc_toolchain(
+       name = "host_toolchain_linux",
+       abi_libc_version = "unknown",  # We should consider how to move this out in the future.
+       abi_version = "unknown",
+       action_configs = [
+           "@llvm_toolchain//tools:clang",
+           "@llvm_toolchain//tools:clang++",
+           "@llvm_toolchain//tools:lld",
+           "@llvm_toolchain//tools:llvm_ar",
+           "@llvm_toolchain//tools:llvm_cov",
+           "@llvm_toolchain//tools:llvm_strip",
+       ],
+       additional_files = ":linux_sysroot_files",
+       action_config_flag_sets = [
+           ":linux_sysroot",
+           "@pw_toolchain//flag_collections:strict_warnings",
+           "@pw_toolchain//flag_sets:no_canonical_prefixes",
+       ],
+       features = [
+           "@pw_toolchain//features:c++17",
+       ],
+       host_system_name = "unknown",
+       supports_param_files = 0,  # Seems like this should be attached to a pw_cc_action_config...
+       target_cpu = "unknown",
+       target_libc = "unknown",
+       target_system_name = "unknown",
+       toolchain_identifier = "host-toolchain-linux",
+       cxx_builtin_include_directories = [
+           "%package(@llvm_toolchain//)%/include/x86_64-unknown-linux-gnu/c++/v1",
+           "%package(@llvm_toolchain//)%/include/c++/v1",
+           "%package(@llvm_toolchain//)%/lib/clang/17/include",
+           "%sysroot%/usr/local/include",
+           "%sysroot%/usr/include/x86_64-linux-gnu",
+           "%sysroot%/usr/include",
+       ],
+   )
+
+------------
+Alternatives
+------------
+Improve Bazel's native C/C++ toolchain rules
+============================================
+Improving Bazel's native rules for defining C/C++ toolchains is out of the
+scope of Pigweed's work. Changing the underlying toolchain API as Bazel
+understands it is a massive undertaking from the perspective of migrating
+existing code. We hope that the custom rule effort can help guide future
+decisions when it comes to toolchain scalability and maintainability.
+
+----------
+Next steps
+----------
+Rust toolchain interop
+======================
+Pigweed's Rust toolchains have some interoperability concerns and requirements.
+The extend of this needs to be thoroughly investigated as a next step to ensure
+that the Rust/C/C++ toolchain experience is relatively unified and ergonomic.
+
+More maintainable ``cxx_builtin_include_directories``
+=====================================================
+In the future, it would be nice to have a more sharable solution for managing
+``cxx_builtin_include_directories`` on a ``pw_cc_toolchain``. This could
+plausibly be done by allowing ``pw_cc_flag_set`` to express
+``cxx_builtin_include_directories`` so they can be propagated back up to the
+``pw_cc_toolchain``.
+
+Feature name collision guidance
+===============================
+Features support relatively complex relationships among each other, but
+traditionally rely on string names to express these relationships rather than
+labels. This introduces significant ambiguity, as it's possible for multiple
+features to use the same logical name so long as they aren't both employed in
+the same toolchain. In practice, the only way to tell what features will end up
+enabled is to manually unpack what features a toolchain pulls in, and
+cross-reference it against the output of
+`--experimental_save_feature_state <https://bazel.build/reference/command-line-reference#flag--experimental_save_feature_state>`_.
+
+One potential solution to this problem is to add a mechanism for expressing
+features as labels, which will allow relationships to be expressed more
+concretely, and help prevent unintended naming collisions. This would not
+replace the ability to express relationships with features not accessible via
+labels, but rather live alongside it.
diff --git a/seed/BUILD.gn b/seed/BUILD.gn
index 6482a0b..62aa258 100644
--- a/seed/BUILD.gn
+++ b/seed/BUILD.gn
@@ -31,6 +31,7 @@
     ":0110",
     ":0111",
     ":0112",
+    ":0113",
   ]
 }
 
@@ -82,3 +83,7 @@
 pw_doc_group("0112") {
   sources = [ "0112-async-poll.rst" ]
 }
+
+pw_doc_group("0113") {
+  sources = [ "0113-bazel-cc-toolchain-api.rst" ]
+}