xref: /aosp_15_r20/build/bazel/rules/cc/testing/transitions.bzl (revision 7594170e27e0732bc44b93d1440d87a54b6ffe7c)
1load("@bazel_skylib//lib:dicts.bzl", "dicts")
2load("@bazel_skylib//lib:sets.bzl", "sets")
3load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
4
5ActionArgsInfo = provider(
6    fields = {
7        "argv_map": "A dict with compile action arguments keyed by the target label",
8    },
9)
10
11def _action_argv_aspect_impl(target, ctx):
12    argv_map = {}
13    if ctx.rule.kind == ctx.attr._target_rule:
14        _cpp_commands_args = []
15        for action in target.actions:
16            if action.mnemonic == ctx.attr._action:
17                _cpp_commands_args.extend(action.argv)
18
19        if len(_cpp_commands_args):
20            argv_map = dicts.add(
21                argv_map,
22                {
23                    target.label.name: _cpp_commands_args,
24                },
25            )
26    elif ctx.rule.kind in ctx.attr._attr_aspect_dict.keys():
27        attrs = ctx.attr._attr_aspect_dict.get(ctx.rule.kind, [])
28        for attr_name in attrs:
29            value = getattr(ctx.rule.attr, attr_name)
30            vlist = value if type(value) == type([]) else [value]
31            for value in vlist:
32                argv_map = dicts.add(
33                    argv_map,
34                    value[ActionArgsInfo].argv_map,
35                )
36    return ActionArgsInfo(
37        argv_map = argv_map,
38    )
39
40def _get_attr_aspects_list(attr_aspects_dict):
41    return sets.to_list(
42        sets.make(
43            [attr for rule in attr_aspects_dict.values() for attr in rule],
44        ),
45    )
46
47# The aspects generated by this function are used to examine compile actions
48# from cc_library targets generated by our macros for the purpose of assessing
49# the results of transitions. Checking the targets directly using their names
50# gives info from before the transition is applied.
51# attr_aspects should be a dict where the keys are the names of rules and the
52# values are lists of attrs that should be traversed by the aspect looking for
53# cc_library targets.
54def compile_action_argv_aspect_generator(attr_aspects):
55    return aspect(
56        implementation = _action_argv_aspect_impl,
57        attr_aspects = _get_attr_aspects_list(attr_aspects),
58        attrs = {
59            "_attr_aspect_dict": attr.string_list_dict(default = attr_aspects),
60            "_action": attr.string(default = "CppCompile"),
61            "_target_rule": attr.string(default = "cc_library"),
62        },
63    )
64
65def link_action_argv_aspect_generator(attr_aspects, target_rule):
66    return aspect(
67        implementation = _action_argv_aspect_impl,
68        attr_aspects = _get_attr_aspects_list(attr_aspects),
69        attrs = {
70            "_attr_aspect_dict": attr.string_list_dict(default = attr_aspects),
71            "_action": attr.string(default = "CppLink"),
72            "_target_rule": attr.string(default = target_rule),
73        },
74    )
75
76def transition_deps_test_impl(ctx):
77    env = analysistest.begin(ctx)
78    target_under_test = analysistest.target_under_test(env)
79    argv_map = target_under_test[ActionArgsInfo].argv_map
80
81    for target in ctx.attr.targets_with_flag:
82        asserts.true(
83            env,
84            target in argv_map,
85            "can't find {} in argv map".format(target),
86        )
87        if target in argv_map:
88            argv = argv_map[target]
89            for flag in ctx.attr.flags:
90                asserts.true(
91                    env,
92                    flag in argv,
93                    "Action of {} didn't have {} flag but it was expected".format(
94                        target,
95                        flag,
96                    ),
97                )
98    for target in ctx.attr.targets_without_flag:
99        asserts.true(
100            env,
101            target in argv_map,
102            "can't find {} in argv map".format(target),
103        )
104        if target in argv_map:
105            argv = argv_map[target]
106            for flag in ctx.attr.flags:
107                asserts.true(
108                    env,
109                    flag not in argv,
110                    "Action of {} had {} flag but it wasn't expected".format(
111                        target,
112                        flag,
113                    ),
114                )
115    return analysistest.end(env)
116
117transition_deps_test_attrs = {
118    "targets_with_flag": attr.string_list(),
119    "targets_without_flag": attr.string_list(),
120    "flags": attr.string_list(),
121}
122