kythe.io@v0.0.68-0.20240422202219-7225dbc01741/tools/build_rules/extra_aspects/actions.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  """Starlark utilities for selecting actions."""
    16  
    17  load("@bazel_skylib//lib:sets.bzl", "sets")
    18  
    19  def select_target_actions(target, rule, config):
    20      """Given a target and a rule, finds actions matching the specified config.
    21  
    22      Args:
    23        target: The Target object provided to an aspect implementation.
    24        rule: The rule_attributes from an aspect's ctx.rule parameter.
    25        config: A KytheActionSelectionInfo instance, configuring action selection.
    26  
    27      Returns:
    28        A list of actions selected by the provided configuration.
    29      """
    30      actions = []
    31      if sets.length(config.rules) == 0 or sets.contains(config.rules, rule.kind):
    32          actions.extend(_filter_actions(target.actions, config.mnemonics, config.exclude_input_extensions))
    33      for attr in config.aspect_rule_attrs.get(rule.kind, []):
    34          for dep in getattr(rule.attr, attr, []):
    35              # {lang}_proto_library targets are implemented as aspects
    36              # with a single "dep" pointing at original proto_library target.
    37              # All of the actions come from the aspect as applied to the dep,
    38              # which is not a top-level dep, so we need to inspect those as well.
    39              # TODO(shahms): It would be nice if there was some way to tell whether or
    40              # not an action came from an aspect or the underlying rule. We could then
    41              # more generally inspect deps to find actions rather than limiting it to
    42              # allowlisted kinds.
    43              # This could be accomplished by having `aspect_ids` populated on deps.
    44              # We could look specifically for "<merged target" in the string-ified dep,
    45              # but that's pretty ugly.
    46              # It looks like adding an "aspect_ids" list attribute should be straightforward.
    47              # https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/MergedConfiguredTarget.java#L54
    48              # It could also just use ActionOwner information.
    49              if str(dep).startswith("<merged target") and hasattr(dep, "actions"):
    50                  actions.extend(_filter_actions(dep.actions, config.mnemonics, config.exclude_input_extensions))
    51      return actions
    52  
    53  def config(rules, aspect_rule_attrs, mnemonics, exclude_input_extensions):
    54      """Create an action selection config.
    55  
    56      Args:
    57       rules: A Skylib set of rule kinds from which to select actions.
    58       aspect_rule_attrs: A dict of {kind: [attrs]} of rule kind attributes from which to select actions.
    59       mnemonics: A Skylib set of action mnemonics to select.
    60       exclude_input_extensions: A Skylib set of file extensions to whose actions to exclude.
    61  
    62      Returns:
    63        A struct with corresponding attributes.
    64      """
    65      return struct(
    66          rules = rules,
    67          aspect_rule_attrs = aspect_rule_attrs,
    68          mnemonics = mnemonics,
    69          exclude_input_extensions = exclude_input_extensions,
    70      )
    71  
    72  def _has_extension(inputs, extensions):
    73      if not extensions or sets.length(extensions) == 0:
    74          return False
    75      for input in inputs.to_list():
    76          if sets.contains(extensions, input.extension):
    77              return True
    78      return False
    79  
    80  def _filter_actions(actions, mnemonics, exclude_extensions = None):
    81      return [
    82          a
    83          for a in actions
    84          if sets.contains(mnemonics, a.mnemonic) and
    85             not _has_extension(a.inputs, exclude_extensions)
    86      ]