diff --git a/rules/private/phases/phase_semanticdb.bzl b/rules/private/phases/phase_semanticdb.bzl index 6553d682..3d2d7f87 100644 --- a/rules/private/phases/phase_semanticdb.bzl +++ b/rules/private/phases/phase_semanticdb.bzl @@ -1,5 +1,12 @@ load("@bazel_skylib//lib:paths.bzl", "paths") -load("@rules_scala_annex//rules:providers.bzl", _ScalaConfiguration = "ScalaConfiguration") +load( + "@rules_scala_annex//rules:providers.bzl", + _ScalaConfiguration = "ScalaConfiguration", + _SemanticDbInfo = "SemanticDbInfo", +) + +def _semanticdb_directory_from_file(file): + return file.path[:file.path.find("META-INF") - 1] # # PHASE: semanticdb @@ -11,32 +18,47 @@ def phase_semanticdb(ctx, g): scala_configuration = ctx.attr.scala[_ScalaConfiguration] if scala_configuration.semanticdb_bundle: - return struct(outputs = [], scalacopts = []) + return struct(outputs = [], arguments_modifier = lambda _: None) + directory_name = "{}/semanticdb".format(ctx.label.name) outputs = [] - semanticdb_directory = paths.join("_semanticdb/", ctx.label.name) - semanticdb_target_root = paths.join(paths.dirname(ctx.outputs.jar.path), semanticdb_directory) for source in ctx.files.srcs: if source.extension == "scala": - output_filename = paths.join( - semanticdb_directory, + path = paths.join( + directory_name, "META-INF", "semanticdb", "{}.semanticdb".format(source.path), ) - outputs.append(ctx.actions.declare_file(output_filename)) + outputs.append(ctx.actions.declare_file(path)) + + def add_scalacopts(arguments): + if len(outputs) == 0: + return + + if scala_configuration.version.startswith("2"): + arguments.add("--compiler_option=-P:semanticdb:failures:error") + arguments.add_all( + [outputs[0]], + format_each = "--compiler_option=-P:semanticdb:targetroot:%s", + map_each = _semanticdb_directory_from_file, + ) + else: + arguments.add_all( + [outputs[0]], + format_each = "--compiler_option=-semanticdb-target:%s", + map_each = _semanticdb_directory_from_file, + ) + + arguments.add("--compiler_option=-Ysemanticdb") - if scala_configuration.version.startswith("2"): - scalacopts = [ - "-P:semanticdb:failures:error", - "-P:semanticdb:targetroot:{}".format(semanticdb_target_root), - ] - else: - scalacopts = [ - "-semanticdb-target:{}".format(semanticdb_target_root), - "-Ysemanticdb", - ] + g.out.providers.append( + _SemanticDbInfo( + target_root = "{}/{}".format(ctx.label.package, directory_name), + semanticdb_files = outputs, + ), + ) - return struct(outputs = outputs, scalacopts = scalacopts) + return struct(outputs = outputs, arguments_modifier = add_scalacopts) diff --git a/rules/private/phases/phase_zinc_compile.bzl b/rules/private/phases/phase_zinc_compile.bzl index c57fccdb..2a323c1b 100644 --- a/rules/private/phases/phase_zinc_compile.bzl +++ b/rules/private/phases/phase_zinc_compile.bzl @@ -37,10 +37,9 @@ def phase_zinc_compile(ctx, g): ] zincs = [dep[_ZincInfo] for dep in ctx.attr.deps if _ZincInfo in dep] - common_scalacopts = scala_configuration.global_scalacopts + ctx.attr.scalacopts + g.semanticdb.scalacopts + common_scalacopts = scala_configuration.global_scalacopts + ctx.attr.scalacopts args = ctx.actions.args() - args.add_all(depset(transitive = [zinc.deps for zinc in zincs]), map_each = _compile_analysis) args.add("--compiler_bridge", zinc_configuration.compiler_bridge) args.add_all("--compiler_classpath", g.classpaths.compiler) @@ -55,8 +54,10 @@ def phase_zinc_compile(ctx, g): args.add_all("--plugins", g.classpaths.plugin) args.add_all("--source_jars", g.classpaths.src_jars) args.add("--tmp", tmp.path) - args.add("--log_level", zinc_configuration.log_level) + + g.semanticdb.arguments_modifier(args) + args.add_all("--", g.classpaths.srcs) args.set_param_file_format("multiline") args.use_param_file("@%s", use_always = True) diff --git a/rules/providers.bzl b/rules/providers.bzl index f602f488..ada2c31d 100644 --- a/rules/providers.bzl +++ b/rules/providers.bzl @@ -207,3 +207,11 @@ LabeledJars = provider( "values": "The preorder depset of label and jars.", }, ) + +SemanticDbInfo = provider( + doc = "Provided by the Scala rules when `semanticdb_bundle` is set to `False`.", + fields = { + "target_root": "The directory in which SemanticDB files were outputted.", + "semanticdb_files": "The SemanticDB files.", + }, +) diff --git a/tests/plugins/semanticdb/BUILD b/tests/plugins/semanticdb/BUILD index 293ed382..af8698e5 100644 --- a/tests/plugins/semanticdb/BUILD +++ b/tests/plugins/semanticdb/BUILD @@ -1,5 +1,6 @@ load("@rules_scala_annex//rules:scala.bzl", "configure_zinc_scala", "scala_library") load("@rules_scala_annex//rules/scala:workspace.bzl", "scala_2_13_version", "scala_3_version") +load(":rule.bzl", "read_semanticdb_info") configure_zinc_scala( name = "scala_2_13_with_semanticdb", @@ -39,9 +40,26 @@ scala_library( tags = ["manual"], ) +read_semanticdb_info( + name = "semanticdb-2_13-semanticdb-info", + scala_target = ":semanticdb-2_13", +) + scala_library( name = "semanticdb-3", srcs = glob(["*.scala"]), scala = ":scala_3_with_semanticdb", tags = ["manual"], ) + +read_semanticdb_info( + name = "semanticdb-3-semanticdb-info", + scala_target = ":semanticdb-3", +) + +scala_library( + name = "semanticdb-empty", + srcs = [], + scala = ":scala_2_13_with_semanticdb", + tags = ["manual"], +) diff --git a/tests/plugins/semanticdb/rule.bzl b/tests/plugins/semanticdb/rule.bzl new file mode 100644 index 00000000..7697ad3e --- /dev/null +++ b/tests/plugins/semanticdb/rule.bzl @@ -0,0 +1,25 @@ +load("@rules_scala_annex//rules:providers.bzl", "SemanticDbInfo") + +def _read_semanticdb_info_impl(ctx): + semanticdb_info = ctx.attr.scala_target[SemanticDbInfo] + output = ctx.actions.declare_file("{}.txt".format(ctx.label.name)) + + ctx.actions.write( + output, + json.encode({ + "targetRoot": semanticdb_info.target_root, + "semanticDbFiles": sorted([file.path for file in semanticdb_info.semanticdb_files]), + }), + ) + + return DefaultInfo(files = depset([output])) + +read_semanticdb_info = rule( + attrs = { + "scala_target": attr.label( + mandatory = True, + providers = [SemanticDbInfo], + ), + }, + implementation = _read_semanticdb_info_impl, +) diff --git a/tests/plugins/semanticdb/test b/tests/plugins/semanticdb/test index 261bd37e..fbcf104f 100755 --- a/tests/plugins/semanticdb/test +++ b/tests/plugins/semanticdb/test @@ -1,9 +1,11 @@ #!/bin/bash -e . "$(dirname "$0")"/../../common.sh +bazel_bin="$(bazel info bazel-bin)" + check_for_semanticdb_files() { for filename in "A.scala.semanticdb" "B.scala.semanticdb"; do - path="../../bazel-bin/plugins/semanticdb/_semanticdb/semanticdb-$1/META-INF/semanticdb/plugins/semanticdb/$filename" + path="../../bazel-bin/plugins/semanticdb/semanticdb-$1/semanticdb/META-INF/semanticdb/plugins/semanticdb/$filename" if [ ! -f "$path" ]; then echo "Error: $path doesn't exist" @@ -12,7 +14,22 @@ check_for_semanticdb_files() { done } +check_semanticdb_info() { + bazel build ":semanticdb-$1-semanticdb-info" + + output_path="$bazel_bin/plugins/semanticdb/semanticdb-$1-semanticdb-info.txt" + semanticdb_file_directory="bazel-out/k8-fastbuild/bin/plugins/semanticdb/semanticdb-$1/semanticdb/META-INF/semanticdb/plugins/semanticdb" + + [ "$(jq ".targetRoot" "$output_path")" = "\"plugins/semanticdb/semanticdb-$1/semanticdb\"" ] + [ "$(jq -c ".semanticDbFiles" "$output_path")" = "[\"$semanticdb_file_directory/A.scala.semanticdb\",\"$semanticdb_file_directory/B.scala.semanticdb\"]" ] +} + bazel build :semanticdb-2_13 check_for_semanticdb_files 2_13 +check_semanticdb_info 2_13 + bazel build :semanticdb-3 check_for_semanticdb_files '3' +check_semanticdb_info '3' + +bazel build :semanticdb-empty