Filter test_suites via our Aspect
We manually mirror Bazel filtering for test_suites.
PiperOrigin-RevId: 183096486
diff --git a/src/TulsiGenerator/Bazel/tulsi/tulsi_aspects.bzl b/src/TulsiGenerator/Bazel/tulsi/tulsi_aspects.bzl
index df83978..d61512c 100644
--- a/src/TulsiGenerator/Bazel/tulsi/tulsi_aspects.bzl
+++ b/src/TulsiGenerator/Bazel/tulsi/tulsi_aspects.bzl
@@ -325,21 +325,22 @@
return datamodelds
-def _collect_dependency_labels(rule, attr_list):
+def _collect_dependency_labels(rule, filter, attr_list):
"""Collects Bazel labels for a list of dependency attributes.
Args:
rule: The Bazel rule whose dependencies should be collected.
+ filter: Filter to apply when gathering dependencies.
attr_list: List of attribute names potentially containing Bazel labels for
dependencies of the given rule.
Returns:
A list of the Bazel labels of dependencies of the given rule.
"""
- rule_attrs = rule.attr
+ attr = rule.attr
deps = [dep
for attribute in attr_list
- for dep in _getattr_as_list(rule_attrs, attribute)]
+ for dep in _filter_deps(filter, _getattr_as_list(attr, attribute))]
return [dep.label for dep in deps if hasattr(dep, 'label')]
@@ -507,11 +508,26 @@
return headers
+def _target_filtering_info(ctx):
+ """Returns filtering information for test rules."""
+ rule = ctx.rule
+
+ # TODO(b/72406542): Clean this up to use a test provider if possible.
+ if rule.kind.endswith('_test'):
+ # Note that a test's size is considered a tag for filtering purposes.
+ size = _getattr_as_list(rule, 'attr.size')
+ tags = _getattr_as_list(rule, 'attr.tags')
+ return struct(tags=tags + size)
+ else:
+ return None
+
+
def _tulsi_sources_aspect(target, ctx):
"""Extracts information from a given rule, emitting it as a JSON struct."""
rule = ctx.rule
target_kind = rule.kind
rule_attr = _get_opt_attr(rule, 'attr')
+ filter = _filter_for_rule(rule)
bundle_name = None
if AppleBundleInfo in target:
bundle_name = target[AppleBundleInfo].bundle_name
@@ -520,7 +536,7 @@
transitive_attributes = dict()
for attr_name in _TULSI_COMPILE_DEPS:
deps = _getattr_as_list(rule_attr, attr_name)
- for dep in deps:
+ for dep in _filter_deps(filter, deps):
if hasattr(dep, 'tulsi_info_files'):
tulsi_info_files += dep.tulsi_info_files
if hasattr(dep, 'transitive_attributes'):
@@ -558,7 +574,7 @@
# Collect the dependencies of this rule, dropping any .jar files (which may be
# created as artifacts of java/j2objc rules).
- dep_labels = _collect_dependency_labels(rule, _TULSI_COMPILE_DEPS)
+ dep_labels = _collect_dependency_labels(rule, filter, _TULSI_COMPILE_DEPS)
compile_deps = [str(d) for d in dep_labels if not d.name.endswith('.jar')]
binary_rule = _get_opt_attr(rule_attr, 'binary')
@@ -686,6 +702,8 @@
transitive_attributes=transitive_attributes,
# Artifacts from this rule.
artifacts=artifacts_depset,
+ # Filtering information for this target.
+ filtering_info=_target_filtering_info(ctx),
)
@@ -703,6 +721,82 @@
return None
+# Due to b/71744111 we have to manually re-create tag filtering for test_suite
+# rules.
+def _tags_conform_to_filter(tags, filter):
+ """Mirrors Bazel tag filtering for test_suites.
+
+ This makes sure that the target has all of the required tags and none of
+ the excluded tags before we include them within a test_suite.
+
+ For more information on filtering inside Bazel, see
+ com.google.devtools.build.lib.packages.TestTargetUtils.java.
+
+ Args:
+ tags: all of the tags for the test target
+ filter: a struct containing excluded_tags and required_tags
+
+ Returns:
+ True if this target passes the filter and False otherwise.
+
+ """
+
+ # None of the excluded tags can be present.
+ for exclude in filter.excluded_tags:
+ if exclude in tags:
+ return False
+
+ # All of the required tags must be present.
+ for required in filter.required_tags:
+ if required not in tags:
+ return False
+
+ # All filters have been satisfied.
+ return True
+
+
+def _filter_for_rule(rule):
+ """Returns a filter for test_suite rules and None for other rules."""
+ if rule.kind != 'test_suite':
+ return None
+
+ excluded_tags = []
+ required_tags = []
+
+ tags = _getattr_as_list(rule, 'attr.tags')
+
+ for tag in tags:
+ if tag.startswith('-'):
+ excluded_tags.append(tag[1:])
+ elif tag.startswith('+'):
+ required_tags.append(tag[1:])
+ elif tag == 'manual':
+ # The manual tag is treated specially; it is ignored for filters.
+ continue
+ else:
+ required_tags.append(tag)
+ return struct(
+ excluded_tags=excluded_tags,
+ required_tags=required_tags,
+ )
+
+
+def _filter_deps(filter, deps):
+ """Filters dep targets based on tags."""
+ if not filter:
+ return deps
+
+ kept_deps = []
+ for dep in deps:
+ info = dep.filtering_info
+ # Only attempt to filter targets that support filtering.
+ # test_suites in a test_suite are not filtered, but their
+ # tests are.
+ if not info or _tags_conform_to_filter(info.tags, filter):
+ kept_deps.append(dep)
+ return kept_deps
+
+
def _tulsi_outputs_aspect(target, ctx):
"""Collects outputs of each build invocation."""