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)