aboutsummaryrefslogtreecommitdiff
path: root/pw_toolchain_bazel/cc_toolchain/private
diff options
context:
space:
mode:
Diffstat (limited to 'pw_toolchain_bazel/cc_toolchain/private')
-rw-r--r--pw_toolchain_bazel/cc_toolchain/private/action_config.bzl300
-rw-r--r--pw_toolchain_bazel/cc_toolchain/private/action_files.bzl74
-rw-r--r--pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl426
-rw-r--r--pw_toolchain_bazel/cc_toolchain/private/feature.bzl293
-rw-r--r--pw_toolchain_bazel/cc_toolchain/private/feature_constraint.bzl43
-rw-r--r--pw_toolchain_bazel/cc_toolchain/private/flag_set.bzl81
-rw-r--r--pw_toolchain_bazel/cc_toolchain/private/providers.bzl125
-rw-r--r--pw_toolchain_bazel/cc_toolchain/private/toolchain_feature.bzl215
-rw-r--r--pw_toolchain_bazel/cc_toolchain/private/unsafe_feature.bzl59
-rw-r--r--pw_toolchain_bazel/cc_toolchain/private/utils.bzl314
10 files changed, 1350 insertions, 580 deletions
diff --git a/pw_toolchain_bazel/cc_toolchain/private/action_config.bzl b/pw_toolchain_bazel/cc_toolchain/private/action_config.bzl
new file mode 100644
index 000000000..d55eda135
--- /dev/null
+++ b/pw_toolchain_bazel/cc_toolchain/private/action_config.bzl
@@ -0,0 +1,300 @@
+# 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_action_config and pw_cc_tool."""
+
+load(
+ ":providers.bzl",
+ "PwActionConfigInfo",
+ "PwActionConfigSetInfo",
+ "PwActionNameSetInfo",
+ "PwFeatureConstraintInfo",
+ "PwFeatureSetInfo",
+ "PwFlagSetInfo",
+ "PwToolInfo",
+)
+
+def _bin_to_files(target, extra_files = []):
+ if not target:
+ return depset(extra_files)
+ info = target[DefaultInfo]
+ exe = info.files_to_run.executable
+ if not exe:
+ return depset(extra_files)
+ return depset(
+ [exe] + extra_files,
+ transitive = [info.files, info.data_runfiles.files],
+ )
+
+def _pw_cc_tool_impl(ctx):
+ """Implementation for pw_cc_tool."""
+
+ # Remaps empty strings to `None` to match behavior of the default values.
+ exe = ctx.executable.tool or None
+ path = ctx.attr.path or None
+
+ if (exe == None) == (path == None):
+ fail("Exactly one of tool and path must be provided. Prefer tool")
+
+ tool = PwToolInfo(
+ label = ctx.label,
+ exe = exe,
+ path = path,
+ files = _bin_to_files(ctx.attr.tool, ctx.files.additional_files),
+ requires_any_of = tuple([fc[PwFeatureConstraintInfo] for fc in ctx.attr.requires_any_of]),
+ execution_requirements = tuple(ctx.attr.execution_requirements),
+ )
+
+ return [
+ tool,
+ DefaultInfo(files = tool.files),
+ ]
+
+pw_cc_tool = rule(
+ implementation = _pw_cc_tool_impl,
+ attrs = {
+ "tool": attr.label(
+ allow_files = True,
+ executable = True,
+ cfg = "exec",
+ doc = """The underlying tool that this rule represents.
+
+This attribute is a label rather than a simple file path. This means that the
+file must be referenced relative to the BUILD file that exports it. For example:
+
+ @llvm_toolchain//:bin/clang
+ ^ ^ ^
+ Where:
+
+ * `@llvm_toolchain` is the repository.
+ * `//` is the directory of the BUILD file that exports the file of interest.
+ * `bin/clang` is the path of the actual binary relative to the BUILD file of
+ interest.
+""",
+ ),
+ "path": attr.string(
+ doc = """An absolute path to a binary to use for this tool.
+
+Relative paths are also supported, but they are relative to the
+`pw_cc_toolchain` that uses this tool rather than relative to this `pw_cc_tool`
+rule.
+
+WARNING: This method of listing a tool is NOT recommended, and is provided as an
+escape hatch for edge cases. Prefer using `tool` whenever possible.
+""",
+ ),
+ "execution_requirements": attr.string_list(
+ doc = "A list of strings that provide hints for execution environment compatibility (e.g. `requires-darwin`).",
+ ),
+ "requires_any_of": attr.label_list(
+ providers = [PwFeatureConstraintInfo],
+ doc = """This will be enabled when any of the constraints are met.
+
+If omitted, this tool will be enabled unconditionally.
+""",
+ ),
+ "additional_files": attr.label_list(
+ allow_files = True,
+ doc = """Additional files that are required for this tool to correctly operate.
+
+These files are propagated up to the `pw_cc_toolchain` so you typically won't
+need to explicitly specify the `*_files` attributes on a `pw_cc_toolchain`.
+""",
+ ),
+ },
+ provides = [PwToolInfo, DefaultInfo],
+ doc = """Declares a singular tool that can be bound to action configs.
+
+`pw_cc_tool` rules are intended to be consumed exclusively by
+`pw_cc_action_config` rules. These rules declare an underlying tool that can
+be used to fulfill various actions. Many actions may reuse a shared tool.
+
+Example:
+
+ # A project-provided tool.
+ pw_cc_tool(
+ name = "clang_tool",
+ tool = "@llvm_toolchain//:bin/clang",
+ )
+
+ # A tool expected to be preinstalled on a user's machine.
+ pw_cc_tool(
+ name = "clang_tool",
+ path = "/usr/bin/clang",
+ )
+""",
+)
+
+def _generate_action_config(ctx, action_name, **kwargs):
+ flag_sets = []
+ for fs in ctx.attr.flag_sets:
+ provided_fs = fs[PwFlagSetInfo]
+ if action_name in provided_fs.actions:
+ flag_sets.append(provided_fs)
+
+ return PwActionConfigInfo(
+ action_name = action_name,
+ flag_sets = tuple(flag_sets),
+ **kwargs
+ )
+
+def _pw_cc_action_config_impl(ctx):
+ """Implementation for pw_cc_tool."""
+ if not ctx.attr.tools:
+ fail("Action configs are not valid unless they specify at least one `pw_cc_tool` in `tools`")
+
+ action_names = depset(transitive = [
+ action[PwActionNameSetInfo].actions
+ for action in ctx.attr.action_names
+ ]).to_list()
+ if not action_names:
+ fail("Action configs are not valid unless they specify at least one action name in `action_names`")
+
+ # 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 = fs[PwFlagSetInfo]
+ flag_set_applies = False
+ for action in action_names:
+ if action in provided_fs.actions:
+ flag_set_applies = True
+ if not flag_set_applies:
+ fail("{} listed as a flag set to apply to {}, but none of the actions match".format(
+ fs.label,
+ ctx.label,
+ ))
+
+ tools = []
+ for tool in ctx.attr.tools:
+ exe = tool[DefaultInfo].files_to_run.executable
+ if PwToolInfo in tool:
+ tools.append(tool[PwToolInfo])
+ elif exe != None:
+ tools.append(PwToolInfo(
+ label = ctx.label,
+ exe = exe,
+ path = None,
+ files = _bin_to_files(tool),
+ requires_any_of = tuple(),
+ execution_requirements = tuple(),
+ ))
+ else:
+ fail("Expected either a pw_cc_tool or a binary, but %s is neither" % tool.label)
+
+ tools = tuple(tools)
+ files = depset(transitive = [tool.files for tool in 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,
+ files = files,
+ )
+ action_configs = [
+ _generate_action_config(ctx, action, **common_kwargs)
+ for action in action_names
+ ]
+
+ return [
+ PwActionConfigSetInfo(
+ label = ctx.label,
+ action_configs = depset(action_configs),
+ ),
+ DefaultInfo(files = files),
+ ]
+
+pw_cc_action_config = rule(
+ implementation = _pw_cc_action_config_impl,
+ attrs = {
+ "action_names": attr.label_list(
+ providers = [PwActionNameSetInfo],
+ mandatory = True,
+ doc = """A list of action names to apply this action to.
+
+See @pw_toolchain//actions:all for valid options.
+""",
+ ),
+ "enabled": attr.bool(
+ default = True,
+ doc = """Whether or not this action config is enabled by default.
+
+Note: This defaults to `True` since it's assumed that most listed action configs
+will be enabled and used by default. This is the opposite of Bazel's native
+default.
+""",
+ ),
+ "tools": attr.label_list(
+ mandatory = True,
+ cfg = "exec",
+ doc = """The tool to use for the specified actions.
+
+A tool can be a `pw_cc_tool`, or a binary.
+
+If multiple tools are specified, the first tool that has `with_features` that
+satisfy the currently enabled feature set is used.
+""",
+ ),
+ "flag_sets": attr.label_list(
+ providers = [PwFlagSetInfo],
+ doc = """Labels that point to `pw_cc_flag_set`s that are
+unconditionally bound to the specified actions.
+
+Note: The flags in the `pw_cc_flag_set` are only bound to matching action names.
+If an action is listed in this rule's `action_names`, but is NOT listed in the
+`pw_cc_flag_set`'s `actions`, the flag will not be applied to that action.
+""",
+ ),
+ "implies": attr.label_list(
+ providers = [PwFeatureSetInfo],
+ doc = "Features that should be enabled when this action is used.",
+ ),
+ },
+ 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
+driving mechanism for controlling toolchain tool invocation/behavior.
+
+Action configs define three key things:
+
+* Which tools to invoke for a given type of action.
+* Tool features and compatibility.
+* `pw_cc_flag_set`s that are unconditionally bound to a tool invocation.
+
+Examples:
+
+ pw_cc_action_config(
+ name = "ar",
+ action_names = ["@pw_toolchain//actions:all_ar_actions"],
+ implies = [
+ "@pw_toolchain//features/legacy:archiver_flags",
+ "@pw_toolchain//features/legacy:linker_param_file",
+ ],
+ tools = [":ar_tool"],
+ )
+
+ pw_cc_action_config(
+ name = "clang",
+ action_names = [
+ "@pw_toolchain//actions:all_asm_actions",
+ "@pw_toolchain//actions:all_c_compiler_actions",
+ ],
+ tools = [":clang_tool"],
+ )
+""",
+)
diff --git a/pw_toolchain_bazel/cc_toolchain/private/action_files.bzl b/pw_toolchain_bazel/cc_toolchain/private/action_files.bzl
new file mode 100644
index 000000000..2a0ab49e5
--- /dev/null
+++ b/pw_toolchain_bazel/cc_toolchain/private/action_files.bzl
@@ -0,0 +1,74 @@
+# 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_action_files."""
+
+load(
+ ":providers.bzl",
+ "PwActionNameSetInfo",
+ "PwExtraActionFilesInfo",
+ "PwExtraActionFilesSetInfo",
+)
+
+def _pw_cc_action_files_impl(ctx):
+ actions = depset(transitive = [
+ action[PwActionNameSetInfo].actions
+ for action in ctx.attr.actions
+ ]).to_list()
+ files = depset(ctx.files.srcs)
+ return [
+ PwExtraActionFilesSetInfo(srcs = depset([
+ PwExtraActionFilesInfo(action = action, files = files)
+ for action in actions
+ ])),
+ DefaultInfo(files = files),
+ ]
+
+pw_cc_action_files = rule(
+ implementation = _pw_cc_action_files_impl,
+ attrs = {
+ "actions": attr.label_list(
+ providers = [PwActionNameSetInfo],
+ mandatory = True,
+ doc = "The actions that the files are required for",
+ ),
+ "srcs": attr.label_list(
+ allow_files = True,
+ doc = "Files required for these actions to correctly operate.",
+ ),
+ },
+ provides = [PwExtraActionFilesSetInfo],
+)
+
+def _pw_cc_action_files_set_impl(ctx):
+ return [
+ PwExtraActionFilesSetInfo(srcs = depset(transitive = [
+ ffa[PwExtraActionFilesSetInfo].srcs
+ for ffa in ctx.attr.srcs
+ ])),
+ DefaultInfo(files = depset(transitive = [
+ src[DefaultInfo].files
+ for src in ctx.attr.srcs
+ ])),
+ ]
+
+pw_cc_action_files_set = rule(
+ implementation = _pw_cc_action_files_set_impl,
+ attrs = {
+ "srcs": attr.label_list(
+ providers = [PwExtraActionFilesSetInfo],
+ doc = "The pw_cc_action_files to combine.",
+ ),
+ },
+ provides = [PwExtraActionFilesSetInfo],
+)
diff --git a/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl b/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl
index d36ec8e96..d022092bf 100644
--- a/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl
+++ b/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl
@@ -13,37 +13,25 @@
# the License.
"""Implementation of the pw_cc_toolchain rule."""
-load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
+load("//features:builtin_features.bzl", "BUILTIN_FEATURES")
load(
- "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
- "FlagSetInfo",
- "action_config",
- "feature",
- "flag_group",
- "flag_set",
- "tool",
- "variable_with_value",
+ ":providers.bzl",
+ "PwActionConfigSetInfo",
+ "PwActionNameSetInfo",
+ "PwExtraActionFilesSetInfo",
+ "PwFeatureInfo",
+ "PwFeatureSetInfo",
+ "PwFlagSetInfo",
+ "PwToolchainConfigInfo",
)
load(
- "//cc_toolchain/private:providers.bzl",
- "ToolchainFeatureInfo",
+ ":utils.bzl",
+ "ALL_FILE_GROUPS",
+ "to_untyped_config",
)
-load(
- "//cc_toolchain/private:utils.bzl",
- "ALL_ASM_ACTIONS",
- "ALL_CPP_COMPILER_ACTIONS",
- "ALL_C_COMPILER_ACTIONS",
- "ALL_LINK_ACTIONS",
- "check_deps",
-)
-
-# TODO(b/301004620): Remove when bazel 7 is released and this constant exists in
-# ACTION_NAMES
-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",
+# These attributes of pw_cc_toolchain are deprecated.
+PW_CC_TOOLCHAIN_DEPRECATED_TOOL_ATTRS = {
"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",
@@ -52,7 +40,13 @@ PW_CC_TOOLCHAIN_CONFIG_ATTRS = {
"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",
+}
+
+PW_CC_TOOLCHAIN_CONFIG_ATTRS = {
+ "action_configs": "List of `pw_cc_action_config` labels that bind tools to the appropriate actions",
+ "flag_sets": "List of `pw_cc_flag_set`s to unconditionally apply to their respective action configs",
+ "toolchain_features": "List of `pw_cc_feature`s that this toolchain supports",
+ "extra_action_files": "Files that are required to run specific actions.",
# Attributes originally part of create_cc_toolchain_config_info.
"toolchain_identifier": "See documentation for cc_common.create_cc_toolchain_config_info()",
@@ -64,6 +58,8 @@ PW_CC_TOOLCHAIN_CONFIG_ATTRS = {
"abi_version": "See documentation for cc_common.create_cc_toolchain_config_info()",
"abi_libc_version": "See documentation for cc_common.create_cc_toolchain_config_info()",
"cc_target_os": "See documentation for cc_common.create_cc_toolchain_config_info()",
+ "builtin_sysroot": "See documentation for cc_common.create_cc_toolchain_config_info()",
+ "cxx_builtin_include_directories": "See documentation for cc_common.create_cc_toolchain_config_info()",
}
PW_CC_TOOLCHAIN_SHARED_ATTRS = ["toolchain_identifier"]
@@ -71,153 +67,11 @@ PW_CC_TOOLCHAIN_SHARED_ATTRS = ["toolchain_identifier"]
PW_CC_TOOLCHAIN_BLOCKED_ATTRS = {
"toolchain_config": "pw_cc_toolchain includes a generated toolchain config",
"artifact_name_patterns": "pw_cc_toolchain does not yet support artifact name patterns",
- "features": "Use feature_deps to add pw_cc_toolchain_feature deps to the toolchain",
- "action_configs": "pw_cc_toolchain does not yet support action configs, use the \"ar\", \"cpp\", \"gcc\", \"gcov\", \"ld\", and \"strip\" attributes to set toolchain tools",
- "cxx_builtin_include_directories": "Use a pw_cc_toolchain_feature to add cxx_builtin_include_directories",
- "tool_paths": "pw_cc_toolchain does not support tool_paths, use \"ar\", \"cpp\", \"gcc\", \"gcov\", \"ld\", and \"strip\" attributes to set toolchain tools",
+ "features": "Use toolchain_features to add pw_cc_toolchain_feature deps to the toolchain",
+ "tool_paths": "pw_cc_toolchain does not support tool_paths, use \"action_configs\" to set toolchain tools",
"make_variables": "pw_cc_toolchain does not yet support make variables",
- "builtin_sysroot": "Use a pw_cc_toolchain_feature to add a builtin_sysroot",
}
-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
- specified actions.
- """
- return [
- action_config(
- action_name = action,
- tools = [
- tool(
- tool = action_tool,
- ),
- ],
- flag_sets = flag_sets_by_action.get(action, default = []),
- )
- for action in action_list
- ]
-
-def _archiver_flags_feature(is_mac):
- """Returns our implementation of the legacy archiver_flags feature.
-
- We provide our own implementation of the archiver_flags. The default
- implementation of this legacy feature at
- https://github.com/bazelbuild/bazel/blob/252d36384b8b630d77d21fac0d2c5608632aa393/src/main/java/com/google/devtools/build/lib/rules/cpp/CppActionConfigs.java#L620-L660
- contains a bug that prevents it from working with llvm-libtool-darwin only
- fixed in
- https://github.com/bazelbuild/bazel/commit/ae7cfa59461b2c694226be689662d387e9c38427,
- which has not yet been released.
-
- However, we don't merely fix the bug. Part of the Pigweed build involves
- linking some empty libraries (with no object files). This leads to invoking
- the archiving tool with no input files. Such an invocation is considered a
- success by llvm-ar, but not by llvm-libtool-darwin. So for now, we use
- flags appropriate for llvm-ar here, even on MacOS.
-
- Args:
- is_mac: Does the toolchain this feature will be included in target MacOS?
-
- Returns:
- The archiver_flags feature.
- """
-
- # TODO: b/297413805 - Remove this implementation.
- return feature(
- name = "archiver_flags",
- flag_sets = [
- flag_set(
- actions = [
- ACTION_NAMES.cpp_link_static_library,
- ],
- flag_groups = [
- flag_group(
- flags = _archiver_flags(is_mac),
- ),
- flag_group(
- expand_if_available = "output_execpath",
- flags = ["%{output_execpath}"],
- ),
- ],
- ),
- 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",
- ),
- ],
- ),
- ],
- ),
- ],
- )
-
-def _archiver_flags(is_mac):
- """Returns flags for llvm-ar."""
- if is_mac:
- return ["--format=darwin", "rcs"]
- 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.
@@ -227,110 +81,60 @@ def _pw_cc_toolchain_config_impl(ctx):
Returns:
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, 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(
- action_name = ACTION_NAMES.cpp_link_static_library,
- implies = ["archiver_flags", "linker_param_file"],
- tools = [
- tool(
- 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,
- tools = [
- tool(
- tool = ctx.executable.gcov,
- ),
- ],
- flag_sets = flag_sets_by_action.get(ACTION_NAMES.llvm_cov, default = []),
- ),
- action_config(
- action_name = OBJ_COPY_ACTION_NAME,
- tools = [
- tool(
- tool = ctx.executable.objcopy,
- ),
- ],
- flag_sets = flag_sets_by_action.get(OBJ_COPY_ACTION_NAME, default = []),
- ),
- action_config(
- action_name = OBJ_DUMP_ACTION_NAME,
- tools = [
- tool(
- tool = ctx.executable.objdump,
- ),
- ],
- flag_sets = flag_sets_by_action.get(OBJ_DUMP_ACTION_NAME, default = []),
- ),
- action_config(
- action_name = ACTION_NAMES.strip,
- tools = [
- tool(
- tool = ctx.executable.strip,
- ),
- ],
- flag_sets = flag_sets_by_action.get(ACTION_NAMES.strip, default = []),
+ 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
+
+ feature_set = PwFeatureSetInfo(features = depset(
+ [ft[PwFeatureInfo] for ft in ctx.attr._builtin_features],
+ transitive = [
+ feature_set[PwFeatureSetInfo].features
+ for feature_set in ctx.attr.toolchain_features
+ ],
+ ))
+ action_config_set = PwActionConfigSetInfo(
+ label = ctx.label,
+ action_configs = depset(transitive = [
+ acs[PwActionConfigSetInfo].action_configs
+ for acs in ctx.attr.action_configs
+ ]),
+ )
+ extra_action_files = PwExtraActionFilesSetInfo(srcs = depset(transitive = [
+ ffa[PwExtraActionFilesSetInfo].srcs
+ for ffa in ctx.attr.extra_action_files
+ ]))
+ flag_sets = [fs[PwFlagSetInfo] for fs in ctx.attr.flag_sets]
+ out = to_untyped_config(feature_set, action_config_set, flag_sets, extra_action_files)
+
+ extra = []
+ return [
+ cc_common.create_cc_toolchain_config_info(
+ ctx = ctx,
+ action_configs = out.action_configs,
+ features = out.features,
+ cxx_builtin_include_directories = builtin_include_dirs,
+ toolchain_identifier = ctx.attr.toolchain_identifier,
+ host_system_name = ctx.attr.host_system_name,
+ target_system_name = ctx.attr.target_system_name,
+ target_cpu = ctx.attr.target_cpu,
+ target_libc = ctx.attr.target_libc,
+ compiler = ctx.attr.compiler,
+ abi_version = ctx.attr.abi_version,
+ abi_libc_version = ctx.attr.abi_libc_version,
+ builtin_sysroot = sysroot_dir,
+ cc_target_os = ctx.attr.cc_target_os,
),
+ PwToolchainConfigInfo(action_to_files = out.action_to_files),
+ DefaultInfo(files = depset(transitive = extra + out.action_to_files.values())),
]
- features = [dep[ToolchainFeatureInfo].feature for dep in ctx.attr.feature_deps]
- features.append(_archiver_flags_feature(ctx.attr.target_libc == "macosx"))
- builtin_include_dirs = []
- for dep in ctx.attr.feature_deps:
- builtin_include_dirs.extend(dep[ToolchainFeatureInfo].cxx_builtin_include_directories)
-
- sysroot_dir = None
- for dep in ctx.attr.feature_deps:
- dep_sysroot = dep[ToolchainFeatureInfo].builtin_sysroot
- if dep_sysroot:
- if sysroot_dir:
- fail("Failed to set sysroot at `{}`, already have sysroot at `{}` ".format(dep_sysroot, sysroot_dir))
- sysroot_dir = dep_sysroot
-
- return cc_common.create_cc_toolchain_config_info(
- ctx = ctx,
- action_configs = all_actions,
- features = features,
- cxx_builtin_include_directories = builtin_include_dirs,
- toolchain_identifier = ctx.attr.toolchain_identifier,
- host_system_name = ctx.attr.host_system_name,
- target_system_name = ctx.attr.target_system_name,
- target_cpu = ctx.attr.target_cpu,
- target_libc = ctx.attr.target_libc,
- compiler = ctx.attr.compiler,
- abi_version = ctx.attr.abi_version,
- abi_libc_version = ctx.attr.abi_libc_version,
- builtin_sysroot = sysroot_dir,
- cc_target_os = ctx.attr.cc_target_os,
- )
-
pw_cc_toolchain_config = rule(
implementation = _pw_cc_toolchain_config_impl,
attrs = {
# Attributes new to this rule.
- "feature_deps": attr.label_list(),
- "gcc": attr.label(allow_single_file = True, executable = True, cfg = "exec"),
- "ld": attr.label(allow_single_file = True, executable = True, cfg = "exec"),
- "ar": attr.label(allow_single_file = True, executable = True, cfg = "exec"),
- "cpp": attr.label(allow_single_file = True, executable = True, cfg = "exec"),
- "gcov": attr.label(allow_single_file = True, executable = True, cfg = "exec"),
- "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(),
+ "action_configs": attr.label_list(providers = [PwActionConfigSetInfo]),
+ "flag_sets": attr.label_list(providers = [PwFlagSetInfo]),
+ "toolchain_features": attr.label_list(providers = [PwFeatureSetInfo]),
+ "extra_action_files": attr.label_list(providers = [PwExtraActionFilesSetInfo]),
# Attributes from create_cc_toolchain_config_info.
"toolchain_identifier": attr.string(),
@@ -342,8 +146,11 @@ pw_cc_toolchain_config = rule(
"abi_version": attr.string(),
"abi_libc_version": attr.string(),
"cc_target_os": attr.string(),
+ "builtin_sysroot": attr.string(),
+ "cxx_builtin_include_directories": attr.string_list(),
+ "_builtin_features": attr.label_list(default = BUILTIN_FEATURES),
},
- provides = [CcToolchainConfigInfo],
+ provides = [CcToolchainConfigInfo, PwToolchainConfigInfo],
)
def _check_args(rule_label, kwargs):
@@ -378,40 +185,97 @@ def _split_args(kwargs, filter_dict):
kwargs: Dictionary of args to split.
filter_dict: The dictionary used as the filter.
- Returns
+ Returns:
Tuple[Dict, Dict]
"""
filtered_args = {}
remainder = {}
for attr_name, val in kwargs.items():
- if attr_name in filter_dict:
+ if attr_name in ALL_FILE_GROUPS:
+ fail("Don't use %s. Instead, use pw_cc_action_files" % attr_name)
+ elif attr_name in filter_dict:
filtered_args[attr_name] = val
else:
remainder[attr_name] = val
return filtered_args, remainder
-def pw_cc_toolchain(**kwargs):
- """A bound cc_toolchain and pw_cc_toolchain_config pair.
+def _cc_file_collector_impl(ctx):
+ actions = depset(transitive = [
+ names[PwActionNameSetInfo].actions
+ for names in ctx.attr.actions
+ ]).to_list()
+ action_to_files = ctx.attr.config[PwToolchainConfigInfo].action_to_files
+
+ extra = []
+ return [DefaultInfo(files = depset(transitive = [
+ action_to_files[action]
+ for action in actions
+ ] + extra))]
+
+_cc_file_collector = rule(
+ implementation = _cc_file_collector_impl,
+ attrs = {
+ "config": attr.label(providers = [PwToolchainConfigInfo], mandatory = True),
+ "actions": attr.label_list(providers = [PwActionNameSetInfo], mandatory = True),
+ },
+)
+
+def pw_cc_toolchain(name, action_config_flag_sets = None, **kwargs):
+ """A suite of cc_toolchain, pw_cc_toolchain_config, and *_files rules.
+
+ Generated rules:
+ {name}: A `cc_toolchain` for this toolchain.
+ _{name}_config: A `pw_cc_toolchain_config` for this toolchain.
+ _{name}_*_files: Generated rules that group together files for
+ "ar_files", "as_files", "compiler_files", "coverage_files",
+ "dwp_files", "linker_files", "objcopy_files", and "strip_files"
+ normally enumerated as part of the `cc_toolchain` rule.
Args:
- **kwargs: All attributes supported by cc_toolchain and pw_cc_toolchain_config.
+ name: str: The name of the label for the toolchain.
+ action_config_flag_sets: Deprecated. Do not use.
+ **kwargs: All attributes supported by either cc_toolchain or pw_cc_toolchain_config.
"""
- _check_args(native.package_relative_label(kwargs["name"]), kwargs)
+ # TODO(b/322872628): Remove this once it's no longer in use.
+ if action_config_flag_sets != None:
+ kwargs["flag_sets"] = action_config_flag_sets
+
+ _check_args(native.package_relative_label(name), kwargs)
- cc_toolchain_config_args, cc_toolchain_args = _split_args(kwargs, PW_CC_TOOLCHAIN_CONFIG_ATTRS)
+ # Split args between `pw_cc_toolchain_config` and `native.cc_toolchain`.
+ cc_toolchain_config_args, cc_toolchain_args = _split_args(kwargs, PW_CC_TOOLCHAIN_CONFIG_ATTRS | PW_CC_TOOLCHAIN_DEPRECATED_TOOL_ATTRS)
# Bind pw_cc_toolchain_config and the cc_toolchain.
- config_name = "{}_config".format(cc_toolchain_args["name"])
- cc_toolchain_config_args["name"] = config_name
- cc_toolchain_args["toolchain_config"] = ":{}".format(config_name)
+ config_name = "_{}_config".format(name)
+ pw_cc_toolchain_config(
+ name = config_name,
+ visibility = ["//visibility:private"],
+ **cc_toolchain_config_args
+ )
+
+ for group, actions in ALL_FILE_GROUPS.items():
+ group_name = "_{}_{}".format(name, group)
+ _cc_file_collector(
+ name = group_name,
+ config = config_name,
+ actions = actions,
+ visibility = ["//visibility:private"],
+ )
+ cc_toolchain_args[group] = group_name
# Copy over arguments that should be shared by both rules.
for arg_name in PW_CC_TOOLCHAIN_SHARED_ATTRS:
if arg_name in cc_toolchain_config_args:
cc_toolchain_args[arg_name] = cc_toolchain_config_args[arg_name]
- pw_cc_toolchain_config(**cc_toolchain_config_args)
- native.cc_toolchain(**cc_toolchain_args)
+ native.cc_toolchain(
+ name = name,
+ toolchain_config = config_name,
+ # TODO: b/321268080 - Remove after transition of this option is complete.
+ exec_transition_for_inputs = False,
+ all_files = config_name,
+ **cc_toolchain_args
+ )
diff --git a/pw_toolchain_bazel/cc_toolchain/private/feature.bzl b/pw_toolchain_bazel/cc_toolchain/private/feature.bzl
new file mode 100644
index 000000000..c2addbe10
--- /dev/null
+++ b/pw_toolchain_bazel/cc_toolchain/private/feature.bzl
@@ -0,0 +1,293 @@
+# 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 the pw_cc_feature and pw_cc_feature_set rules."""
+
+load(
+ ":providers.bzl",
+ "PwBuiltinFeatureInfo",
+ "PwFeatureConstraintInfo",
+ "PwFeatureInfo",
+ "PwFeatureSetInfo",
+ "PwFlagSetInfo",
+ "PwMutuallyExclusiveCategoryInfo",
+)
+
+def _pw_cc_mutually_exclusive_category_impl(ctx):
+ return [PwMutuallyExclusiveCategoryInfo(
+ name = ctx.attr.category_name or str(ctx.label),
+ )]
+
+pw_cc_mutually_exclusive_category = rule(
+ implementation = _pw_cc_mutually_exclusive_category_impl,
+ provides = [PwMutuallyExclusiveCategoryInfo],
+ doc = """Creates a category of mutually exclusive features.
+
+Example:
+
+ pw_cc_mutually_exclusive_category(
+ name = "compilation_mode",
+ )
+
+ pw_cc_feature(name = "opt", mutually_exclusive = [":compilation_mode"], ...)
+ pw_cc_feature(name = "dbg", mutually_exclusive = [":compilation_mode"], ...)
+ pw_cc_feature(name = "fastbuild", mutually_exclusive = [":compilation_mode"], ...)
+
+""",
+ attrs = {
+ "category_name": attr.string(
+ doc = """A backdoor to support old-style provides
+
+Not recommended to be used, as it can clash with other provides.
+""",
+ ),
+ },
+)
+
+def _pw_cc_feature_set_impl(ctx):
+ if not ctx.attr.all_of:
+ fail("At least one feature must be specified in `all_of`")
+ features = depset(transitive = [attr[PwFeatureSetInfo].features for attr in ctx.attr.all_of])
+
+ return [
+ PwFeatureSetInfo(features = features),
+ PwFeatureConstraintInfo(all_of = features, none_of = depset([])),
+ ]
+
+pw_cc_feature_set = rule(
+ implementation = _pw_cc_feature_set_impl,
+ attrs = {
+ "all_of": attr.label_list(
+ providers = [PwFeatureSetInfo],
+ doc = """Features that must be enabled for this feature set to be deemed compatible with the current toolchain configuration.""",
+ ),
+ },
+ provides = [PwFeatureSetInfo],
+ doc = """Defines a set of features.
+
+Example:
+
+ pw_cc_feature_set(
+ name = "thin_lto_requirements",
+ all_of = [
+ ":thin_lto",
+ ":opt",
+ ],
+ )
+""",
+)
+
+def _pw_cc_feature_impl(ctx):
+ name = ctx.attr.feature_name
+ if name.startswith("implied_by_"):
+ fail("Feature names starting with 'implied_by' are reserved")
+
+ implies_features = depset(transitive = [
+ attr[PwFeatureSetInfo].features
+ for attr in ctx.attr.implies
+ ])
+ requires = [req[PwFeatureSetInfo] for req in ctx.attr.requires_any_of]
+
+ overrides = None
+ if ctx.attr.overrides != None:
+ overrides = ctx.attr.overrides[PwFeatureInfo]
+ if overrides.name != name:
+ fail("%s is supposed to override %s, but they have different feature names" % (ctx.label, overrides.label))
+
+ feature = PwFeatureInfo(
+ label = ctx.label,
+ name = name,
+ enabled = ctx.attr.enabled,
+ flag_sets = depset([
+ fs[PwFlagSetInfo]
+ for fs in ctx.attr.flag_sets
+ ]),
+ implies_features = implies_features,
+ implies_action_configs = depset([]),
+ requires_any_of = tuple(requires),
+ provides = tuple([
+ p[PwMutuallyExclusiveCategoryInfo].name
+ for p in ctx.attr.mutually_exclusive
+ ]),
+ known = False,
+ overrides = overrides,
+ )
+
+ return [
+ feature,
+ PwFeatureSetInfo(features = depset([feature])),
+ PwFeatureConstraintInfo(all_of = depset([feature]), none_of = depset([])),
+ PwMutuallyExclusiveCategoryInfo(name = name),
+ ]
+
+pw_cc_feature = rule(
+ implementation = _pw_cc_feature_impl,
+ attrs = {
+ "feature_name": attr.string(
+ mandatory = True,
+ doc = """The name of the feature that this rule implements.
+
+Feature names are used to express feature dependencies and compatibility.
+Because features are tracked by string names rather than labels, there's great
+flexibility in swapping out feature implementations or overriding the built-in
+legacy features that Bazel silently binds to every toolchain.
+
+`feature_name` is used rather than `name` to distinguish between the rule
+name, and the intended final feature name. This allows similar rules to exist
+in the same package, even if slight differences are required.
+
+Example:
+
+ pw_cc_feature(
+ name = "sysroot_macos",
+ feature_name = "sysroot",
+ ...
+ )
+
+ pw_cc_feature(
+ name = "sysroot_linux",
+ feature_name = "sysroot",
+ ...
+ )
+
+While two features with the same `feature_name` may not be bound to the same
+toolchain, they can happily live alongside each other in the same BUILD file.
+""",
+ ),
+ "enabled": attr.bool(
+ mandatory = True,
+ doc = """Whether or not this feature is enabled by default.""",
+ ),
+ "flag_sets": attr.label_list(
+ doc = """Flag sets that, when expanded, implement this feature.""",
+ providers = [PwFlagSetInfo],
+ ),
+ "requires_any_of": attr.label_list(
+ doc = """A list of feature sets that define toolchain compatibility.
+
+If *at least one* of the listed `pw_cc_feature_set`s are fully satisfied (all
+features exist in the toolchain AND are currently enabled), this feature is
+deemed compatible and may be enabled.
+
+Note: Even if `pw_cc_feature.requires_any_of` is satisfied, a feature is not
+enabled unless another mechanism (e.g. command-line flags,
+`pw_cc_feature.implies`,`pw_cc_feature.enabled`) signals that the feature should
+actually be enabled.
+""",
+ providers = [PwFeatureSetInfo],
+ ),
+ "implies": attr.label_list(
+ providers = [PwFeatureSetInfo],
+ doc = """List of features enabled along with this feature.
+
+Warning: If any of the named features cannot be enabled, this feature is
+silently disabled.
+""",
+ ),
+ "mutually_exclusive": attr.label_list(
+ providers = [PwMutuallyExclusiveCategoryInfo],
+ doc = """A list of things that this is mutually exclusive with.
+
+It can be either:
+* A feature, in which case the two features are mutually exclusive.
+* A `pw_cc_mutually_exclusive_category`, in which case all features that write
+ `mutually_exclusive = [":category"]` are mutually exclusive with each other.
+
+If this feature has a side-effect of implementing another feature, it can be
+useful to list that feature here to ensure they aren't enabled at the
+same time.
+""",
+ ),
+ "overrides": attr.label(
+ providers = [PwBuiltinFeatureInfo, PwFeatureInfo],
+ doc = """A declaration that this feature overrides a known feature.
+
+In the example below, if you missed the "overrides" attribute, it would complain
+that the feature "opt" was defined twice.
+
+Example:
+
+ pw_cc_feature(
+ name = "opt",
+ feature_name = "opt",
+ ...
+ overrides = "@pw_toolchain//features/well_known:opt",
+ )
+
+""",
+ ),
+ },
+ provides = [
+ PwFeatureInfo,
+ PwFeatureSetInfo,
+ PwFeatureConstraintInfo,
+ PwMutuallyExclusiveCategoryInfo,
+ ],
+ doc = """Defines the implemented behavior of a C/C++ toolchain feature.
+
+This rule is effectively a wrapper for the `feature` constructor in
+@rules_cc//cc:cc_toolchain_config_lib.bzl.
+
+A feature is basically a dynamic flag set. There are a variety of dependencies
+and compatibility requirements that must be satisfied for the listed flag sets
+to be applied.
+
+A feature may be enabled or disabled through the following mechanisms:
+* Via command-line flags, or a `.bazelrc`.
+* Through inter-feature relationships (enabling one feature may implicitly
+ enable another).
+* Individual rules may elect to manually enable or disable features through the
+ builtin ``features`` attribute.
+
+Because of the dynamic nature of toolchain features, it's generally best to
+avoid enumerating features as part of your toolchain with the following
+exceptions:
+* You want the flags to be controllable via Bazel's CLI. For example, adding
+ `-v` to a compiler invocation is often too verbose to be useful for most
+ workflows, but can be instrumental when debugging obscure errors. By
+ expressing compiler verbosity as a feature, users may opt-in when necessary.
+* You need to carry forward Starlark toolchain behaviors. If you're migrating a
+ complex Starlark-based toolchain definition to these rules, many of the
+ workflows and flags were likely based on features. This rule exists to support
+ those existing structures.
+
+For more details about how Bazel handles features, see the official Bazel
+documentation at
+https://bazel.build/docs/cc-toolchain-config-reference#features.
+
+Examples:
+
+ # A feature that can be easily toggled to include extra compiler output to
+ # help debug things like include search path ordering and showing all the
+ # flags passed to the compiler.
+ #
+ # Add `--features=verbose_compiler_output` to your Bazel invocation to
+ # enable.
+ pw_cc_feature(
+ name = "verbose_compiler_output",
+ enabled = False,
+ feature_name = "verbose_compiler_output",
+ flag_sets = [":verbose_compiler_flags"],
+ )
+
+ # This feature signals a capability, and doesn't have associated flags.
+ #
+ # For a list of well-known features, see:
+ # https://bazel.build/docs/cc-toolchain-config-reference#wellknown-features
+ pw_cc_feature(
+ name = "link_object_files",
+ enabled = True,
+ feature_name = "supports_start_end_lib",
+ )
+""",
+)
diff --git a/pw_toolchain_bazel/cc_toolchain/private/feature_constraint.bzl b/pw_toolchain_bazel/cc_toolchain/private/feature_constraint.bzl
new file mode 100644
index 000000000..e43357592
--- /dev/null
+++ b/pw_toolchain_bazel/cc_toolchain/private/feature_constraint.bzl
@@ -0,0 +1,43 @@
+# 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 the pw_cc_feature_constraint rule."""
+
+load(":providers.bzl", "PwFeatureConstraintInfo", "PwFeatureSetInfo")
+
+visibility("//cc_toolchain")
+
+def _pw_cc_feature_constraint_impl(ctx):
+ all_of = [fp[PwFeatureConstraintInfo] for fp in ctx.attr.all_of]
+ none_of = [
+ fs[PwFeatureSetInfo].features
+ for fs in ctx.attr.none_of
+ ]
+ none_of.extend([fp.none_of for fp in all_of])
+ return [PwFeatureConstraintInfo(
+ all_of = depset(transitive = [fp.all_of for fp in all_of]),
+ none_of = depset(transitive = none_of),
+ )]
+
+pw_cc_feature_constraint = rule(
+ implementation = _pw_cc_feature_constraint_impl,
+ attrs = {
+ "all_of": attr.label_list(
+ providers = [PwFeatureConstraintInfo],
+ ),
+ "none_of": attr.label_list(
+ providers = [PwFeatureSetInfo],
+ ),
+ },
+ provides = [PwFeatureConstraintInfo],
+)
diff --git a/pw_toolchain_bazel/cc_toolchain/private/flag_set.bzl b/pw_toolchain_bazel/cc_toolchain/private/flag_set.bzl
index 23094e864..ee810e304 100644
--- a/pw_toolchain_bazel/cc_toolchain/private/flag_set.bzl
+++ b/pw_toolchain_bazel/cc_toolchain/private/flag_set.bzl
@@ -14,11 +14,15 @@
"""Implementation of pw_cc_flag_set and pw_cc_flag_group."""
load(
- "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
- "FlagGroupInfo",
- "FlagSetInfo",
+ "@rules_cc//cc:cc_toolchain_config_lib.bzl",
"flag_group",
- "flag_set",
+)
+load(
+ ":providers.bzl",
+ "PwActionNameSetInfo",
+ "PwFeatureConstraintInfo",
+ "PwFlagGroupInfo",
+ "PwFlagSetInfo",
)
def _pw_cc_flag_group_impl(ctx):
@@ -52,7 +56,7 @@ pw_cc_flag_group = 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`.
+provides `PwFlagGroupInfo` or `PwFlagSetInfo`.
""",
),
"iterate_over": attr.string(
@@ -94,7 +98,7 @@ Example:
doc = "Expands the expression in `flags` if the specified build variable is NOT set.",
),
},
- provides = [FlagGroupInfo],
+ provides = [PwFlagGroupInfo],
doc = """Declares an (optionally parametric) ordered set of flags.
`pw_cc_flag_group` rules are expected to be consumed exclusively by
@@ -148,59 +152,80 @@ def _pw_cc_flag_set_impl(ctx):
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,
- )
+ if not dep[PwFlagGroupInfo]:
+ fail("{} in `flag_groups` of {} does not provide PwFlagGroupInfo".format(dep.label, ctx.label))
+
+ flag_groups = [dep[PwFlagGroupInfo] for dep in ctx.attr.flag_groups]
+
+ actions = depset(transitive = [
+ action[PwActionNameSetInfo].actions
+ for action in ctx.attr.actions
+ ]).to_list()
+ if not actions:
+ fail("Each pw_cc_flag_set must specify at least one action")
+
+ requires = [fc[PwFeatureConstraintInfo] for fc in ctx.attr.requires_any_of]
+ return [
+ PwFlagSetInfo(
+ label = ctx.label,
+ actions = tuple(actions),
+ requires_any_of = tuple(requires),
+ flag_groups = tuple(flag_groups),
+ env = ctx.attr.env,
+ env_expand_if_available = ctx.attr.env_expand_if_available,
+ ),
+ ]
pw_cc_flag_set = rule(
implementation = _pw_cc_flag_set_impl,
attrs = {
- "actions": attr.string_list(
+ "actions": attr.label_list(
+ providers = [PwActionNameSetInfo],
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.
+See @pw_toolchain//actions:all for valid options.
""",
- # 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
+`PwFlagGroupInfo`. 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.
""",
),
+ "env": attr.string_dict(
+ doc = "Environment variables to be added to these actions",
+ ),
+ "env_expand_if_available": attr.string(
+ doc = "A build variable that needs to be available in order to expand the env entries",
+ ),
"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`.
+`PwFlagGroupInfo`.
Note: `flags` and `flag_groups` are mutually exclusive.
""",
),
+ "requires_any_of": attr.label_list(
+ providers = [PwFeatureConstraintInfo],
+ doc = """This will be enabled when any of the constraints are met.
+
+If omitted, this flag set will be enabled unconditionally.
+""",
+ ),
},
- provides = [FlagSetInfo],
+ provides = [PwFlagSetInfo],
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`.
+Flag sets can be attached to a `pw_cc_toolchain` via `flag_sets`.
Examples:
diff --git a/pw_toolchain_bazel/cc_toolchain/private/providers.bzl b/pw_toolchain_bazel/cc_toolchain/private/providers.bzl
index a63a7d17a..ad5a3e465 100644
--- a/pw_toolchain_bazel/cc_toolchain/private/providers.bzl
+++ b/pw_toolchain_bazel/cc_toolchain/private/providers.bzl
@@ -13,11 +13,126 @@
# the License.
"""All shared providers that act as an API between toolchain-related rules."""
-ToolchainFeatureInfo = provider(
- doc = "A provider containing cc_toolchain features and related fields.",
+load(
+ "@rules_cc//cc:cc_toolchain_config_lib.bzl",
+ "FlagGroupInfo",
+)
+load("//actions:providers.bzl", "ActionNameInfo", "ActionNameSetInfo")
+
+visibility(["//cc_toolchain", "//cc_toolchain/tests/..."])
+
+# Note that throughout this file, we never use a list. This is because mutable
+# types cannot be stored in depsets. Thus, we type them as a sequence in the
+# provider, and convert them to a tuple in the constructor to ensure
+# immutability.
+
+PwActionNameInfo = ActionNameInfo
+PwActionNameSetInfo = ActionNameSetInfo
+
+PwFlagGroupInfo = FlagGroupInfo
+PwFlagSetInfo = provider(
+ doc = "A type-safe version of @bazel_tools's FlagSetInfo",
+ fields = {
+ "label": "Label: The label that defined this flag set. Put this in error messages for easy debugging",
+ "actions": "Sequence[str]: The set of actions this is associated with",
+ "requires_any_of": "Sequence[FeatureConstraintInfo]: This will be enabled if any of the listed predicates are met. Equivalent to with_features",
+ "flag_groups": "Sequence[FlagGroupInfo]: Set of flag groups to include.",
+ "env": "Mapping[str, str]: Environment variables to apply with the flags",
+ "env_expand_if_available": "Option[str]: The build variable that needs to be available in order to expand the env entry.",
+ },
+)
+
+PwFeatureInfo = provider(
+ doc = "A type-safe version of @bazel_tools's FeatureInfo",
+ fields = {
+ "label": "Label: The label that defined this feature. Put this in error messages for easy debugging",
+ "name": "str: The name of the feature",
+ "enabled": "bool: Whether this feature is enabled by default",
+ "flag_sets": "depset[FlagSetInfo]: Flag sets enabled by this feature",
+ "implies_features": "depset[FeatureInfo]: Set of features implied by this feature",
+ "implies_action_configs": "depset[ActionConfigInfo]: Set of action configs enabled by this feature",
+ "requires_any_of": "Sequence[FeatureSetInfo]: A list of feature sets, at least one of which is required to enable this feature. This is semantically equivalent to the requires attribute of rules_cc's FeatureInfo",
+ "provides": "Sequence[str]: Indicates that this feature is one of several mutually exclusive alternate features.",
+ "known": "bool: Whether the feature is a known feature. Known features are assumed to be defined elsewhere.",
+ "overrides": "Optional[FeatureInfo]: The feature that this overrides",
+ },
+)
+PwFeatureSetInfo = provider(
+ doc = "A type-safe version of @bazel_tools's FeatureSetInfo",
+ fields = {
+ "features": "depset[FeatureInfo]: The set of features this corresponds to",
+ },
+)
+PwFeatureConstraintInfo = provider(
+ doc = "A type-safe version of @bazel_tools's WithFeatureSetInfo",
+ fields = {
+ "label": "Label: The label that defined this predicate. Put this in error messages for easy debugging",
+ "all_of": "depset[FeatureInfo]: A set of features which must be enabled",
+ "none_of": "depset[FeatureInfo]: A set of features, none of which can be enabled",
+ },
+)
+PwBuiltinFeatureInfo = provider(
+ doc = "A tag marking something as a known feature. The only use of this is to ensure that pw_cc_feature disallows override = <non known feature>.",
+ fields = {},
+)
+PwMutuallyExclusiveCategoryInfo = provider(
+ doc = "Multiple features with the category will be mutally exclusive",
+ fields = {
+ "name": "str: The name of the provider",
+ },
+)
+
+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",
+ "files": "depset[File]: The files required to run these actions",
+ },
+)
+
+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 = provider(
+ doc = "A type-safe version of @bazel_tool's ToolInfo",
+ fields = {
+ "label": "Label: The label that defined this tool",
+ "exe": "Optional[File]: The file corresponding to the tool",
+ "path": "Optional[str]: The path to the tool. Prefer tool (mutually exclusive with tool).",
+ "files": "Depset[File]: The files associated with the tool",
+ "requires_any_of": "Sequence[PwFeatureConstraintInfo]: A set of constraints required to enable the tool. Equivalent to with_features",
+ "execution_requirements": "Sequence[str]: A set of execution requirements of the tool",
+ },
+)
+
+PwExtraActionFilesInfo = provider(
+ doc = "Extra files to provide to an action",
+ fields = {
+ "action": "str: The action to associate with",
+ "files": "depset[File]: Files to add to this action",
+ },
+)
+PwExtraActionFilesSetInfo = provider(
+ doc = "Set of PwExtraActionFilesInfo",
+ fields = {
+ "srcs": "depset[PwExtraActionFilesInfo]: Sets of action files",
+ },
+)
+
+PwToolchainConfigInfo = provider(
+ doc = "Additional metadata about the config of the pigweed toolchain.",
fields = {
- "feature": "feature: A group of build flags structured as a toolchain feature.",
- "cxx_builtin_include_directories": "List[str]: Builtin C/C++ standard library include directories.",
- "builtin_sysroot": "str: Path to the sysroot directory. Use `external/[repo_name]` for sysroots provided as an external repository.",
+ "action_to_files": "Dict[str, depset[File]]: A set of files required to execute a given action",
},
)
diff --git a/pw_toolchain_bazel/cc_toolchain/private/toolchain_feature.bzl b/pw_toolchain_bazel/cc_toolchain/private/toolchain_feature.bzl
deleted file mode 100644
index e16d331da..000000000
--- a/pw_toolchain_bazel/cc_toolchain/private/toolchain_feature.bzl
+++ /dev/null
@@ -1,215 +0,0 @@
-# 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 the pw_cc_toolchain_feature rule."""
-
-load(
- "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
- "feature",
- "flag_group",
- "flag_set",
-)
-load(
- "//cc_toolchain/private:providers.bzl",
- "ToolchainFeatureInfo",
-)
-load("//cc_toolchain/private:utils.bzl", "ACTION_MAP")
-
-TOOLCHAIN_FEATURE_INFO_ATTRS = {
- "aropts": "List[str]: Flags to pass to all archive actions",
- "asmopts": "List[str]: Flags to pass to assembler actions",
- "copts": "List[str]: Flags to pass to all C/C++ compile actions",
- "conlyopts": "List[str]: Flags to pass to C compile actions",
- "cxxopts": "List[str]: Flags to pass to C++ compile actions",
- "linkopts": "List[str]: Flags to pass to C compile actions",
- "linker_files": "List[File]: Files to link",
- "cxx_builtin_include_directories": "List[str]: Paths to C++ standard library include directories",
- "builtin_sysroot": "str: Path to the directory containing the sysroot",
-}
-
-def _dict_to_str(dict_to_stringify):
- """Converts a dictionary to a multi-line string.
-
- Args:
- dict_to_stringify (Dict[str, str]): Dictionary to stringify.
-
- Returns:
- str: Multi-line string representing the dictionary, or {empty}.
- """
- result = []
- for key in dict_to_stringify.keys():
- result.append(" {}: {}".format(key, dict_to_stringify[key]))
- if not result:
- return "{empty}"
- return "\n".join(["{"] + result + ["}"])
-
-def _feature_flag_set(actions, flags):
- """Transforms a list of flags and actions into a flag_set.
-
- Args:
- actions (List[str]): Actions that the provided flags will be applied to.
- flags (List[str]): List of flags to apply to the specified actions.
-
- Returns:
- flag_set: A flag_set binding the provided flags to the specified
- actions.
- """
- return flag_set(
- actions = actions,
- flag_groups = ([
- flag_group(
- flags = flags,
- ),
- ]),
- )
-
-def _check_args(ctx, **kwargs):
- """Checks that args provided to build_toolchain_feature_info are valid.
-
- Args:
- ctx: The context of the current build rule.
- **kwargs: All attributes supported by pw_cc_toolchain_feature.
-
- Returns:
- None
- """
- for key in kwargs.keys():
- if key not in TOOLCHAIN_FEATURE_INFO_ATTRS:
- fail(
- "Unknown attribute \"{}\" used by {}. Valid attributes are:\n{}".format(
- key,
- ctx.label,
- _dict_to_str(TOOLCHAIN_FEATURE_INFO_ATTRS),
- ),
- )
-
-def _build_flag_sets(**kwargs):
- """Transforms a dictionary of arguments into a list of flag_sets.
-
- Args:
- **kwargs: All attributes supported by pw_cc_toolchain_feature.
-
- Returns:
- List[flag_set]: A list of flag_sets that bind all provided flags to
- their appropriate actions.
- """
- all_flags = []
- for action in ACTION_MAP.keys():
- if kwargs[action]:
- all_flags.append(_feature_flag_set(ACTION_MAP[action], kwargs[action]))
- return all_flags
-
-def _initialize_args(**kwargs):
- """Initializes build_toolchain_feature_info arguments.
-
- Args:
- **kwargs: All attributes supported by pw_cc_toolchain_feature.
-
- Returns:
- Dict[str, Any]: Dictionary containing arguments default initialized to
- be compatible with _build_flag_sets.
- """
- initialized_args = {}
- for action in ACTION_MAP.keys():
- if action in kwargs:
- initialized_args[action] = kwargs[action]
- else:
- initialized_args[action] = []
-
- if "linker_files" in kwargs:
- linker_files = kwargs["linker_files"]
- linker_flags = [file.path for file in linker_files]
-
- initialized_args["linkopts"] = initialized_args["linkopts"] + linker_flags
- initialized_args["linker_files"] = depset(linker_files)
- else:
- initialized_args["linker_files"] = depset()
-
- if "cxx_builtin_include_directories" in kwargs:
- initialized_args["cxx_builtin_include_directories"] = kwargs["cxx_builtin_include_directories"]
- else:
- initialized_args["cxx_builtin_include_directories"] = []
-
- if "builtin_sysroot" in kwargs:
- initialized_args["builtin_sysroot"] = kwargs["builtin_sysroot"]
- else:
- initialized_args["builtin_sysroot"] = None
- return initialized_args
-
-def build_toolchain_feature_info(ctx, **kwargs):
- """Builds a ToolchainFeatureInfo provider.
-
- Args:
- ctx: The context of the current build rule.
- **kwargs: All attributes supported by pw_cc_toolchain_feature.
-
- Returns:
- ToolchainFeatureInfo, DefaultInfo: All providers supported by
- pw_cc_toolchain_feature.
- """
- _check_args(ctx, **kwargs)
-
- initialized_args = _initialize_args(**kwargs)
-
- new_feature = feature(
- name = ctx.attr.name,
- enabled = True,
- flag_sets = _build_flag_sets(**initialized_args),
- )
-
- return [
- ToolchainFeatureInfo(
- feature = new_feature,
- cxx_builtin_include_directories = initialized_args["cxx_builtin_include_directories"],
- builtin_sysroot = initialized_args["builtin_sysroot"],
- ),
- DefaultInfo(files = initialized_args["linker_files"]),
- ]
-
-def _pw_cc_toolchain_feature_impl(ctx):
- """Rule that provides ToolchainFeatureInfo.
-
- Args:
- ctx: The context of the current build rule.
-
- Returns:
- ToolchainFeatureInfo, DefaultInfo
- """
- return build_toolchain_feature_info(
- ctx = ctx,
- aropts = ctx.attr.aropts,
- asmopts = ctx.attr.asmopts,
- copts = ctx.attr.copts,
- conlyopts = ctx.attr.conlyopts,
- cxxopts = ctx.attr.cxxopts,
- linkopts = ctx.attr.linkopts,
- linker_files = ctx.files.linker_files,
- cxx_builtin_include_directories = ctx.attr.cxx_builtin_include_directories,
- builtin_sysroot = ctx.attr.builtin_sysroot,
- )
-
-pw_cc_toolchain_feature = rule(
- implementation = _pw_cc_toolchain_feature_impl,
- attrs = {
- "aropts": attr.string_list(),
- "asmopts": attr.string_list(),
- "copts": attr.string_list(),
- "conlyopts": attr.string_list(),
- "cxxopts": attr.string_list(),
- "linkopts": attr.string_list(),
- "linker_files": attr.label_list(allow_files = True),
- "cxx_builtin_include_directories": attr.string_list(),
- "builtin_sysroot": attr.string(),
- },
- provides = [ToolchainFeatureInfo, DefaultInfo],
-)
diff --git a/pw_toolchain_bazel/cc_toolchain/private/unsafe_feature.bzl b/pw_toolchain_bazel/cc_toolchain/private/unsafe_feature.bzl
new file mode 100644
index 000000000..ebb4d1def
--- /dev/null
+++ b/pw_toolchain_bazel/cc_toolchain/private/unsafe_feature.bzl
@@ -0,0 +1,59 @@
+# 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 the pw_cc_unsafe_feature rule."""
+
+load(
+ ":providers.bzl",
+ "PwBuiltinFeatureInfo",
+ "PwFeatureConstraintInfo",
+ "PwFeatureInfo",
+ "PwFeatureSetInfo",
+)
+
+def _pw_cc_unsafe_feature_impl(ctx):
+ feature = PwFeatureInfo(
+ label = ctx.label,
+ name = ctx.attr.feature_name,
+ enabled = False,
+ flag_sets = depset([]),
+ implies_features = depset([]),
+ implies_action_configs = depset([]),
+ requires_any_of = tuple([]),
+ provides = depset([]),
+ known = True,
+ overrides = None,
+ )
+ providers = [
+ feature,
+ PwFeatureSetInfo(features = depset([feature])),
+ PwFeatureConstraintInfo(all_of = depset([feature]), none_of = depset([])),
+ ]
+ if ctx.attr.builtin:
+ providers.append(PwBuiltinFeatureInfo())
+ return providers
+
+pw_cc_unsafe_feature = rule(
+ implementation = _pw_cc_unsafe_feature_impl,
+ attrs = {
+ "feature_name": attr.string(
+ mandatory = True,
+ doc = "The name of the feature",
+ ),
+ "builtin": attr.bool(
+ doc = "Whether the feature is builtin, and can be overridden",
+ ),
+ },
+ provides = [PwFeatureInfo, PwFeatureSetInfo, PwFeatureConstraintInfo],
+ doc = "A declaration that a feature with this name is defined elsewhere.",
+)
diff --git a/pw_toolchain_bazel/cc_toolchain/private/utils.bzl b/pw_toolchain_bazel/cc_toolchain/private/utils.bzl
index 7030f2498..b9400daaf 100644
--- a/pw_toolchain_bazel/cc_toolchain/private/utils.bzl
+++ b/pw_toolchain_bazel/cc_toolchain/private/utils.bzl
@@ -13,61 +13,273 @@
# the License.
"""Private utilities and global variables."""
-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",
+ "@rules_cc//cc:cc_toolchain_config_lib.bzl",
+ rules_cc_action_config = "action_config",
+ rules_cc_env_entry = "env_entry",
+ rules_cc_env_set = "env_set",
+ rules_cc_feature = "feature",
+ rules_cc_feature_set = "feature_set",
+ rules_cc_flag_set = "flag_set",
+ rules_cc_with_feature_set = "with_feature_set",
)
-# A potentially more complete set of these constants are available at
-# @rules_cc//cc:action_names.bzl, but it's not clear if they should be depended
-# on.
-ALL_ASM_ACTIONS = [
- ACTION_NAMES.assemble,
- ACTION_NAMES.preprocess_assemble,
-]
-ALL_C_COMPILER_ACTIONS = [
- ACTION_NAMES.c_compile,
- ACTION_NAMES.cc_flags_make_variable,
-]
-ALL_CPP_COMPILER_ACTIONS = [
- ACTION_NAMES.cpp_compile,
- ACTION_NAMES.cpp_header_parsing,
-]
-ALL_LINK_ACTIONS = [
- ACTION_NAMES.cpp_link_executable,
- ACTION_NAMES.cpp_link_dynamic_library,
- ACTION_NAMES.cpp_link_nodeps_dynamic_library,
-]
-ALL_AR_ACTIONS = [
- ACTION_NAMES.cpp_link_static_library,
-]
-
-ACTION_MAP = {
- "aropts": ALL_AR_ACTIONS,
- "asmopts": ALL_ASM_ACTIONS,
- "copts": ALL_C_COMPILER_ACTIONS + ALL_CPP_COMPILER_ACTIONS,
- "conlyopts": ALL_C_COMPILER_ACTIONS,
- "cxxopts": ALL_CPP_COMPILER_ACTIONS,
- "linkopts": ALL_LINK_ACTIONS,
+visibility(["//cc_toolchain/tests/..."])
+
+ALL_FILE_GROUPS = {
+ "ar_files": ["@pw_toolchain//actions:all_ar_actions"],
+ "as_files": ["@pw_toolchain//actions:all_asm_actions"],
+ "compiler_files": ["@pw_toolchain//actions:all_compiler_actions"],
+ "coverage_files": ["@pw_toolchain//actions:llvm_cov"],
+ "dwp_files": [],
+ "linker_files": ["@pw_toolchain//actions:all_link_actions"],
+ "objcopy_files": ["@pw_toolchain//actions:objcopy_embed_data"],
+ "strip_files": ["@pw_toolchain//actions:strip"],
}
-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 _ensure_fulfillable(any_of, known, label, fail = fail):
+ # Requirements can be fulfilled if there are no requirements.
+ fulfillable = not any_of
+ for group in any_of:
+ all_met = True
+ for entry in group.to_list():
+ if entry.label not in known:
+ all_met = False
+ break
+ if all_met:
+ fulfillable = True
+ break
+
+ 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):
+ """Converts a PwFlagSet to rules_cc's flag set."""
+ _ensure_fulfillable(
+ any_of = [constraint.all_of for constraint in flag_set.requires_any_of],
+ known = known,
+ label = flag_set.label,
+ fail = fail,
+ )
+ actions = list(flag_set.actions)
+ with_features = [
+ _to_untyped_feature_constraint(fc)
+ for fc in flag_set.requires_any_of
+ ]
+
+ out_flag_set = None
+ if flag_set.flag_groups:
+ out_flag_set = rules_cc_flag_set(
+ flag_groups = list(flag_set.flag_groups),
+ actions = actions,
+ with_features = with_features,
+ )
+
+ out_env_set = None
+ if flag_set.env:
+ out_env_set = rules_cc_env_set(
+ env_entries = [
+ rules_cc_env_entry(
+ key = key,
+ value = value,
+ expand_if_available = flag_set.env_expand_if_available,
+ )
+ for key, value in flag_set.env.items()
+ ],
+ actions = actions,
+ with_features = with_features,
+ )
+ return struct(
+ flag_set = out_flag_set,
+ env_set = out_env_set,
+ )
+
+def _to_untyped_flag_sets(flag_sets, known, fail):
+ out_flag_sets = []
+ out_env_sets = []
+ out = [_to_untyped_flag_set(flag_set, known, fail) for flag_set in flag_sets]
+ for entry in out:
+ if entry.flag_set != None:
+ out_flag_sets.append(entry.flag_set)
+ if entry.env_set != None:
+ out_env_sets.append(entry.env_set)
+ return struct(flag_sets = out_flag_sets, env_sets = out_env_sets)
+
+def _to_untyped_feature_set(feature_set):
+ return rules_cc_feature_set([
+ feature.name
+ for feature in feature_set.features.to_list()
+ ])
+
+def _to_untyped_feature_constraint(feature_constraint):
+ return rules_cc_with_feature_set(
+ features = [ft.name for ft in feature_constraint.all_of.to_list()],
+ not_features = [ft.name for ft in feature_constraint.none_of.to_list()],
+ )
+
+def _to_untyped_implies(provider, known, fail = fail):
+ implies = []
+ for feature in provider.implies_features.to_list():
+ if feature.label not in known:
+ fail("%s implies %s, which is not explicitly mentioned in your toolchain configuration" % (provider.label, feature.label))
+ implies.append(feature.name)
+ for action_config in provider.implies_action_configs.to_list():
+ if action_config.label not in known:
+ fail("%s implies %s, which is not explicitly mentioned in your toolchain configuration" % (provider.label, action_config.label))
+ implies.append(action_config.action_name)
+ return implies
+
+def _to_untyped_feature(feature, known, fail = fail):
+ if feature.known:
+ return None
+
+ _ensure_fulfillable(
+ any_of = [fs.features for fs in feature.requires_any_of],
+ known = known,
+ label = feature.label,
+ fail = fail,
+ )
+
+ flags = _to_untyped_flag_sets(feature.flag_sets.to_list(), known, fail = fail)
+
+ return rules_cc_feature(
+ name = feature.name,
+ enabled = feature.enabled,
+ flag_sets = flags.flag_sets,
+ env_sets = flags.env_sets,
+ implies = _to_untyped_implies(feature, known, fail = fail),
+ requires = [_to_untyped_feature_set(requirement) for requirement in feature.requires_any_of],
+ provides = list(feature.provides),
+ )
+
+def _to_untyped_tool(tool, known, fail = fail):
+ _ensure_fulfillable(
+ any_of = [constraint.all_of for constraint in tool.requires_any_of],
+ known = known,
+ label = tool.label,
+ fail = fail,
+ )
+
+ # Rules_cc is missing the "tool" parameter.
+ return struct(
+ path = tool.path,
+ tool = tool.exe,
+ execution_requirements = list(tool.execution_requirements),
+ with_features = [
+ _to_untyped_feature_constraint(fc)
+ for fc in tool.requires_any_of
+ ],
+ type_name = "tool",
+ )
+
+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()
+ untyped_flags = _to_untyped_flag_sets(flag_sets, known = known, fail = fail)
+ implies = _to_untyped_implies(action_config, known, fail = fail)
+
+ # Action configs don't take in an env like they do a flag set.
+ # In order to support them, we create a feature with the env that the action
+ # config will enable, and imply it in the action config.
+ feature = None
+ if untyped_flags.env_sets:
+ feature = rules_cc_feature(
+ name = "implied_by_%s" % action_config.action_name,
+ env_sets = untyped_flags.env_sets,
)
+ implies.append(feature.name)
+
+ return struct(
+ action_config = rules_cc_action_config(
+ action_name = action_config.action_name,
+ enabled = action_config.enabled,
+ tools = [
+ _to_untyped_tool(tool, known, fail = fail)
+ for tool in action_config.tools
+ ],
+ flag_sets = [
+ # Make the flag sets actionless.
+ rules_cc_flag_set(
+ actions = [],
+ with_features = fs.with_features,
+ flag_groups = fs.flag_groups,
+ )
+ for fs in untyped_flags.flag_sets
+ ],
+ implies = implies,
+ ),
+ features = [feature] if feature else [],
+ )
+
+def to_untyped_config(feature_set, action_config_set, flag_sets, extra_action_files, 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
+ extra_action_files: Files to be added to actions
+ 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()
+ for feature in feature_list:
+ known_labels[feature.label] = None
+ existing_feature = known_feature_names.get(feature.name, None)
+ if existing_feature != None and feature.overrides != existing_feature and existing_feature.overrides != feature:
+ fail("Conflicting features: %s and %s both have feature name %s" % (feature.label, existing_feature.label, feature.name))
+
+ known_feature_names[feature.name] = feature
+
+ untyped_features = []
+ for feature in feature_list:
+ 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
+ out_ac = _to_untyped_action_config(
+ ac,
+ extra_flag_sets = flag_sets_by_action.get(ac.action_name, []),
+ known = known_labels,
+ fail = fail,
+ )
+ untyped_acs.append(out_ac.action_config)
+ untyped_features.extend(out_ac.features)
+
+ action_to_files = {
+ ac.action_name: [ac.files]
+ for ac in acs
+ }
+ for ffa in extra_action_files.srcs.to_list():
+ action_to_files.setdefault(ffa.action, []).append(ffa.files)
-def check_deps(ctx):
- for dep in ctx.attr.feature_deps:
- _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")
+ return struct(
+ features = untyped_features,
+ action_configs = untyped_acs,
+ action_to_files = {k: depset(transitive = v) for k, v in action_to_files.items()},
+ )