1# Copyright 2024 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""A rule for letting enabled toolchain features drive build configuration."""
15
16load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
17load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain", "use_cpp_toolchain")
18
19def _cc_toolchain_feature_is_enabled_impl(ctx):
20    toolchain = find_cpp_toolchain(ctx)
21    feature_configuration = cc_common.configure_features(
22        ctx = ctx,
23        cc_toolchain = toolchain,
24    )
25    val = cc_common.is_enabled(
26        feature_configuration = feature_configuration,
27        feature_name = ctx.attr.feature_name,
28    )
29    return [
30        config_common.FeatureFlagInfo(value = str(val)),
31        BuildSettingInfo(value = val),
32    ]
33
34_cc_toolchain_feature_is_enabled = rule(
35    implementation = _cc_toolchain_feature_is_enabled_impl,
36    attrs = {
37        "feature_name": attr.string(
38            mandatory = True,
39            doc = "The feature name to match against",
40        ),
41        "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")),
42    },
43    doc = """Extracts a matching, enabled feature from the current C/C++ toolchain.
44
45This rule is a bridge that allows feature presence/absence to be communicated
46from a toolchain configuration as if it was just a `bool_flag`.
47
48This allows toolchain features to become branches in `select()` statements:
49
50```py
51_cc_toolchain_feature_is_enabled(
52    name = "toolchain_flavor_chocolate",
53    feature_name = "toolchain_flavor_chocolate",
54)
55
56config_setting(
57    name = "chocolate",
58    flag_values = {":toolchain_flavor_chocolate": "True"},
59)
60
61cc_library(
62    name = "libfoo",
63    copts = select({
64        ":chocolate": ["-fno-dog-food"],
65        "//conditions:default": [],
66    }),
67    srcs = ["foo.cc"],
68)
69```
70""",
71    toolchains = use_cpp_toolchain(),
72    fragments = ["cpp"],
73)
74
75def pw_cc_toolchain_feature_is_enabled(*, name, feature_name, **kwargs):
76    _cc_toolchain_query_name = name + ".value"
77    _cc_toolchain_feature_is_enabled(
78        name = _cc_toolchain_query_name,
79        feature_name = feature_name,
80        **kwargs
81    )
82
83    native.config_setting(
84        name = name,
85        flag_values = {":{}".format(_cc_toolchain_query_name): "True"},
86    )
87