kythe.io@v0.0.68-0.20240422202219-7225dbc01741/tools/build_rules/verifier_test/verifier_test_impl.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  """Private implementation details for verifier_test rules.
    15  
    16  Public API available in verifier_test.bzl.
    17  """
    18  
    19  load("@bazel_skylib//lib:shell.bzl", "shell")
    20  
    21  visibility("private")
    22  
    23  KytheVerifierSources = provider(
    24      doc = "Input files which the verifier should inspect for assertions.",
    25      fields = {
    26          "files": "Depset of files which should be considered.",
    27      },
    28  )
    29  
    30  KytheEntries = provider(
    31      doc = "Kythe indexer entry facts.",
    32      fields = {
    33          "compressed": "Depset of combined, compressed index entries.",
    34          "files": "Depset of files which combine to make an index.",
    35          "log": "Log file from the indexer execution. The file will be stored in test outputs.",
    36          "name": "Name that uniquely identifies these entries within a test. For example it can be language name if test is multi-language.",
    37      },
    38  )
    39  
    40  KytheEntryProducerInfo = provider(
    41      doc = "Provider indicating an executable to be called which will produce Kythe entries on stdout.",
    42      fields = {
    43          "executables": "A list of File objects to run which should produce Kythe entries on stdout.",
    44          "runfiles": "Required runfiles.",
    45          "log": "Log file from the indexer execution. The file will be stored in test outputs.",
    46          "name": "Name that uniquely identifies these entries within a test. For example it can be language name if test is multi-language.",
    47      },
    48  )
    49  
    50  def _verifier_test_impl(ctx):
    51      entries = []
    52      entries_gz = []
    53      sources = []
    54      for src in ctx.attr.srcs:
    55          if KytheVerifierSources in src:
    56              sources.append(src[KytheVerifierSources].files)
    57          else:
    58              sources.append(src.files)
    59  
    60      # Check that all deps have unique names.
    61      unique_names = {}
    62      for dep in ctx.attr.deps:
    63          info = None
    64          if KytheEntryProducerInfo in dep:
    65              info = dep[KytheEntryProducerInfo]
    66          elif KytheEntries in dep:
    67              info = dep[KytheEntries]
    68          if info and hasattr(info, "name"):
    69              if info.name in unique_names:
    70                  fail("Multiple deps producers with the same name: %s. Deps: %s, %s" % (info.name, unique_names[info.name], dep.label.name))
    71              unique_names[info.name] = dep.label.name
    72  
    73      # List of '$indexer:$name' strings to execute during the test. 'name' is used in log file.
    74      indexers = []
    75      runfiles = []
    76  
    77      # List of '$logfile:$name_indexer.log' strings.
    78      logs = []
    79  
    80      # List of '$entryfile:$name_entries.json' strings.
    81      entries_to_log = []
    82      for dep in ctx.attr.deps:
    83          if KytheEntryProducerInfo in dep:
    84              runfiles.append(dep[KytheEntryProducerInfo].runfiles)
    85              for executable in dep[KytheEntryProducerInfo].executables:
    86                  name = dep[KytheEntryProducerInfo].name if hasattr(dep[KytheEntryProducerInfo], "name") else dep.label.name.replace("/", "_")
    87                  indexers.append("%s:%s" % (
    88                      shell.quote(executable.short_path),
    89                      name,
    90                  ))
    91          if KytheEntries in dep:
    92              if dep[KytheEntries].files:
    93                  entries.append(dep[KytheEntries].files)
    94                  if hasattr(dep[KytheEntries], "name"):
    95                      for file in dep[KytheEntries].files.to_list():
    96                          entries_to_log.append("%s:%s_entries.json" % (
    97                              shell.quote(file.short_path),
    98                              dep[KytheEntries].name,
    99                          ))
   100              else:
   101                  entries_gz.append(dep[KytheEntries].compressed)
   102  
   103              if hasattr(dep[KytheEntries], "log"):
   104                  runfiles.append(ctx.runfiles(files = [dep[KytheEntries].log]))
   105                  logs.append("%s:%s_indexer.log" % (
   106                      shell.quote(dep[KytheEntries].log.short_path),
   107                      dep[KytheEntries].name,
   108                  ))
   109  
   110      # Flatten input lists
   111      entries = depset(transitive = entries).to_list()
   112      entries_gz = depset(transitive = entries_gz).to_list()
   113      sources = depset(transitive = sources).to_list()
   114  
   115      if not (entries or entries_gz or indexers):
   116          fail("Missing required entry stream input (check your deps!)")
   117      args = ctx.attr.opts + [shell.quote(src.short_path) for src in sources]
   118  
   119      # If no dependency specifies KytheVerifierSources and
   120      # we aren't provided explicit sources, assume `--use_file_nodes`.
   121      if not sources and "--use_file_nodes" not in args:
   122          args.append("--use_file_nodes")
   123  
   124      ctx.actions.expand_template(
   125          template = ctx.file._template,
   126          output = ctx.outputs.executable,
   127          is_executable = True,
   128          substitutions = {
   129              "@ARGS@": " ".join(args),
   130              "@INDEXERS@": " ".join(indexers),
   131              "@INDEXER_LOGS@": " ".join(logs),
   132              "@ENTRIES@": " ".join([shell.quote(e.short_path) for e in entries]),
   133              "@ENTRIES_TO_LOG@": " ".join(entries_to_log),
   134              "@ENTRIES_GZ@": " ".join([shell.quote(e.short_path) for e in entries_gz]),
   135              # If failure is expected, invert the sense of the verifier return.
   136              "@INVERT@": "!" if not ctx.attr.expect_success else "",
   137              "@VERIFIER@": shell.quote(ctx.executable._verifier.short_path),
   138              "@REWRITE@": "1" if not ctx.attr.resolve_code_facts else "",
   139              "@MARKEDSOURCE@": shell.quote(ctx.executable._markedsource.short_path),
   140              "@WORKSPACE_NAME@": ctx.workspace_name,
   141              "@ENTRYSTREAM@": shell.quote(ctx.executable._entrystream.short_path),
   142          },
   143      )
   144      tools = [
   145          ctx.outputs.executable,
   146          ctx.executable._verifier,
   147          ctx.executable._markedsource,
   148          ctx.executable._entrystream,
   149      ]
   150      return [
   151          DefaultInfo(
   152              runfiles = ctx.runfiles(files = sources + entries + entries_gz + tools).merge_all(runfiles),
   153              executable = ctx.outputs.executable,
   154          ),
   155      ]
   156  
   157  verifier_test = rule(
   158      attrs = {
   159          "srcs": attr.label_list(
   160              doc = "Targets or files containing verifier goals.",
   161              allow_files = True,
   162              providers = [KytheVerifierSources],
   163          ),
   164          "resolve_code_facts": attr.bool(default = False),
   165          # Arguably, "expect_failure" is more natural, but that
   166          # attribute is used by Skylark.
   167          "expect_success": attr.bool(default = True),
   168          "opts": attr.string_list(),
   169          "deps": attr.label_list(
   170              doc = "Targets which produce graph entries to verify.",
   171              providers = [[KytheEntries], [KytheEntryProducerInfo]],
   172          ),
   173          "_template": attr.label(
   174              default = Label("//tools/build_rules/verifier_test:verifier_test.sh.in"),
   175              allow_single_file = True,
   176          ),
   177          "_verifier": attr.label(
   178              default = Label("//kythe/cxx/verifier"),
   179              executable = True,
   180              cfg = "target",
   181          ),
   182          "_markedsource": attr.label(
   183              default = Label("//kythe/go/util/tools/markedsource"),
   184              executable = True,
   185              cfg = "target",
   186          ),
   187          "_entrystream": attr.label(
   188              default = Label("//kythe/go/platform/tools/entrystream"),
   189              executable = True,
   190              cfg = "target",
   191          ),
   192      },
   193      test = True,
   194      implementation = _verifier_test_impl,
   195  )