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