kythe.io@v0.0.68-0.20240422202219-7225dbc01741/tools/build_rules/extra_aspects/config.bzl (about) 1 # Copyright 2023 The Kythe Authors. All rights reserved. 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 15 """Kythe extractor configuration rules and providers.""" 16 17 load("@bazel_skylib//lib:sets.bzl", "sets") 18 load("@bazel_skylib//lib:structs.bzl", "structs") 19 load(":actions.bzl", _action_selection_config = "config") 20 load( 21 ":extra_actions.bzl", 22 "ActionListenerConfigInfo", 23 "ActionListenerInfo", 24 "ProtoWriterInfo", 25 "action_listener_aspect", 26 "extract_target", 27 ) 28 29 KytheExtractorConfigInfo = provider( 30 doc = "Configuration options for Kythe extraction.", 31 fields = { 32 "allowed_rule_kinds": "A Skylib set of allowed rule kinds (or None to allow everything).", 33 "action_listeners": "A depset of ActionListenerInfo from the provided target.", 34 "action_selection_config": "A KytheActionSelectionInfo instance.", 35 }, 36 ) 37 38 def _use_default_proto_writer(action_listener, proto_writer): 39 if not getattr(action_listener, "write_extra_action", None): 40 keys = structs.to_dict(action_listener) 41 keys["write_extra_action"] = proto_writer.write_extra_action 42 return ActionListenerInfo(**keys) 43 return action_listener 44 45 def _get_transitive_action_listeners(action_listeners, proto_writer): 46 return depset(direct = [ 47 _use_default_proto_writer(t[ActionListenerInfo], proto_writer[ProtoWriterInfo]) 48 for t in action_listeners 49 if ActionListenerInfo in t 50 ], transitive = [ 51 t[ActionListenerConfigInfo].action_listeners 52 for t in action_listeners 53 if ActionListenerConfigInfo in t 54 ]) 55 56 def _config_impl(ctx): 57 action_listeners = _get_transitive_action_listeners( 58 ctx.attr.action_listeners, 59 ctx.attr.proto_writer, 60 ).to_list() 61 62 # TODO(b/182403136): Workaround for error applying aspects to jsunit_test rules. 63 # They complain about a "non-executable dependency" so we provide a do-nothing executable 64 # file to silence this. 65 dummy_executable = ctx.actions.declare_file(ctx.label.name + ".dummy_exec") 66 ctx.actions.write( 67 output = dummy_executable, 68 content = "", 69 is_executable = True, 70 ) 71 72 return [ 73 KytheExtractorConfigInfo( 74 action_listeners = action_listeners, 75 allowed_rule_kinds = _allowed_rule_kinds(ctx.attr.rules, ctx.attr.aspect_rule_attrs), 76 action_selection_config = _action_selection_config( 77 rules = sets.make(ctx.attr.rules), 78 aspect_rule_attrs = ctx.attr.aspect_rule_attrs, 79 mnemonics = sets.union(*[a.mnemonics for a in action_listeners]), 80 exclude_input_extensions = sets.make(ctx.attr.exclude_input_extensions), 81 ), 82 ), 83 DefaultInfo( 84 executable = dummy_executable, 85 ), 86 ] 87 88 kythe_extractor_config = rule( 89 implementation = _config_impl, 90 attrs = { 91 "rules": attr.string_list(mandatory = True), 92 "aspect_rule_attrs": attr.string_list_dict(), 93 "action_listeners": attr.label_list( 94 aspects = [action_listener_aspect], 95 providers = [ 96 [ActionListenerInfo], 97 [ActionListenerConfigInfo], 98 ], 99 allow_rules = ["action_listener"], 100 ), 101 "proto_writer": attr.label( 102 default = Label("//tools/build_rules/extra_aspects:spawn-info-writer"), 103 providers = [ProtoWriterInfo], 104 ), 105 "exclude_input_extensions": attr.string_list(), 106 }, 107 provides = [KytheExtractorConfigInfo], 108 ) 109 110 def _action_listener_config_impl(ctx): 111 return [ 112 ActionListenerConfigInfo( 113 action_listeners = _get_transitive_action_listeners(ctx.attr.action_listeners, ctx.attr.proto_writer), 114 ), 115 ] 116 117 action_listener_config = rule( 118 implementation = _action_listener_config_impl, 119 attrs = { 120 "action_listeners": attr.label_list( 121 aspects = [action_listener_aspect], 122 providers = [ 123 [ActionListenerInfo], 124 [ActionListenerConfigInfo], 125 ], 126 allow_rules = ["action_listener"], 127 ), 128 "proto_writer": attr.label( 129 mandatory = True, 130 providers = [ProtoWriterInfo], 131 ), 132 }, 133 provides = [ActionListenerConfigInfo], 134 ) 135 136 def _allowed_rule_kinds(rules, aspect_rule_attrs): 137 if not rules: 138 return None # An empty `rules` attribute means allow all kinds. 139 return sets.union(sets.make(rules), sets.make(aspect_rule_attrs.keys())) 140 141 def run_configured_extractor(target, ctx, config): 142 # If there is an explicit allowlist of rule kinds, only extract those. 143 # Attempting to extract an unknown rule kind is not an error. 144 # As command-line aspects are called for all top-level targets, 145 # they must be able to quietly handle this case. 146 if config.allowed_rule_kinds and not sets.contains(config.allowed_rule_kinds, ctx.rule.kind): 147 return depset() 148 return extract_target(target, ctx, config)