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

     1  # Copyright 2019 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  load("@rules_java//java:defs.bzl", "JavaInfo", "java_binary", "java_common")
    16  load(
    17      ":verifier_test.bzl",
    18      "KytheVerifierSources",
    19      "extract",
    20      "index_compilation",
    21      "verifier_test",
    22  )
    23  load(
    24      "//kythe/cxx/indexer/proto/testdata:proto_verifier_test.bzl",
    25      "proto_extract_kzip",
    26  )
    27  load("//kythe/java/com/google/devtools/kythe/extractors/java/bazel:aspect.bzl", "extract_java")
    28  
    29  KytheGeneratedSourcesInfo = provider(
    30      doc = "Generated Java source directory and jar.",
    31      fields = {
    32          "srcjar": "Source jar of generated files.",
    33          "dir": "Directory of unpacked files in srcjar.",
    34      },
    35  )
    36  
    37  def _invoke(rulefn, name, **kwargs):
    38      """Invoke rulefn with name and kwargs, returning the label of the rule."""
    39      rulefn(name = name, **kwargs)
    40      return "//{}:{}".format(native.package_name(), name)
    41  
    42  def _filter_java_sources(src):
    43      if type(src) != "File":
    44          return src
    45      src = src.path
    46      if src.endswith(".java"):
    47          return src
    48      return None
    49  
    50  def _java_extract_kzip_impl(ctx):
    51      deps = []
    52      for dep in ctx.attr.deps:
    53          deps.append(dep[JavaInfo])
    54  
    55      srcs = []
    56      srcjars = []
    57      dirs = []
    58      for src in ctx.attr.srcs:
    59          if KytheGeneratedSourcesInfo in src:
    60              srcjars.append(src[KytheGeneratedSourcesInfo].srcjar)
    61              dirs.append(src[KytheGeneratedSourcesInfo].dir)
    62          else:
    63              srcs.append(src.files)
    64      srcs = depset(transitive = srcs).to_list()
    65  
    66      # Actually compile the sources to be used as a dependency for other tests
    67      jar = ctx.actions.declare_file(ctx.outputs.kzip.basename + ".jar", sibling = ctx.outputs.kzip)
    68  
    69      java_toolchain = ctx.attr._java_toolchain[java_common.JavaToolchainInfo]
    70      java_info = java_common.compile(
    71          ctx,
    72          javac_opts = ctx.attr.opts,
    73          java_toolchain = java_toolchain,
    74          source_jars = srcjars,
    75          source_files = srcs,
    76          output = jar,
    77          deps = deps,
    78      )
    79  
    80      jars = depset(transitive = [dep.compile_jars for dep in deps]).to_list()
    81  
    82      args = ctx.actions.args()
    83      args.add_all(ctx.attr.opts + ["-encoding", "utf-8"])
    84      args.add_joined("-cp", jars, join_with = ":")
    85      args.add_all(dirs, map_each = _filter_java_sources, expand_directories = True)
    86  
    87      extract(
    88          srcs = srcs,
    89          ctx = ctx,
    90          extractor = ctx.executable.extractor,
    91          kzip = ctx.outputs.kzip,
    92          mnemonic = "JavaExtractKZip",
    93          opts = args,
    94          vnames_config = ctx.file.vnames_config,
    95          deps = jars + ctx.files.data + dirs,
    96      )
    97      return [
    98          java_info,
    99          KytheVerifierSources(files = depset(srcs)),
   100      ]
   101  
   102  java_extract_kzip = rule(
   103      attrs = {
   104          "srcs": attr.label_list(
   105              mandatory = True,
   106              allow_empty = False,
   107              allow_files = True,
   108          ),
   109          "data": attr.label_list(
   110              allow_files = True,
   111          ),
   112          "extractor": attr.label(
   113              default = Label("@io_kythe//kythe/java/com/google/devtools/kythe/extractors/java/standalone:javac_extractor"),
   114              executable = True,
   115              cfg = "exec",
   116          ),
   117          "opts": attr.string_list(),
   118          "vnames_config": attr.label(
   119              default = Label("//external:vnames_config"),
   120              allow_single_file = True,
   121          ),
   122          "deps": attr.label_list(
   123              providers = [JavaInfo],
   124          ),
   125          "_java_toolchain": attr.label(
   126              default = Label("@rules_java//toolchains:current_java_toolchain"),
   127          ),
   128      },
   129      toolchains = ["@bazel_tools//tools/jdk:toolchain_type"],
   130      fragments = ["java"],
   131      outputs = {"kzip": "%{name}.kzip"},
   132      implementation = _java_extract_kzip_impl,
   133  )
   134  
   135  _default_java_extractor_opts = [
   136      "-source",
   137      "9",
   138      "-target",
   139      "9",
   140  ]
   141  
   142  def java_verifier_test(
   143          name,
   144          srcs = None,
   145          compilation = None,
   146          meta = [],
   147          verifier_deps = [],
   148          deps = [],
   149          size = "small",
   150          timeout = None,
   151          tags = [],
   152          extractor = None,
   153          resolve_code_facts = False,
   154          extractor_opts = _default_java_extractor_opts,
   155          indexer_opts = ["--verbose"],
   156          verifier_opts = ["--ignore_dups"],
   157          load_plugin = None,
   158          extra_goals = [],
   159          vnames_config = None,
   160          visibility = None):
   161      """Extract, analyze, and verify a Java compilation.
   162  
   163      Args:
   164        srcs: The compilation's source file inputs; each file's verifier goals will be checked
   165        compilation: Specific Bazel Java target compilation to extract, analyze, and verify
   166        verifier_deps: Optional list of java_verifier_test targets to be used as Java compilation dependencies
   167        deps: Optional list of Java compilation dependencies
   168        meta: Optional list of Kythe metadata files
   169        extractor: Executable extractor tool to invoke (defaults to javac_extractor)
   170        extractor_opts: List of options passed to the extractor tool
   171        indexer_opts: List of options passed to the indexer tool
   172        verifier_opts: List of options passed to the verifier tool
   173        load_plugin: Optional Java analyzer plugin to load
   174        extra_goals: List of text files containing verifier goals additional to those in srcs
   175        vnames_config: Optional path to a VName configuration file
   176      """
   177      if compilation:
   178          kzip = name + "_kzip"
   179          extract_java(name = kzip, testonly = True, compilation = compilation)
   180      else:
   181          kzip = _invoke(
   182              java_extract_kzip,
   183              name = name + "_kzip",
   184              testonly = True,
   185              srcs = srcs,
   186              data = meta,
   187              extractor = extractor,
   188              opts = extractor_opts,
   189              tags = tags,
   190              visibility = visibility,
   191              vnames_config = vnames_config,
   192              # This is a hack to depend on the .jar producer.
   193              deps = deps + [d + "_kzip" for d in verifier_deps],
   194          )
   195      indexer = "//kythe/java/com/google/devtools/kythe/analyzers/java:indexer"
   196      tools = []
   197      if load_plugin:
   198          # If loaded plugins have deps, those must be included in the loaded jar
   199          java_binary(
   200              name = name + "_load_plugin",
   201              main_class = "not.Used",
   202              runtime_deps = [load_plugin],
   203          )
   204          load_plugin_deploy_jar = ":{}_load_plugin_deploy.jar".format(name)
   205          indexer_opts = indexer_opts + [
   206              "--load_plugin",
   207              "$(location {})".format(load_plugin_deploy_jar),
   208          ]
   209          tools.append(load_plugin_deploy_jar)
   210  
   211      entries = _invoke(
   212          index_compilation,
   213          name = name + "_entries",
   214          testonly = True,
   215          indexer = indexer,
   216          target_indexer = indexer,
   217          opts = indexer_opts,
   218          tags = tags + ["manual"],
   219          tools = tools,
   220          target_tools = tools,
   221          visibility = visibility,
   222          deps = [kzip],
   223      )
   224      goals = extra_goals
   225      if len(goals) > 0:
   226          goals += [entries] + [dep + "_entries" for dep in verifier_deps]
   227      return _invoke(
   228          verifier_test,
   229          name = name,
   230          size = size,
   231          timeout = timeout,
   232          srcs = goals,
   233          opts = verifier_opts + [
   234              "--goal_regex='\\s*//\\s*-(.*)'",
   235          ],
   236          tags = tags,
   237          visibility = visibility,
   238          resolve_code_facts = resolve_code_facts,
   239          deps = [entries] + [dep + "_entries" for dep in verifier_deps],
   240      )
   241  
   242  def _generate_java_proto_impl(ctx):
   243      # Generate the Java protocol buffer sources into a directory.
   244      # Note: out contains .meta files with annotations for cross-language xrefs.
   245      out = ctx.actions.declare_directory(ctx.label.name)
   246      protoc = ctx.executable._protoc
   247      ctx.actions.run_shell(
   248          outputs = [out],
   249          inputs = ctx.files.srcs,
   250          tools = [protoc],
   251          command = "\n".join([
   252              "#/bin/bash",
   253              "set -e",
   254              # Creating the declared directory in this action is necessary for
   255              # remote execution environments.  This differs from local execution
   256              # where Bazel will create the directory before this action is
   257              # executed.
   258              "mkdir -p " + out.path,
   259              " ".join([
   260                  protoc.path,
   261                  "--java_out=annotate_code:" + out.path,
   262              ] + [src.path for src in ctx.files.srcs]),
   263          ]),
   264      )
   265  
   266      # Produce a source jar file for the native Java compilation in the java_extract_kzip rule.
   267      # Note: we can't use java_common.pack_sources because our input is a directory.
   268      srcjar = ctx.actions.declare_file(ctx.label.name + ".srcjar")
   269      args = ctx.actions.args()
   270      args.add_all(["--output", srcjar])
   271      args.add_all(["--resources", out], map_each = _filter_java_sources, expand_directories = True)
   272      ctx.actions.run(
   273          outputs = [srcjar],
   274          inputs = [out],
   275          executable = ctx.executable._singlejar,
   276          arguments = [args],
   277      )
   278  
   279      return [
   280          DefaultInfo(files = depset([out, srcjar])),
   281          KytheGeneratedSourcesInfo(dir = out, srcjar = srcjar),
   282      ]
   283  
   284  _generate_java_proto = rule(
   285      attrs = {
   286          "srcs": attr.label_list(
   287              mandatory = True,
   288              allow_files = True,
   289              providers = [JavaInfo],
   290          ),
   291          "_protoc": attr.label(
   292              default = Label("@com_google_protobuf//:protoc"),
   293              executable = True,
   294              cfg = "exec",
   295          ),
   296          "_singlejar": attr.label(
   297              default = Label("@rules_java//toolchains:singlejar"),
   298              executable = True,
   299              cfg = "exec",
   300          ),
   301      },
   302      implementation = _generate_java_proto_impl,
   303  )
   304  
   305  def java_proto_verifier_test(
   306          name,
   307          srcs,
   308          size = "small",
   309          proto_libs = [],
   310          proto_srcs = [],
   311          tags = [],
   312          java_extractor_opts = _default_java_extractor_opts,
   313          verifier_opts = ["--ignore_dups"],
   314          vnames_config = None,
   315          visibility = None):
   316      """Verify cross-language references between Java and Proto.
   317  
   318      Args:
   319        name: Name of the test.
   320        size: Size of the test.
   321        tags: Test target tags.
   322        visibility: Visibility of the test target.
   323        srcs: The compilation's Java source files; each file's verifier goals will be checked
   324        proto_libs: The proto_library targets containing proto_srcs
   325        proto_srcs: The compilation's proto source files; each file's verifier goals will be checked
   326        verifier_opts: List of options passed to the verifier tool
   327        vnames_config: Optional path to a VName configuration file
   328  
   329      Returns: the label of the test.
   330      """
   331      proto_kzip = _invoke(
   332          proto_extract_kzip,
   333          name = name + "_proto_kzip",
   334          srcs = proto_libs,
   335          tags = tags,
   336          visibility = visibility,
   337          vnames_config = vnames_config,
   338      )
   339      proto_entries = _invoke(
   340          index_compilation,
   341          name = name + "_proto_entries",
   342          testonly = True,
   343          indexer = "//kythe/cxx/indexer/proto:indexer",
   344          opts = ["--index_file"],
   345          tags = tags,
   346          visibility = visibility,
   347          deps = [proto_kzip],
   348      )
   349  
   350      # TODO(justinbuchanan): use java_proto_library instead of manually invoking protoc
   351      _generate_java_proto(
   352          name = name + "_gensrc",
   353          srcs = proto_srcs,
   354      )
   355  
   356      kzip = _invoke(
   357          java_extract_kzip,
   358          name = name + "_java_kzip",
   359          srcs = srcs + [":" + name + "_gensrc"],
   360          opts = java_extractor_opts,
   361          tags = tags,
   362          visibility = visibility,
   363          vnames_config = vnames_config,
   364          deps = [
   365              "@com_google_protobuf//:protobuf_java",
   366              "@maven//:org_apache_tomcat_tomcat_annotations_api",
   367          ],
   368      )
   369  
   370      entries = _invoke(
   371          index_compilation,
   372          name = name + "_java_entries",
   373          testonly = True,
   374          indexer = "//kythe/java/com/google/devtools/kythe/analyzers/java:indexer",
   375          opts = ["--verbose"],
   376          tags = tags,
   377          visibility = visibility,
   378          deps = [kzip],
   379      )
   380      return _invoke(
   381          verifier_test,
   382          name = name,
   383          size = size,
   384          srcs = [entries, proto_entries],
   385          deps = [entries, proto_entries],
   386          opts = verifier_opts,
   387          tags = tags,
   388          visibility = visibility,
   389      )