Properly support target-specific capabilities

Changes supports_pic and supports_dynamic_linker features to
cc_target_capability rules since the capability is tied to target
platform capabilities rather than tool capabilities.
This commit is contained in:
Armando Montanez 2024-11-07 11:40:14 -08:00
parent 5a91c52992
commit b8045d5626
9 changed files with 153 additions and 19 deletions

View File

@ -1,3 +1,4 @@
load("//cc/toolchains:target_capability.bzl", "cc_target_capability")
load("//cc/toolchains:tool_capability.bzl", "cc_tool_capability") load("//cc/toolchains:tool_capability.bzl", "cc_tool_capability")
package(default_visibility = ["//visibility:public"]) package(default_visibility = ["//visibility:public"])
@ -10,10 +11,10 @@ cc_tool_capability(
name = "supports_interface_shared_libraries", name = "supports_interface_shared_libraries",
) )
cc_tool_capability( cc_target_capability(
name = "supports_dynamic_linker", name = "supports_dynamic_linker",
) )
cc_tool_capability( cc_target_capability(
name = "supports_pic", name = "supports_pic",
) )

View File

@ -119,6 +119,11 @@ FeatureInfo = provider(
"allowlist_include_directories": "(depset[DirectoryInfo]) Include directories implied by this feature that should be allowlisted in Bazel's include checker", "allowlist_include_directories": "(depset[DirectoryInfo]) Include directories implied by this feature that should be allowlisted in Bazel's include checker",
}, },
) )
FeatureImplyabilityInfo = provider(
doc = "Provider used to constrain which kinds of features can be implied",
)
FeatureSetInfo = provider( FeatureSetInfo = provider(
doc = "A set of features", doc = "A set of features",
# @unsorted-dict-items # @unsorted-dict-items
@ -161,7 +166,16 @@ ToolInfo = provider(
) )
ToolCapabilityInfo = provider( ToolCapabilityInfo = provider(
doc = "A capability associated with a tool (eg. supports_pic).", doc = "A capability associated with a tool (eg. supports_start_end_lib).",
# @unsorted-dict-items
fields = {
"label": "(Label) The label defining this provider. Place in error messages to simplify debugging",
"feature": "(FeatureInfo) The feature this capability defines",
},
)
TargetCapabilityInfo = provider(
doc = "A capability associated with a target (eg. supports_pic).",
# @unsorted-dict-items # @unsorted-dict-items
fields = { fields = {
"label": "(Label) The label defining this provider. Place in error messages to simplify debugging", "label": "(Label) The label defining this provider. Place in error messages to simplify debugging",

View File

@ -23,6 +23,7 @@ load(
":cc_toolchain_info.bzl", ":cc_toolchain_info.bzl",
"ArgsListInfo", "ArgsListInfo",
"FeatureConstraintInfo", "FeatureConstraintInfo",
"FeatureImplyabilityInfo",
"FeatureInfo", "FeatureInfo",
"FeatureSetInfo", "FeatureSetInfo",
"MutuallyExclusiveCategoryInfo", "MutuallyExclusiveCategoryInfo",
@ -87,6 +88,7 @@ def _cc_feature_impl(ctx):
return [ return [
feature, feature,
FeatureImplyabilityInfo(),
FeatureSetInfo(label = ctx.label, features = depset([feature])), FeatureSetInfo(label = ctx.label, features = depset([feature])),
FeatureConstraintInfo( FeatureConstraintInfo(
label = ctx.label, label = ctx.label,
@ -144,7 +146,7 @@ be enabled.
providers = [FeatureSetInfo], providers = [FeatureSetInfo],
), ),
"implies": attr.label_list( "implies": attr.label_list(
providers = [FeatureSetInfo], providers = [FeatureSetInfo, FeatureImplyabilityInfo],
doc = """List of features enabled along with this feature. doc = """List of features enabled along with this feature.
Warning: If any of the features cannot be enabled, this feature is Warning: If any of the features cannot be enabled, this feature is
@ -186,6 +188,7 @@ cc_feature(
), ),
}, },
provides = [ provides = [
FeatureImplyabilityInfo,
FeatureInfo, FeatureInfo,
FeatureSetInfo, FeatureSetInfo,
FeatureConstraintInfo, FeatureConstraintInfo,

View File

@ -21,6 +21,7 @@ load("//cc/toolchains:feature_constraint.bzl", _cc_feature_constraint = "cc_feat
load("//cc/toolchains:feature_set.bzl", _cc_feature_set = "cc_feature_set") load("//cc/toolchains:feature_set.bzl", _cc_feature_set = "cc_feature_set")
load("//cc/toolchains:mutually_exclusive_category.bzl", _cc_mutually_exclusive_category = "cc_mutually_exclusive_category") load("//cc/toolchains:mutually_exclusive_category.bzl", _cc_mutually_exclusive_category = "cc_mutually_exclusive_category")
load("//cc/toolchains:nested_args.bzl", _cc_nested_args = "cc_nested_args") load("//cc/toolchains:nested_args.bzl", _cc_nested_args = "cc_nested_args")
load("//cc/toolchains:target_capability.bzl", _cc_target_capability = "cc_target_capability")
load("//cc/toolchains:tool.bzl", _cc_tool = "cc_tool") load("//cc/toolchains:tool.bzl", _cc_tool = "cc_tool")
load("//cc/toolchains:tool_capability.bzl", _cc_tool_capability = "cc_tool_capability") load("//cc/toolchains:tool_capability.bzl", _cc_tool_capability = "cc_tool_capability")
load("//cc/toolchains:tool_map.bzl", _cc_tool_map = "cc_tool_map") load("//cc/toolchains:tool_map.bzl", _cc_tool_map = "cc_tool_map")
@ -28,6 +29,7 @@ load("//cc/toolchains:toolchain.bzl", _cc_toolchain = "cc_toolchain")
load("//cc/toolchains/impl:external_feature.bzl", _cc_external_feature = "cc_external_feature") load("//cc/toolchains/impl:external_feature.bzl", _cc_external_feature = "cc_external_feature")
load("//cc/toolchains/impl:variables.bzl", _cc_variable = "cc_variable") load("//cc/toolchains/impl:variables.bzl", _cc_variable = "cc_variable")
cc_target_capability = _cc_target_capability
cc_tool_map = _cc_tool_map cc_tool_map = _cc_tool_map
cc_tool = _cc_tool cc_tool = _cc_tool
cc_tool_capability = _cc_tool_capability cc_tool_capability = _cc_tool_capability
@ -48,6 +50,7 @@ cc_toolchain = _cc_toolchain
# links in the generated documentation so that maintainers don't need to manually # links in the generated documentation so that maintainers don't need to manually
# ensure every reference to a rule is properly linked. # ensure every reference to a rule is properly linked.
DOCUMENTED_TOOLCHAIN_RULES = [ DOCUMENTED_TOOLCHAIN_RULES = [
"cc_target_capability",
"cc_tool_map", "cc_tool_map",
"cc_tool", "cc_tool",
"cc_tool_capability", "cc_tool_capability",

View File

@ -0,0 +1,92 @@
# Copyright 2024 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Implementation of the cc_target_capability rule."""
load(
":cc_toolchain_info.bzl",
"ArgsListInfo",
"FeatureConstraintInfo",
"FeatureInfo",
"FeatureSetInfo",
"TargetCapabilityInfo",
)
def _cc_target_capability_impl(ctx):
ft = FeatureInfo(
name = ctx.attr.feature_name or ctx.label.name,
label = ctx.label,
enabled = False,
args = ArgsListInfo(
label = ctx.label,
args = (),
files = depset(),
by_action = (),
allowlist_include_directories = depset(),
),
implies = depset(),
requires_any_of = (),
mutually_exclusive = (),
external = False,
overridable = True,
overrides = None,
allowlist_include_directories = depset(),
)
# Intentionally does not provide FeatureImplyabilityInfo to prevent
# features from implying these kinds of rules.
return [
ft,
FeatureSetInfo(label = ctx.label, features = depset([ft])),
TargetCapabilityInfo(label = ctx.label, feature = ft),
FeatureConstraintInfo(label = ctx.label, all_of = depset([ft])),
]
cc_target_capability = rule(
implementation = _cc_target_capability_impl,
provides = [TargetCapabilityInfo, FeatureSetInfo, FeatureConstraintInfo],
doc = """A target capability is an optional feature that a target platform supports.
For example, not all target platforms have dynamic loaders (e.g. microcontroller
firmware), so a toolchain may conditionally enable the capabilty to communicate
the capability to C/C++ rule implementations.
```
load("//cc/toolchains:toolchain.bzl", "cc_toolchain")
cc_toolchain(
name = "universal_cc_toolchain",
# Assume no operating system means no dynamic loader support.
enabled_features = select({
"@platforms//os:none": [],
"//conditions:default": [
"//cc/toolchains/capabilities:supports_dynamic_linker",
],
}),
# ...
)
```
`cc_target_capability` rules cannot be listed in a
[`cc_feature.implies`](#cc_feature-implies) list.
Note: User-defined capabilities should prefer traditional
[user-defined build settings](https://bazel.build/extending/config#user-defined-build-settings).
This construct exists to communicate these features to preexisting C/C++ rule
implementations that expect these options to be exposed as
[features](https://bazel.build/docs/cc-toolchain-config-reference#features).
""",
attrs = {
"feature_name": attr.string(doc = "The name of the feature to generate for this capability"),
},
)

View File

@ -44,10 +44,11 @@ def _cc_tool_capability_impl(ctx):
overrides = None, overrides = None,
allowlist_include_directories = depset(), allowlist_include_directories = depset(),
) )
# Intentionally does not provide FeatureImplyabilityInfo to prevent
# features from implying these kinds of rules.
return [ return [
ToolCapabilityInfo(label = ctx.label, feature = ft), ToolCapabilityInfo(label = ctx.label, feature = ft),
# Only give it a feature constraint info and not a feature info.
# This way you can't imply it - you can only require it.
FeatureConstraintInfo(label = ctx.label, all_of = depset([ft])), FeatureConstraintInfo(label = ctx.label, all_of = depset([ft])),
] ]
@ -56,28 +57,41 @@ cc_tool_capability = rule(
provides = [ToolCapabilityInfo, FeatureConstraintInfo], provides = [ToolCapabilityInfo, FeatureConstraintInfo],
doc = """A capability is an optional feature that a tool supports. doc = """A capability is an optional feature that a tool supports.
For example, not all compilers support PIC, so to handle this, we write: For example, not all linkers support --start-lib, so to handle this, we write:
``` ```
load("//cc/toolchains:args.bzl", "cc_args")
load("//cc/toolchains:tool.bzl", "cc_tool")
cc_tool( cc_tool(
name = "clang", name = "clang",
src = "@host_tools/bin/clang", src = "@host_tools/bin/clang",
capabilities = [ capabilities = [
"//cc/toolchains/capabilities:supports_pic", "//cc/toolchains/capabilities:supports_start_end_lib",
], ],
) )
cc_args( cc_args(
name = "pic", name = "pic",
requires = [ requires = ["//cc/toolchains/capabilities:supports_start_end_lib"],
"//cc/toolchains/capabilities:supports_pic" args = ["-Wl,--start-lib"],
],
args = ["-fPIC"],
) )
``` ```
This ensures that `-fPIC` is added to the command-line only when we are using a This ensures that `-Wl,--start-lib` is added to the command-line only when using
tool that supports PIC. a tool that supports the argument.
`cc_target_capability` rules cannot be listed in a
[`cc_feature.implies`](#cc_feature-implies) list, or in
[`cc_toolchain.enabled_features`](#cc_toolchain-enabled_features).
Note: Because [`cc_tool`](#cc_tool) rules are always evaluated under the exec
configuration, a `select()` to guide capabilities will `select()` on the
properties of the exec configuration. If you need a capability that is
conditionally guided by the target configuration, prefer using configurabilty
constructs to enable the feature at the
[`cc_toolchain.enabled_features`](#cc_toolchain-enabled_features) level.
""", """,
attrs = { attrs = {
"feature_name": attr.string(doc = "The name of the feature to generate for this capability"), "feature_name": attr.string(doc = "The name of the feature to generate for this capability"),

View File

@ -6,7 +6,7 @@ load(":tool_test.bzl", "TARGETS", "TESTS")
cc_tool( cc_tool(
name = "tool", name = "tool",
src = "//tests/rule_based_toolchain/testdata:bin_wrapper.sh", src = "//tests/rule_based_toolchain/testdata:bin_wrapper.sh",
capabilities = ["//cc/toolchains/capabilities:supports_pic"], capabilities = ["//cc/toolchains/capabilities:supports_start_end_lib"],
data = ["//tests/rule_based_toolchain/testdata:bin"], data = ["//tests/rule_based_toolchain/testdata:bin"],
tags = ["requires-network"], tags = ["requires-network"],
) )

View File

@ -47,7 +47,7 @@ cc_tool(
name = "c_compile_tool", name = "c_compile_tool",
src = "//tests/rule_based_toolchain/testdata:bin_wrapper", src = "//tests/rule_based_toolchain/testdata:bin_wrapper",
allowlist_include_directories = ["//tests/rule_based_toolchain/testdata:subdirectory_3"], allowlist_include_directories = ["//tests/rule_based_toolchain/testdata:subdirectory_3"],
capabilities = ["//cc/toolchains/capabilities:supports_pic"], capabilities = ["//cc/toolchains/capabilities:supports_start_end_lib"],
) )
cc_sysroot( cc_sysroot(
@ -69,7 +69,10 @@ util.helper_target(
":sysroot", ":sysroot",
":c_compile_args", ":c_compile_args",
], ],
enabled_features = [":simple_feature"], enabled_features = [
":simple_feature",
"//cc/toolchains/capabilities:supports_pic",
],
known_features = [":compile_feature"], known_features = [":compile_feature"],
tool_map = ":compile_tool_map", tool_map = ":compile_tool_map",
) )

View File

@ -197,6 +197,10 @@ def _toolchain_collects_files_test(env, targets):
], ],
)], )],
), ),
legacy_feature(
name = "supports_pic",
enabled = True,
),
legacy_feature( legacy_feature(
name = "compile_feature", name = "compile_feature",
enabled = False, enabled = False,
@ -208,7 +212,7 @@ def _toolchain_collects_files_test(env, targets):
)], )],
), ),
legacy_feature( legacy_feature(
name = "supports_pic", name = "supports_start_end_lib",
enabled = False, enabled = False,
), ),
legacy_feature( legacy_feature(
@ -225,7 +229,7 @@ def _toolchain_collects_files_test(env, targets):
action_name = "c_compile", action_name = "c_compile",
enabled = True, enabled = True,
tools = [legacy_tool(tool = exe)], tools = [legacy_tool(tool = exe)],
implies = ["supports_pic"], implies = ["supports_start_end_lib"],
flag_sets = [ flag_sets = [
legacy_flag_set( legacy_flag_set(
flag_groups = [ flag_groups = [