mirror of https://github.com/bazelbuild/rules_cc
BEGIN_PUBLIC
Add support for testing rules_cc's new toolchains with rules_testing. END_PUBLIC PiperOrigin-RevId: 608769646 Change-Id: I1a698355e5e977cc86eedc7cf6e8e0f888593cb8
This commit is contained in:
parent
833f17060c
commit
2e780ceda9
|
@ -12,3 +12,4 @@ use_repo(cc_configure, "local_config_cc_toolchains")
|
|||
register_toolchains("@local_config_cc_toolchains//:all")
|
||||
|
||||
bazel_dep(name = "bazel_skylib", version = "1.3.0", dev_dependency = True)
|
||||
bazel_dep(name = "rules_testing", version = "0.6.0", dev_dependency = True)
|
||||
|
|
|
@ -90,3 +90,10 @@ load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_
|
|||
rules_proto_dependencies()
|
||||
|
||||
rules_proto_toolchains()
|
||||
|
||||
http_archive(
|
||||
name = "rules_testing",
|
||||
sha256 = "02c62574631876a4e3b02a1820cb51167bb9cdcdea2381b2fa9d9b8b11c407c4",
|
||||
strip_prefix = "rules_testing-0.6.0",
|
||||
url = "https://github.com/bazelbuild/rules_testing/releases/download/v0.6.0/rules_testing-v0.6.0.tar.gz",
|
||||
)
|
||||
|
|
|
@ -17,7 +17,10 @@
|
|||
# that can access the providers directly.
|
||||
# Once it's stabilized, we *may* consider opening up parts of the API, or we may
|
||||
# decide to just require users to use the public user-facing rules.
|
||||
visibility("//third_party/bazel_rules/rules_cc/toolchains/...")
|
||||
visibility([
|
||||
"//cc/toolchains/...",
|
||||
"//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
|
||||
|
@ -120,12 +123,11 @@ ActionConfigInfo = provider(
|
|||
# @unsorted-dict-items
|
||||
fields = {
|
||||
"label": "(Label) The label defining this provider. Place in error messages to simplify debugging",
|
||||
"action_name": "(str) The name of the action",
|
||||
"action": "(ActionTypeInfo) 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": "(depset[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",
|
||||
"flag_sets": "(Sequence[FlagSetInfo]) Set of flag sets the action sets",
|
||||
"implies": "(depset[FeatureInfo]) Set of features implied by this action config",
|
||||
"files": "(depset[File]) The files required to run these actions",
|
||||
},
|
||||
)
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
load("@rules_testing//lib:util.bzl", "util")
|
||||
load("//cc/toolchains:actions.bzl", "cc_action_type", "cc_action_type_set")
|
||||
load("//tests/rule_based_toolchain:analysis_test_suite.bzl", "analysis_test_suite")
|
||||
load(":actions_test.bzl", "TARGETS", "TESTS")
|
||||
|
||||
util.helper_target(
|
||||
cc_action_type,
|
||||
name = "c_compile",
|
||||
action_name = "c_compile",
|
||||
visibility = ["//tests/rule_based_toolchain:__subpackages__"],
|
||||
)
|
||||
|
||||
util.helper_target(
|
||||
cc_action_type,
|
||||
name = "cpp_compile",
|
||||
action_name = "cpp_compile",
|
||||
visibility = ["//tests/rule_based_toolchain:__subpackages__"],
|
||||
)
|
||||
|
||||
util.helper_target(
|
||||
cc_action_type_set,
|
||||
name = "all_compile",
|
||||
actions = [
|
||||
":c_compile",
|
||||
":cpp_compile",
|
||||
],
|
||||
visibility = ["//tests/rule_based_toolchain:__subpackages__"],
|
||||
)
|
||||
|
||||
analysis_test_suite(
|
||||
name = "test_suite",
|
||||
targets = TARGETS,
|
||||
tests = TESTS,
|
||||
)
|
|
@ -0,0 +1,43 @@
|
|||
# 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.
|
||||
"""Tests for actions for the rule based toolchain."""
|
||||
|
||||
load(
|
||||
"//cc/toolchains:cc_toolchain_info.bzl",
|
||||
"ActionTypeInfo",
|
||||
"ActionTypeSetInfo",
|
||||
)
|
||||
|
||||
visibility("private")
|
||||
|
||||
def _test_action_types_impl(env, targets):
|
||||
env.expect.that_target(targets.c_compile).provider(ActionTypeInfo) \
|
||||
.name().equals("c_compile")
|
||||
env.expect.that_target(targets.cpp_compile).provider(ActionTypeSetInfo) \
|
||||
.actions().contains_exactly([targets.cpp_compile.label])
|
||||
env.expect.that_target(targets.all_compile).provider(ActionTypeSetInfo) \
|
||||
.actions().contains_exactly([
|
||||
targets.c_compile.label,
|
||||
targets.cpp_compile.label,
|
||||
])
|
||||
|
||||
TARGETS = [
|
||||
":c_compile",
|
||||
":cpp_compile",
|
||||
":all_compile",
|
||||
]
|
||||
|
||||
TESTS = {
|
||||
"actions_test": _test_action_types_impl,
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
# 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.
|
||||
"""Test suites for the rule based toolchain."""
|
||||
|
||||
load("@rules_testing//lib:analysis_test.bzl", "analysis_test")
|
||||
load(":subjects.bzl", "FACTORIES")
|
||||
|
||||
visibility("//tests/rule_based_toolchain/...")
|
||||
|
||||
_DEFAULT_TARGET = "//tests/rule_based_toolchain/actions:c_compile"
|
||||
|
||||
# Tests of internal starlark functions will often not require any targets,
|
||||
# but analysis_test requires at least one, so we pick an arbitrary one.
|
||||
def analysis_test_suite(name, tests, targets = [_DEFAULT_TARGET]):
|
||||
"""A test suite for the internals of the toolchain.
|
||||
|
||||
Args:
|
||||
name: (str) The name of the test suite.
|
||||
tests: (dict[str, fn]) A mapping from test name to implementations.
|
||||
targets: (List[Label|str]) List of targets accessible to the test.
|
||||
"""
|
||||
targets = [native.package_relative_label(target) for target in targets]
|
||||
|
||||
test_case_names = []
|
||||
for test_name, impl in tests.items():
|
||||
if not test_name.endswith("_test"):
|
||||
fail("Expected test keys to end with '_test', got test case %r" % test_name)
|
||||
test_case_names.append(":" + test_name)
|
||||
analysis_test(
|
||||
name = test_name,
|
||||
impl = impl,
|
||||
provider_subject_factories = FACTORIES,
|
||||
targets = {label.name: label for label in targets},
|
||||
)
|
||||
|
||||
native.test_suite(
|
||||
name = name,
|
||||
tests = test_case_names,
|
||||
)
|
|
@ -0,0 +1,127 @@
|
|||
# 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.
|
||||
"""Generates provider factories."""
|
||||
|
||||
load("@bazel_skylib//lib:structs.bzl", "structs")
|
||||
load("@rules_testing//lib:truth.bzl", "subjects")
|
||||
|
||||
visibility("private")
|
||||
|
||||
def generate_factory(type, name, attrs):
|
||||
"""Generates a factory for a custom struct.
|
||||
|
||||
There are three reasons we need to do so:
|
||||
1. It's very difficult to read providers printed by these types.
|
||||
eg. If you have a 10 layer deep diamond dependency graph, and try to
|
||||
print the top value, the bottom value will be printed 2^10 times.
|
||||
2. Collections of subjects are not well supported by rules_testing
|
||||
eg. `FeatureInfo(flag_sets = [FlagSetInfo(...)])`
|
||||
(You can do it, but the inner values are just regular bazel structs and
|
||||
you can't do fluent assertions on them).
|
||||
3. Recursive types are not supported at all
|
||||
eg. `FeatureInfo(implies = depset([FeatureInfo(...)]))`
|
||||
|
||||
To solve this, we create a factory that:
|
||||
* Validates that the types of the children are correct.
|
||||
* Inlines providers to their labels when unambiguous.
|
||||
|
||||
For example, given:
|
||||
|
||||
```
|
||||
foo = FeatureInfo(name = "foo", label = Label("//:foo"))
|
||||
bar = FeatureInfo(..., implies = depset([foo]))
|
||||
```
|
||||
|
||||
It would convert itself a subject for the following struct:
|
||||
`FeatureInfo(..., implies = depset([Label("//:foo")]))`
|
||||
|
||||
Args:
|
||||
type: (type) The type to create a factory for (eg. FooInfo)
|
||||
name: (str) The name of the type (eg. "FooInfo")
|
||||
attrs: (dict[str, Factory]) The attributes associated with this type.
|
||||
|
||||
Returns:
|
||||
A struct `FooFactory` suitable for use with
|
||||
* `analysis_test(provider_subject_factories=[FooFactory])`
|
||||
* `generate_factory(..., attrs=dict(foo = FooFactory))`
|
||||
* `ProviderSequence(FooFactory)`
|
||||
* `DepsetSequence(FooFactory)`
|
||||
"""
|
||||
attrs["label"] = subjects.label
|
||||
|
||||
want_keys = sorted(attrs.keys())
|
||||
|
||||
def validate(*, value, meta):
|
||||
got_keys = sorted(structs.to_dict(value).keys())
|
||||
if got_keys != want_keys:
|
||||
meta.add_failure("Wanted a %s with keys %r, got %r" % (name, want_keys, got_keys), "")
|
||||
|
||||
def type_factory(value, *, meta):
|
||||
validate(value = value, meta = meta)
|
||||
|
||||
transformed_value = {}
|
||||
transformed_factories = {}
|
||||
for field, factory in attrs.items():
|
||||
field_value = getattr(value, field)
|
||||
|
||||
# If it's a type generated by generate_factory, inline it.
|
||||
if hasattr(factory, "factory"):
|
||||
factory.validate(value = field_value, meta = meta.derive(field))
|
||||
transformed_value[field] = field_value.label
|
||||
transformed_factories[field] = subjects.label
|
||||
else:
|
||||
transformed_value[field] = field_value
|
||||
transformed_factories[field] = factory
|
||||
|
||||
return subjects.struct(
|
||||
struct(**transformed_value),
|
||||
meta = meta,
|
||||
attrs = transformed_factories,
|
||||
)
|
||||
|
||||
return struct(
|
||||
type = type,
|
||||
name = name,
|
||||
factory = type_factory,
|
||||
validate = validate,
|
||||
)
|
||||
|
||||
def _provider_collection(element_factory, fn):
|
||||
def factory(value, *, meta):
|
||||
value = fn(value)
|
||||
|
||||
# Validate that it really is the correct type
|
||||
for i in range(len(value)):
|
||||
element_factory.validate(
|
||||
value = value[i],
|
||||
meta = meta.derive("offset({})".format(i)),
|
||||
)
|
||||
|
||||
# Inline the providers to just labels.
|
||||
return subjects.collection([v.label for v in value], meta = meta)
|
||||
|
||||
return factory
|
||||
|
||||
# This acts like a class, so we name it like one.
|
||||
# buildifier: disable=name-conventions
|
||||
ProviderSequence = lambda element_factory: _provider_collection(
|
||||
element_factory,
|
||||
fn = lambda x: list(x),
|
||||
)
|
||||
|
||||
# buildifier: disable=name-conventions
|
||||
ProviderDepset = lambda element_factory: _provider_collection(
|
||||
element_factory,
|
||||
fn = lambda x: x.to_list(),
|
||||
)
|
|
@ -0,0 +1,185 @@
|
|||
# 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.
|
||||
"""Test subjects for cc_toolchain_info providers."""
|
||||
|
||||
load("@rules_testing//lib:truth.bzl", "subjects")
|
||||
load(
|
||||
"//cc/toolchains:cc_toolchain_info.bzl",
|
||||
"ActionConfigInfo",
|
||||
"ActionConfigSetInfo",
|
||||
"ActionTypeInfo",
|
||||
"ActionTypeSetInfo",
|
||||
"FeatureConstraintInfo",
|
||||
"FeatureInfo",
|
||||
"FeatureSetInfo",
|
||||
"FlagGroupInfo",
|
||||
"FlagSetInfo",
|
||||
"MutuallyExclusiveCategoryInfo",
|
||||
"ToolInfo",
|
||||
)
|
||||
load(":generate_factory.bzl", "ProviderDepset", "ProviderSequence", "generate_factory")
|
||||
|
||||
visibility("private")
|
||||
|
||||
# buildifier: disable=name-conventions
|
||||
_ActionTypeFactory = generate_factory(
|
||||
ActionTypeInfo,
|
||||
"ActionTypeInfo",
|
||||
dict(
|
||||
name = subjects.str,
|
||||
),
|
||||
)
|
||||
|
||||
# buildifier: disable=name-conventions
|
||||
_ActionTypeSetFactory = generate_factory(
|
||||
ActionTypeSetInfo,
|
||||
"ActionTypeInfo",
|
||||
dict(
|
||||
actions = ProviderDepset(_ActionTypeFactory),
|
||||
),
|
||||
)
|
||||
|
||||
# buildifier: disable=name-conventions
|
||||
_MutuallyExclusiveCategoryFactory = generate_factory(
|
||||
MutuallyExclusiveCategoryInfo,
|
||||
"MutuallyExclusiveCategoryInfo",
|
||||
dict(name = subjects.str),
|
||||
)
|
||||
|
||||
_FEATURE_FLAGS = dict(
|
||||
name = subjects.str,
|
||||
enabled = subjects.bool,
|
||||
flag_sets = None,
|
||||
implies = None,
|
||||
requires_any_of = None,
|
||||
provides = ProviderSequence(_MutuallyExclusiveCategoryFactory),
|
||||
known = subjects.bool,
|
||||
overrides = None,
|
||||
)
|
||||
|
||||
# Break the dependency loop.
|
||||
# buildifier: disable=name-conventions
|
||||
_FakeFeatureFactory = generate_factory(
|
||||
FeatureInfo,
|
||||
"FeatureInfo",
|
||||
_FEATURE_FLAGS,
|
||||
)
|
||||
|
||||
# buildifier: disable=name-conventions
|
||||
_FeatureSetFactory = generate_factory(
|
||||
FeatureSetInfo,
|
||||
"FeatureSetInfo",
|
||||
dict(features = _FakeFeatureFactory),
|
||||
)
|
||||
|
||||
# buildifier: disable=name-conventions
|
||||
_FeatureConstraintFactory = generate_factory(
|
||||
FeatureConstraintInfo,
|
||||
"FeatureConstraintInfo",
|
||||
dict(
|
||||
all_of = ProviderDepset(_FakeFeatureFactory),
|
||||
none_of = ProviderDepset(_FakeFeatureFactory),
|
||||
),
|
||||
)
|
||||
|
||||
# buildifier: disable=name-conventions
|
||||
_FlagGroupFactory = generate_factory(
|
||||
FlagGroupInfo,
|
||||
"FlagGroupInfo",
|
||||
dict(
|
||||
flags = subjects.collection,
|
||||
),
|
||||
)
|
||||
|
||||
# buildifier: disable=name-conventions
|
||||
_FlagSetFactory = generate_factory(
|
||||
FlagSetInfo,
|
||||
"FlagSetInfo",
|
||||
dict(
|
||||
actions = ProviderDepset(_ActionTypeFactory),
|
||||
# Use a collection here because we don't want to
|
||||
flag_groups = subjects.collection,
|
||||
requires_any_of = ProviderSequence(_FeatureConstraintFactory),
|
||||
),
|
||||
)
|
||||
|
||||
# buildifier: disable=name-conventions
|
||||
_FeatureFactory = generate_factory(
|
||||
FeatureInfo,
|
||||
"FeatureInfo",
|
||||
_FEATURE_FLAGS | dict(
|
||||
implies = ProviderDepset(_FakeFeatureFactory),
|
||||
requires_any_of = ProviderSequence(_FeatureSetFactory),
|
||||
overrides = _FakeFeatureFactory,
|
||||
),
|
||||
)
|
||||
|
||||
# buildifier: disable=name-conventions
|
||||
_ToolFactory = generate_factory(
|
||||
ToolInfo,
|
||||
"ToolInfo",
|
||||
dict(
|
||||
exe = subjects.file,
|
||||
runifles = subjects.depset_file,
|
||||
requires_any_of = ProviderSequence(_FeatureConstraintFactory),
|
||||
),
|
||||
)
|
||||
|
||||
# buildifier: disable=name-conventions
|
||||
_ActionConfigFactory = generate_factory(
|
||||
ActionConfigInfo,
|
||||
"ActionConfigInfo",
|
||||
dict(
|
||||
action = _ActionTypeFactory,
|
||||
enabled = subjects.bool,
|
||||
tools = ProviderSequence(_ToolFactory),
|
||||
flag_sets = ProviderSequence(_FlagSetFactory),
|
||||
implies = ProviderDepset(_FeatureFactory),
|
||||
files = subjects.depset_file,
|
||||
),
|
||||
)
|
||||
|
||||
def _action_config_set_factory_impl(value, *, meta):
|
||||
# We can't use the usual strategy of "inline the labels" since all labels
|
||||
# are the same.
|
||||
transformed = {}
|
||||
for ac in value.action_configs.to_list():
|
||||
key = ac.action.label
|
||||
if key in transformed:
|
||||
meta.add_failure("Action declared twice in action config", key)
|
||||
transformed[key] = _ActionConfigFactory.factory(
|
||||
value = ac,
|
||||
meta = meta.derive(".get({})".format(key)),
|
||||
)
|
||||
return transformed
|
||||
|
||||
# buildifier: disable=name-conventions
|
||||
_ActionConfigSetFactory = struct(
|
||||
type = ActionConfigSetInfo,
|
||||
name = "ActionConfigSetInfo",
|
||||
factory = _action_config_set_factory_impl,
|
||||
)
|
||||
|
||||
FACTORIES = [
|
||||
_ActionTypeFactory,
|
||||
_ActionTypeSetFactory,
|
||||
_FlagGroupFactory,
|
||||
_FlagSetFactory,
|
||||
_MutuallyExclusiveCategoryFactory,
|
||||
_FeatureFactory,
|
||||
_FeatureConstraintFactory,
|
||||
_FeatureSetFactory,
|
||||
_ToolFactory,
|
||||
_ActionConfigSetFactory,
|
||||
]
|
Loading…
Reference in New Issue