From db087578f7b7dc633cb9c8cae258d0bfb3775e08 Mon Sep 17 00:00:00 2001 From: Googler Date: Tue, 27 Feb 2024 04:48:53 -0800 Subject: [PATCH] BEGIN_PUBLIC Implement cc_feature_set and cc_feature_constraint. END_PUBLIC PiperOrigin-RevId: 610713183 Change-Id: Ia009ac536b71cd9aa44578f823f13361c1580e37 --- cc/toolchains/feature_constraint.bzl | 54 ++++++++++++++++++ cc/toolchains/feature_set.bzl | 57 +++++++++++++++++++ cc/toolchains/impl/features_attr.bzl | 29 ++++++++++ tests/rule_based_toolchain/features/BUILD | 38 ++++++++++++- .../features/features_test.bzl | 39 ++++++++++++- tests/rule_based_toolchain/subjects.bzl | 2 +- 6 files changed, 216 insertions(+), 3 deletions(-) create mode 100644 cc/toolchains/feature_constraint.bzl create mode 100644 cc/toolchains/feature_set.bzl create mode 100644 cc/toolchains/impl/features_attr.bzl diff --git a/cc/toolchains/feature_constraint.bzl b/cc/toolchains/feature_constraint.bzl new file mode 100644 index 0000000..b02d420 --- /dev/null +++ b/cc/toolchains/feature_constraint.bzl @@ -0,0 +1,54 @@ +# 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_feature_constraint rule.""" + +load( + "//cc/toolchains/impl:collect.bzl", + "collect_features", + "collect_provider", +) +load("//cc/toolchains/impl:features_attr.bzl", "disallow_features_attr") +load( + ":cc_toolchain_info.bzl", + "FeatureConstraintInfo", + "FeatureSetInfo", +) + +def _cc_feature_constraint_impl(ctx): + all_of = collect_provider(ctx.attr.all_of, FeatureConstraintInfo) + none_of = [collect_features(ctx.attr.none_of)] + none_of.extend([fc.none_of for fc in all_of]) + return [FeatureConstraintInfo( + label = ctx.label, + all_of = depset(transitive = [fc.all_of for fc in all_of]), + none_of = depset(transitive = none_of), + )] + +_cc_feature_constraint = rule( + implementation = _cc_feature_constraint_impl, + attrs = { + "all_of": attr.label_list( + providers = [FeatureConstraintInfo], + ), + "none_of": attr.label_list( + providers = [FeatureSetInfo], + ), + }, + provides = [FeatureConstraintInfo], + doc = """Defines a constraint on features. + +Can be used with require_any_of to specify that something is only enabled when +a constraint is met.""", +) +cc_feature_constraint = disallow_features_attr(_cc_feature_constraint) diff --git a/cc/toolchains/feature_set.bzl b/cc/toolchains/feature_set.bzl new file mode 100644 index 0000000..369de9c --- /dev/null +++ b/cc/toolchains/feature_set.bzl @@ -0,0 +1,57 @@ +# 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_feature_set rule.""" + +load("//cc/toolchains/impl:collect.bzl", "collect_features") +load("//cc/toolchains/impl:features_attr.bzl", "require_features_attr") +load( + ":cc_toolchain_info.bzl", + "FeatureConstraintInfo", + "FeatureSetInfo", +) + +def _cc_feature_set_impl(ctx): + features = collect_features(ctx.attr.features_) + return [ + FeatureSetInfo(label = ctx.label, features = features), + FeatureConstraintInfo( + label = ctx.label, + all_of = features, + none_of = depset([]), + ), + ] + +_cc_feature_set = rule( + implementation = _cc_feature_set_impl, + attrs = { + "features_": attr.label_list( + providers = [FeatureSetInfo], + doc = "A set of features", + ), + }, + provides = [FeatureSetInfo], + doc = """Defines a set of features. + +Example: + + cc_feature_set( + name = "thin_lto_requirements", + all_of = [ + ":thin_lto", + ":opt", + ], + ) +""", +) +cc_feature_set = require_features_attr(_cc_feature_set) diff --git a/cc/toolchains/impl/features_attr.bzl b/cc/toolchains/impl/features_attr.bzl new file mode 100644 index 0000000..ab8d83a --- /dev/null +++ b/cc/toolchains/impl/features_attr.bzl @@ -0,0 +1,29 @@ +# 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. +"""Helpers for dealing with the fact that features is a reserved attribute.""" + +# buildifier: disable=unnamed-macro +def disallow_features_attr(rule): + def rule_wrapper(*, name, **kwargs): + if "features" in kwargs: + fail("Cannot use features in %s" % native.package_relative_label(name)) + rule(name = name, **kwargs) + + return rule_wrapper + +def require_features_attr(rule): + def rule_wrapper(*, name, features, **kwargs): + rule(name = name, features_ = features, **kwargs) + + return rule_wrapper diff --git a/tests/rule_based_toolchain/features/BUILD b/tests/rule_based_toolchain/features/BUILD index 0d9f5cd..701d478 100644 --- a/tests/rule_based_toolchain/features/BUILD +++ b/tests/rule_based_toolchain/features/BUILD @@ -1,6 +1,8 @@ load("@rules_testing//lib:util.bzl", "util") load("//cc/toolchains:args.bzl", "cc_args") load("//cc/toolchains:feature.bzl", "cc_feature") +load("//cc/toolchains:feature_constraint.bzl", "cc_feature_constraint") +load("//cc/toolchains:feature_set.bzl", "cc_feature_set") load("//tests/rule_based_toolchain:analysis_test_suite.bzl", "analysis_test_suite") load(":features_test.bzl", "TARGETS", "TESTS") @@ -21,13 +23,30 @@ util.helper_target( visibility = ["//tests/rule_based_toolchain:__subpackages__"], ) +util.helper_target( + cc_feature, + name = "simple2", + args = [":c_compile"], + enabled = False, + feature_name = "simple2", +) + +util.helper_target( + cc_feature_set, + name = "feature_set", + features = [ + ":simple", + ":simple2", + ], +) + util.helper_target( cc_feature, name = "requires", args = [":c_compile"], enabled = True, feature_name = "requires", - requires_any_of = [":simple"], + requires_any_of = [":feature_set"], ) util.helper_target( @@ -48,6 +67,23 @@ util.helper_target( mutually_exclusive = [":simple"], ) +util.helper_target( + cc_feature_constraint, + name = "direct_constraint", + all_of = [":simple"], + none_of = [":simple2"], +) + +util.helper_target( + cc_feature_constraint, + name = "transitive_constraint", + all_of = [ + ":direct_constraint", + ":requires", + ], + none_of = [":implies"], +) + analysis_test_suite( name = "test_suite", targets = TARGETS, diff --git a/tests/rule_based_toolchain/features/features_test.bzl b/tests/rule_based_toolchain/features/features_test.bzl index 75ae7ee..b6a7db6 100644 --- a/tests/rule_based_toolchain/features/features_test.bzl +++ b/tests/rule_based_toolchain/features/features_test.bzl @@ -16,7 +16,9 @@ load( "//cc/toolchains:cc_toolchain_info.bzl", "ArgsInfo", + "FeatureConstraintInfo", "FeatureInfo", + "FeatureSetInfo", "MutuallyExclusiveCategoryInfo", ) @@ -43,7 +45,7 @@ def _feature_collects_requirements_test(env, targets): env.expect.that_target(targets.requires).provider( FeatureInfo, ).requires_any_of().contains_exactly([ - targets.simple.label, + targets.feature_set.label, ]) def _feature_collects_implies_test(env, targets): @@ -63,12 +65,44 @@ def _feature_collects_mutual_exclusion_test(env, targets): targets.simple.label, ]) +def _feature_set_collects_features_test(env, targets): + env.expect.that_target(targets.feature_set).provider( + FeatureSetInfo, + ).features().contains_exactly([ + targets.simple.label, + targets.simple2.label, + ]) + +def _feature_constraint_collects_direct_features_test(env, targets): + constraint = env.expect.that_target(targets.direct_constraint).provider( + FeatureConstraintInfo, + ) + constraint.all_of().contains_exactly([targets.simple.label]) + constraint.none_of().contains_exactly([targets.simple2.label]) + +def _feature_constraint_collects_transitive_features_test(env, targets): + constraint = env.expect.that_target(targets.transitive_constraint).provider( + FeatureConstraintInfo, + ) + constraint.all_of().contains_exactly([ + targets.simple.label, + targets.requires.label, + ]) + constraint.none_of().contains_exactly([ + targets.simple2.label, + targets.implies.label, + ]) + TARGETS = [ ":c_compile", ":simple", + ":simple2", + ":feature_set", ":requires", ":implies", ":mutual_exclusion_feature", + ":direct_constraint", + ":transitive_constraint", ] # @unsorted-dict-items @@ -77,4 +111,7 @@ TESTS = { "feature_collects_requirements_test": _feature_collects_requirements_test, "feature_collects_implies_test": _feature_collects_implies_test, "feature_collects_mutual_exclusion_test": _feature_collects_mutual_exclusion_test, + "feature_set_collects_features_test": _feature_set_collects_features_test, + "feature_constraint_collects_direct_features_test": _feature_constraint_collects_direct_features_test, + "feature_constraint_collects_transitive_features_test": _feature_constraint_collects_transitive_features_test, } diff --git a/tests/rule_based_toolchain/subjects.bzl b/tests/rule_based_toolchain/subjects.bzl index 23f5ec7..2525887 100644 --- a/tests/rule_based_toolchain/subjects.bzl +++ b/tests/rule_based_toolchain/subjects.bzl @@ -88,7 +88,7 @@ _FakeFeatureFactory = generate_factory( _FeatureSetFactory = generate_factory( FeatureSetInfo, "FeatureSetInfo", - dict(features = _FakeFeatureFactory), + dict(features = ProviderDepset(_FakeFeatureFactory)), ) # buildifier: disable=name-conventions