kythe.io@v0.0.68-0.20240422202219-7225dbc01741/tools/build_rules/verifier_test/verifier_test.bzl (about)

     1  #
     2  # Copyright 2016 The Kythe Authors. All rights reserved.
     3  #
     4  # Licensed under the Apache License, Version 2.0 (the "License");
     5  # you may not use this file except in compliance with the License.
     6  # You may obtain a copy of the License at
     7  #
     8  #   http://www.apache.org/licenses/LICENSE-2.0
     9  #
    10  # Unless required by applicable law or agreed to in writing, software
    11  # distributed under the License is distributed on an "AS IS" BASIS,
    12  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  # See the License for the specific language governing permissions and
    14  # limitations under the License.
    15  """Rules and macros related to Kythe verifier-based tests."""
    16  
    17  load("@bazel_skylib//lib:shell.bzl", "shell")
    18  load(
    19      ":verifier_test_impl.bzl",
    20      _KytheEntries = "KytheEntries",
    21      _KytheEntryProducerInfo = "KytheEntryProducerInfo",
    22      _KytheVerifierSources = "KytheVerifierSources",
    23      _verifier_test = "verifier_test",
    24  )
    25  
    26  KytheEntries = _KytheEntries
    27  KytheEntryProducerInfo = _KytheEntryProducerInfo
    28  KytheVerifierSources = _KytheVerifierSources
    29  
    30  def _atomize_entries_impl(ctx):
    31      zcat = ctx.executable._zcat
    32      entrystream = ctx.executable._entrystream
    33      postprocessor = ctx.executable._postprocessor
    34      atomizer = ctx.executable._atomizer
    35  
    36      inputs = depset(ctx.files.srcs, transitive = [
    37          dep.kythe_entries
    38          for dep in ctx.attr.deps
    39      ])
    40  
    41      sort_args = ctx.actions.args()
    42      sort_args.add_all([zcat, entrystream, sorted_entries])
    43      sort_args.add_all(inputs)
    44      sorted_entries = ctx.actions.declare_file("_sorted_entries", sibling = ctx.outputs.entries)
    45      ctx.actions.run_shell(
    46          outputs = [sorted_entries],
    47          inputs = [zcat, entrystream] + inputs.to_list(),
    48          mnemonic = "SortEntries",
    49          command = '("$1" "${@:4}" | "$2" --sort) > "$3" || rm -f "$3"',
    50          arguments = [sort_args],
    51      )
    52  
    53      process_args = ctx.actions.args()
    54      process_args.add_all(["--entries", sorted_entries, "--out", leveldb])
    55      leveldb = ctx.actions.declare_file("_serving_tables", sibling = ctx.outputs.entries)
    56      ctx.actions.run(
    57          outputs = [leveldb],
    58          inputs = [sorted_entries, postprocessor],
    59          executable = postprocessor,
    60          mnemonic = "PostProcessEntries",
    61          arguments = [process_args],
    62      )
    63  
    64      atomize_args = ctx.actions.args()
    65      atomize_args.add_all([atomizer, "--api", leveldb])
    66      atomize_args.add_all(ctx.attr.file_tickets)
    67      atomize_args.add(ctx.outputs.entries)
    68      ctx.actions.run_shell(
    69          outputs = [ctx.outputs.entries],
    70          inputs = [atomizer, leveldb],
    71          mnemonic = "AtomizeEntries",
    72          command = '("${@:1:${#@}-1}" || rm -f "${@:${#@}}") | gzip -c > "${@:${#@}}"',
    73          arguments = [atomize_args],
    74          execution_requirements = {
    75              # TODO(shahms): Remove this when we can use a non-LevelDB store.
    76              "local": "true",  # LevelDB is bad and should feel bad.
    77          },
    78      )
    79      return struct()
    80  
    81  atomize_entries = rule(
    82      attrs = {
    83          "srcs": attr.label_list(
    84              mandatory = True,
    85              allow_files = [
    86                  ".entries",
    87                  ".entries.gz",
    88              ],
    89          ),
    90          "file_tickets": attr.string_list(
    91              mandatory = True,
    92              allow_empty = False,
    93          ),
    94          "deps": attr.label_list(
    95              providers = ["kythe_entries"],
    96          ),
    97          "_atomizer": attr.label(
    98              default = Label("//kythe/go/test/tools/xrefs_atomizer"),
    99              executable = True,
   100              cfg = "exec",
   101          ),
   102          "_entrystream": attr.label(
   103              default = Label("//kythe/go/platform/tools/entrystream"),
   104              executable = True,
   105              cfg = "exec",
   106          ),
   107          "_postprocessor": attr.label(
   108              default = Label("//kythe/go/serving/tools/write_tables"),
   109              executable = True,
   110              cfg = "exec",
   111          ),
   112          "_zcat": attr.label(
   113              default = Label("//tools:zcatext"),
   114              executable = True,
   115              cfg = "exec",
   116          ),
   117      },
   118      outputs = {
   119          "entries": "%{name}.entries.gz",
   120      },
   121      implementation = _atomize_entries_impl,
   122  )
   123  
   124  def extract(
   125          ctx,
   126          kzip,
   127          extractor,
   128          srcs,
   129          opts,
   130          deps = [],
   131          env = {},
   132          vnames_config = None,
   133          mnemonic = "ExtractCompilation"):
   134      """Create an extract action using the provided tool and inputs.
   135  
   136      Runs the extractor tool under an environment to produce the given kzip
   137      output file.  The extractor is passed each string from opts after expanding
   138      any build artifact locations and then each File's path from the srcs
   139      collection.
   140  
   141      Args:
   142        ctx: The Bazel rule context to use for actions.
   143        kzip: Declared .kzip output File
   144        extractor: Executable extractor tool to invoke
   145        srcs: Files passed to extractor tool; the compilation's source file inputs
   146        opts: List of options (or Args object) passed to the extractor tool before source files
   147        deps: Dependencies for the extractor's action (not passed to extractor on command-line)
   148        env: Dictionary of environment variables to provide.
   149        vnames_config: Optional path to a VName configuration file
   150        mnemonic: Mnemonic of the extractor's action
   151  
   152      Returns:
   153        The output file generated.
   154      """
   155      final_env = {
   156          "KYTHE_OUTPUT_FILE": kzip.path,
   157          "KYTHE_ROOT_DIRECTORY": ".",
   158      }
   159      final_env.update(env)
   160  
   161      if type(srcs) != "depset":
   162          srcs = depset(direct = srcs)
   163      if type(deps) != "depset":
   164          deps = depset(direct = deps)
   165      direct_inputs = []
   166      if vnames_config:
   167          final_env["KYTHE_VNAMES"] = vnames_config.path
   168          direct_inputs.append(vnames_config)
   169      inputs = depset(direct = direct_inputs, transitive = [srcs, deps])
   170  
   171      args = opts
   172      if type(args) != "Args":
   173          args = ctx.actions.args()
   174          args.add_all([ctx.expand_location(o) for o in opts])
   175      args.add_all(srcs)
   176  
   177      ctx.actions.run(
   178          inputs = inputs,
   179          tools = [extractor],
   180          outputs = [kzip],
   181          mnemonic = mnemonic,
   182          executable = extractor,
   183          arguments = [args],
   184          env = final_env,
   185          toolchain = None,
   186      )
   187      return kzip
   188  
   189  def _index_compilation_impl(ctx):
   190      sources = []
   191      intermediates = []
   192      test_runners = []
   193      kzips = []
   194      for dep in ctx.attr.deps:
   195          if KytheVerifierSources in dep:
   196              sources.append(dep[KytheVerifierSources].files)
   197          for input in dep.files.to_list():
   198              entries = ctx.actions.declare_file(
   199                  ctx.label.name + input.basename + ".entries",
   200                  sibling = ctx.outputs.entries,
   201              )
   202              intermediates.append(entries)
   203  
   204              if ctx.attr.target_indexer:
   205                  iargs = []
   206                  iargs += ctx.attr.opts
   207                  iargs.append(input.short_path)
   208                  test_runners.append(_make_test_runner(ctx, {}, arguments = iargs))
   209                  kzips.append(input)
   210  
   211              args = ctx.actions.args()
   212              args.add(ctx.executable.indexer)
   213              args.add_all([ctx.expand_location(o) for o in ctx.attr.opts])
   214              args.add_all([input, entries])
   215              ctx.actions.run_shell(
   216                  outputs = [entries],
   217                  inputs = [input],
   218                  tools = [ctx.executable.indexer] + ctx.files.tools,
   219                  arguments = [args],
   220                  command = '("${@:1:${#@}-1}" || rm -f "${@:${#@}}") > "${@:${#@}}"',
   221                  mnemonic = "IndexCompilation",
   222                  toolchain = None,
   223              )
   224  
   225      args = ctx.actions.args()
   226      args.add("cat")
   227      args.add_all(intermediates)
   228      args.add(ctx.outputs.entries)
   229      ctx.actions.run_shell(
   230          outputs = [ctx.outputs.entries],
   231          inputs = intermediates,
   232          command = '("${@:1:${#@}-1}" || rm -f "${@:${#@}}") | gzip -c > "${@:${#@}}"',
   233          mnemonic = "CompressEntries",
   234          arguments = [args],
   235      )
   236      providers = [
   237          KytheVerifierSources(files = depset(transitive = sources)),
   238      ]
   239      if test_runners:
   240          providers.append(
   241              KytheEntryProducerInfo(
   242                  executables = test_runners,
   243                  runfiles = ctx.runfiles(
   244                      files = (test_runners + kzips + ctx.files.target_tools),
   245                  ).merge(ctx.attr.target_indexer[DefaultInfo].default_runfiles),
   246              ),
   247          )
   248      else:
   249          providers.append(
   250              KytheEntries(compressed = depset([ctx.outputs.entries]), files = depset(intermediates)),
   251          )
   252      return providers
   253  
   254  def _make_test_runner(ctx, env, arguments):
   255      output = ctx.actions.declare_file(ctx.label.name + "_test_runner")
   256      ctx.actions.expand_template(
   257          output = output,
   258          is_executable = True,
   259          template = ctx.file._test_template,
   260          substitutions = {
   261              "@INDEXER@": shell.quote(ctx.executable.target_indexer.short_path),
   262              "@ENV@": "\n".join([
   263                  shell.quote("{key}={value}".format(key = key, value = value))
   264                  for key, value in env.items()
   265              ]),
   266              "@ARGS@": "\n".join([
   267                  shell.quote(ctx.expand_location(a.replace("$(location", "$(rootpath")))
   268                  for a in arguments
   269              ]),
   270          },
   271      )
   272      return output
   273  
   274  index_compilation = rule(
   275      attrs = {
   276          "indexer": attr.label(
   277              mandatory = True,
   278              executable = True,
   279              cfg = "exec",
   280          ),
   281          "target_indexer": attr.label(
   282              executable = True,
   283              cfg = "target",
   284          ),
   285          "opts": attr.string_list(),
   286          "tools": attr.label_list(
   287              cfg = "exec",
   288              allow_files = True,
   289          ),
   290          "target_tools": attr.label_list(
   291              allow_files = True,
   292          ),
   293          "deps": attr.label_list(
   294              mandatory = True,
   295              allow_empty = False,
   296              allow_files = [".kzip"],
   297          ),
   298          "_test_template": attr.label(
   299              default = Label("//tools/build_rules/verifier_test:indexer.sh.in"),
   300              allow_single_file = True,
   301          ),
   302      },
   303      outputs = {
   304          "entries": "%{name}.entries.gz",
   305      },
   306      implementation = _index_compilation_impl,
   307  )
   308  
   309  def _invoke(rulefn, name, **kwargs):
   310      """Invoke rulefn with name and kwargs, returning the label of the rule."""
   311      rulefn(name = name, **kwargs)
   312      return "//{}:{}".format(native.package_name(), name)
   313  
   314  def kythe_integration_test(name, srcs, file_tickets, tags = [], size = "small"):
   315      entries = _invoke(
   316          atomize_entries,
   317          name = name + "_atomized_entries",
   318          testonly = True,
   319          srcs = [],
   320          file_tickets = file_tickets,
   321          tags = tags,
   322          deps = srcs,
   323      )
   324      return _invoke(
   325          verifier_test,
   326          name = name,
   327          size = size,
   328          opts = ["--ignore_dups"],
   329          tags = tags,
   330          deps = [entries],
   331      )
   332  
   333  _VERIFIER_TEST_TAGS = []
   334  
   335  def verifier_test(**kwargs):
   336      tags = kwargs.pop("tags", []) or []
   337      _verifier_test(
   338          tags = tags + _VERIFIER_TEST_TAGS,
   339          **kwargs
   340      )