diff --git a/docs/newdocs/phases.md b/docs/newdocs/phases.md index 84e9d37a..82cbc938 100644 --- a/docs/newdocs/phases.md +++ b/docs/newdocs/phases.md @@ -30,4 +30,4 @@ _NOTE: This is a work in progress_ Bazel scala annex allows users to customize `scala_binary`, `scala_library`, and `scala_test` by adding/replacing [phases](#phases). Each of these rules has a corresponding `make_(*extras)` method which takes as arguments a list of of extra config items. -For an example of this in action, see [/rules/scala_with_scalafmt.bzl](/rules/scala_with_scalafmt.bzl) and [/rules/scalafmt/ext.blz](/rules/scalafmt/ext.bzl) \ No newline at end of file +For an example of this in action, see [/rules/scala_with_scalafmt.bzl](/rules/scala_with_scalafmt.bzl) and [/rules/scalafmt/ext.blz](/rules/scalafmt/ext.bzl) diff --git a/rules/private/phases.bzl b/rules/private/phases.bzl index b96f36f6..a5167edd 100644 --- a/rules/private/phases.bzl +++ b/rules/private/phases.bzl @@ -11,6 +11,7 @@ load(":phases/phase_library_defaultinfo.bzl", _phase_library_defaultinfo = "phas load(":phases/phase_noop.bzl", _phase_noop = "phase_noop") load(":phases/phase_resources.bzl", _phase_resources = "phase_resources") load(":phases/phase_scalafmt_nondefault_outputs.bzl", _phase_scalafmt_nondefault_outputs = "phase_scalafmt_nondefault_outputs") +load(":phases/phase_semanticdb.bzl", _phase_semanticdb = "phase_semanticdb") load(":phases/phase_singlejar.bzl", _phase_singlejar = "phase_singlejar") load(":phases/phase_test_launcher.bzl", _phase_test_launcher = "phase_test_launcher") load(":phases/phase_zinc_compile.bzl", _phase_zinc_compile = "phase_zinc_compile") @@ -20,6 +21,10 @@ adjust_phases = _adjust_phases run_phases = _run_phases +phase_binary_deployjar = _phase_binary_deployjar + +phase_binary_launcher = _phase_binary_launcher + phase_bootstrap_compile = _phase_bootstrap_compile phase_classpaths = _phase_classpaths @@ -30,24 +35,22 @@ phase_coverage_jacoco = _phase_coverage_jacoco phase_ijinfo = _phase_ijinfo -phase_noop = _phase_noop +phase_javainfo = _phase_javainfo -phase_resources = _phase_resources +phase_library_defaultinfo = _phase_library_defaultinfo -phase_zinc_compile = _phase_zinc_compile +phase_noop = _phase_noop -phase_zinc_depscheck = _phase_zinc_depscheck +phase_resources = _phase_resources -phase_javainfo = _phase_javainfo +phase_scalafmt_nondefault_outputs = _phase_scalafmt_nondefault_outputs -phase_library_defaultinfo = _phase_library_defaultinfo +phase_semanticdb = _phase_semanticdb phase_singlejar = _phase_singlejar -phase_binary_deployjar = _phase_binary_deployjar - -phase_binary_launcher = _phase_binary_launcher +phase_test_launcher = _phase_test_launcher -phase_scalafmt_nondefault_outputs = _phase_scalafmt_nondefault_outputs +phase_zinc_compile = _phase_zinc_compile -phase_test_launcher = _phase_test_launcher +phase_zinc_depscheck = _phase_zinc_depscheck diff --git a/rules/private/phases/phase_semanticdb.bzl b/rules/private/phases/phase_semanticdb.bzl new file mode 100644 index 00000000..6553d682 --- /dev/null +++ b/rules/private/phases/phase_semanticdb.bzl @@ -0,0 +1,42 @@ +load("@bazel_skylib//lib:paths.bzl", "paths") +load("@rules_scala_annex//rules:providers.bzl", _ScalaConfiguration = "ScalaConfiguration") + +# +# PHASE: semanticdb +# +# Configures the compiler to output SemanticDB metadata. Note that this phase won't work without the +# SemanticDB compiler plugin being enabled. +# +def phase_semanticdb(ctx, g): + scala_configuration = ctx.attr.scala[_ScalaConfiguration] + + if scala_configuration.semanticdb_bundle: + return struct(outputs = [], scalacopts = []) + + 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, + "META-INF", + "semanticdb", + "{}.semanticdb".format(source.path), + ) + + outputs.append(ctx.actions.declare_file(output_filename)) + + 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", + ] + + return struct(outputs = outputs, scalacopts = scalacopts) diff --git a/rules/private/phases/phase_zinc_compile.bzl b/rules/private/phases/phase_zinc_compile.bzl index 1823fd49..ae5c4120 100644 --- a/rules/private/phases/phase_zinc_compile.bzl +++ b/rules/private/phases/phase_zinc_compile.bzl @@ -37,6 +37,7 @@ def phase_zinc_compile(ctx, g): ] zincs = [dep[_ZincInfo] for dep in ctx.attr.deps if _ZincInfo in dep] + common_scalacopts = ctx.attr.scalacopts + g.semanticdb.scalacopts args = ctx.actions.args() @@ -45,7 +46,7 @@ def phase_zinc_compile(ctx, g): args.add_all("--compiler_classpath", g.classpaths.compiler) args.add_all("--classpath", g.classpaths.compile) args.add_all(scala_configuration.global_scalacopts, format_each = "--compiler_option=%s") - args.add_all(ctx.attr.scalacopts, format_each = "--compiler_option=%s") + args.add_all(common_scalacopts, format_each = "--compiler_option=%s") args.add_all(javacopts, format_each = "--java_compiler_option=%s") args.add(ctx.label, format = "--label=%s") args.add("--main_manifest", mains_file) @@ -73,7 +74,14 @@ def phase_zinc_compile(ctx, g): ] + [zinc.deps_files for zinc in zincs], ) - outputs = [g.classpaths.jar, mains_file, analysis_store, analysis_store_text, used, tmp] + outputs = [ + g.classpaths.jar, + mains_file, + analysis_store, + analysis_store_text, + used, + tmp, + ] + g.semanticdb.outputs execution_requirements_tags = { "supports-multiplex-workers": "1", diff --git a/rules/providers.bzl b/rules/providers.bzl index 4247f331..f602f488 100644 --- a/rules/providers.bzl +++ b/rules/providers.bzl @@ -7,12 +7,13 @@ load( ScalaConfiguration = provider( doc = "Scala compile-time and runtime configuration", fields = { - "version": "The Scala full version.", "compiler_classpath": "The compiler classpath.", - "runtime_classpath": "The runtime classpath.", "global_plugins": "Globally enabled compiler plugins", "global_scalacopts": "Globally enabled compiler options", + "runtime_classpath": "The runtime classpath.", + "semanticdb_bundle": "Whether to bundle SemanticDB files in the resulting JAR. Note that in Scala 2, this requires the SemanticDB compiler plugin.", "use_ijar": "Whether to use ijars for this Scala compiler", + "version": "The Scala full version.", }, ) @@ -24,18 +25,14 @@ def _declare_scala_configuration_implementation(ctx): global_plugins = ctx.attr.global_plugins, global_scalacopts = ctx.attr.global_scalacopts, runtime_classpath = ctx.attr.runtime_classpath, - version = ctx.attr.version, + semanticdb_bundle = ctx.attr.semanticdb_bundle, use_ijar = ctx.attr.use_ijar, + version = ctx.attr.version, ), ] declare_scala_configuration = rule( attrs = { - "version": attr.string(mandatory = True), - "runtime_classpath": attr.label_list( - mandatory = True, - providers = [JavaInfo], - ), "compiler_classpath": attr.label_list( mandatory = True, providers = [JavaInfo], @@ -47,6 +44,15 @@ declare_scala_configuration = rule( "global_scalacopts": attr.string_list( doc = "Scalac options that will always be enabled.", ), + "runtime_classpath": attr.label_list( + mandatory = True, + providers = [JavaInfo], + ), + "semanticdb_bundle": attr.bool( + default = True, + doc = "Whether to bundle SemanticDB files in the resulting JAR. Note that in Scala 2, this requires the SemanticDB compiler plugin.", + ), + "version": attr.string(mandatory = True), }, doc = "Creates a `ScalaConfiguration`.", implementation = _declare_scala_configuration_implementation, diff --git a/rules/scala.bzl b/rules/scala.bzl index 58cefd13..60e2f0b8 100644 --- a/rules/scala.bzl +++ b/rules/scala.bzl @@ -484,15 +484,10 @@ Generates Scaladocs. configure_bootstrap_scala = rule( attrs = { - "version": attr.string(mandatory = True), "compiler_classpath": attr.label_list( mandatory = True, providers = [JavaInfo], ), - "runtime_classpath": attr.label_list( - mandatory = True, - providers = [JavaInfo], - ), "global_plugins": attr.label_list( doc = "Scalac plugins that will always be enabled.", providers = [JavaInfo], @@ -500,29 +495,35 @@ configure_bootstrap_scala = rule( "global_scalacopts": attr.string_list( doc = "Scalac options that will always be enabled.", ), + "runtime_classpath": attr.label_list( + mandatory = True, + providers = [JavaInfo], + ), + "semanticdb_bundle": attr.bool( + default = True, + doc = "Whether to bundle SemanticDB files in the resulting JAR. Note that in Scala 2, this requires the SemanticDB compiler plugin.", + ), "use_ijar": attr.bool( doc = "Whether to use ijars for this compiler.", default = True, ), + "version": attr.string(mandatory = True), }, implementation = _configure_bootstrap_scala_implementation, ) _configure_zinc_scala = rule( attrs = { - "version": attr.string(mandatory = True), - "runtime_classpath": attr.label_list( + "compiler_bridge": attr.label( + allow_single_file = True, mandatory = True, - providers = [JavaInfo], ), "compiler_classpath": attr.label_list( mandatory = True, providers = [JavaInfo], ), - "compiler_bridge": attr.label( - allow_single_file = True, - mandatory = True, - ), + "deps_direct": attr.string(default = "error"), + "deps_used": attr.string(default = "error"), "global_plugins": attr.label_list( doc = "Scalac plugins that will always be enabled.", providers = [JavaInfo], @@ -530,19 +531,32 @@ _configure_zinc_scala = rule( "global_scalacopts": attr.string_list( doc = "Scalac options that will always be enabled.", ), + "incremental": attr.bool( + doc = "Whether Zinc's incremental compilation will be available for this Zinc compiler. If True, this requires additional configuration to use incremental compilation.", + default = False, + ), "log_level": attr.string( doc = "Compiler log level", default = "warn", ), + "runtime_classpath": attr.label_list( + mandatory = True, + providers = [JavaInfo], + ), + "semanticdb_bundle": attr.bool( + default = True, + doc = "Whether to bundle SemanticDB files in the resulting JAR. Note that in Scala 2, this requires the SemanticDB compiler plugin.", + ), "use_ijar": attr.bool( doc = "Whether to use ijars for this compiler.", default = True, ), - "deps_direct": attr.string(default = "error"), - "deps_used": attr.string(default = "error"), - "incremental": attr.bool( - doc = "Whether Zinc's incremental compilation will be available for this Zinc compiler. If True, this requires additional configuration to use incremental compilation.", - default = False, + "version": attr.string(mandatory = True), + "_code_coverage_instrumentation_worker": attr.label( + default = "@rules_scala_annex//src/main/scala/higherkindness/rules_scala/workers/jacoco/instrumenter", + allow_files = True, + executable = True, + cfg = "host", ), "_compile_worker": attr.label( default = "@rules_scala_annex//src/main/scala/higherkindness/rules_scala/workers/zinc/compile", @@ -556,12 +570,6 @@ _configure_zinc_scala = rule( executable = True, cfg = "host", ), - "_code_coverage_instrumentation_worker": attr.label( - default = "@rules_scala_annex//src/main/scala/higherkindness/rules_scala/workers/jacoco/instrumenter", - allow_files = True, - executable = True, - cfg = "host", - ), }, implementation = _configure_zinc_scala_implementation, ) diff --git a/rules/scala/private/provider.bzl b/rules/scala/private/provider.bzl index 10ee6d73..c18a11f1 100644 --- a/rules/scala/private/provider.bzl +++ b/rules/scala/private/provider.bzl @@ -9,6 +9,7 @@ load( load( "//rules/private:phases.bzl", _phase_bootstrap_compile = "phase_bootstrap_compile", + _phase_semanticdb = "phase_semanticdb", _phase_zinc_compile = "phase_zinc_compile", _phase_zinc_depscheck = "phase_zinc_depscheck", ) @@ -20,8 +21,9 @@ def configure_bootstrap_scala_implementation(ctx): global_plugins = ctx.attr.global_plugins, global_scalacopts = ctx.attr.global_scalacopts, runtime_classpath = ctx.attr.runtime_classpath, - version = ctx.attr.version, + semanticdb_bundle = ctx.attr.semanticdb_bundle, use_ijar = ctx.attr.use_ijar, + version = ctx.attr.version, ), _ScalaRulePhase( phases = [ @@ -37,8 +39,9 @@ def configure_zinc_scala_implementation(ctx): global_plugins = ctx.attr.global_plugins, global_scalacopts = ctx.attr.global_scalacopts, runtime_classpath = ctx.attr.runtime_classpath, - version = ctx.attr.version, + semanticdb_bundle = ctx.attr.semanticdb_bundle, use_ijar = ctx.attr.use_ijar, + version = ctx.attr.version, ), _CodeCoverageConfiguration( instrumentation_worker = ctx.attr._code_coverage_instrumentation_worker, @@ -56,6 +59,7 @@ def configure_zinc_scala_implementation(ctx): ), _ScalaRulePhase( phases = [ + ("+", "classpaths", "semanticdb", _phase_semanticdb), ("=", "compile", "compile", _phase_zinc_compile), ("+", "compile", "depscheck", _phase_zinc_depscheck), ], diff --git a/tests/annex_test_install.json b/tests/annex_test_install.json index 092dfa77..965a746f 100644 --- a/tests/annex_test_install.json +++ b/tests/annex_test_install.json @@ -1,8 +1,8 @@ { "dependency_tree": { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": -1203700734, - "__RESOLVED_ARTIFACTS_HASH": 1444768052, + "__INPUT_ARTIFACTS_HASH": 13201521, + "__RESOLVED_ARTIFACTS_HASH": 787994482, "conflict_resolution": { "com.google.protobuf:protobuf-java:3.11.4": "com.google.protobuf:protobuf-java:3.15.8", "com.thesamet.scalapb:lenses_2.13:0.9.0": "com.thesamet.scalapb:lenses_2.13:0.11.4", @@ -605,6 +605,40 @@ "sha256": "1e144d19da246a2401b4c7d1254be4e9b599f2f03a55bb44e2c23e9a3ddcbb50", "url": "https://repo.maven.apache.org/maven2/org/scalactic/scalactic_2.13/3.2.19/scalactic_2.13-3.2.19-sources.jar" }, + { + "coord": "org.scalameta:semanticdb-scalac_2.13.14:4.9.9", + "dependencies": [ + "org.scala-lang:scala-library:2.13.14" + ], + "directDependencies": [ + "org.scala-lang:scala-library:2.13.14" + ], + "file": "v1/https/repo.maven.apache.org/maven2/org/scalameta/semanticdb-scalac_2.13.14/4.9.9/semanticdb-scalac_2.13.14-4.9.9.jar", + "mirror_urls": [ + "https://repo.maven.apache.org/maven2/org/scalameta/semanticdb-scalac_2.13.14/4.9.9/semanticdb-scalac_2.13.14-4.9.9.jar", + "https://maven-central.storage-download.googleapis.com/maven2/org/scalameta/semanticdb-scalac_2.13.14/4.9.9/semanticdb-scalac_2.13.14-4.9.9.jar", + "https://mirror.bazel.build/repo1.maven.org/maven2/org/scalameta/semanticdb-scalac_2.13.14/4.9.9/semanticdb-scalac_2.13.14-4.9.9.jar" + ], + "sha256": "c5269b5b7264fc5082357acdb4d9cf6790f49b4195f127878bd4f46659e3c52b", + "url": "https://repo.maven.apache.org/maven2/org/scalameta/semanticdb-scalac_2.13.14/4.9.9/semanticdb-scalac_2.13.14-4.9.9.jar" + }, + { + "coord": "org.scalameta:semanticdb-scalac_2.13.14:jar:sources:4.9.9", + "dependencies": [ + "org.scala-lang:scala-library:jar:sources:2.13.14" + ], + "directDependencies": [ + "org.scala-lang:scala-library:jar:sources:2.13.14" + ], + "file": "v1/https/repo.maven.apache.org/maven2/org/scalameta/semanticdb-scalac_2.13.14/4.9.9/semanticdb-scalac_2.13.14-4.9.9-sources.jar", + "mirror_urls": [ + "https://repo.maven.apache.org/maven2/org/scalameta/semanticdb-scalac_2.13.14/4.9.9/semanticdb-scalac_2.13.14-4.9.9-sources.jar", + "https://maven-central.storage-download.googleapis.com/maven2/org/scalameta/semanticdb-scalac_2.13.14/4.9.9/semanticdb-scalac_2.13.14-4.9.9-sources.jar", + "https://mirror.bazel.build/repo1.maven.org/maven2/org/scalameta/semanticdb-scalac_2.13.14/4.9.9/semanticdb-scalac_2.13.14-4.9.9-sources.jar" + ], + "sha256": "feb2dafb9d70a854d38886d32164614e8e251c071610a1964a18bfa6cf4039db", + "url": "https://repo.maven.apache.org/maven2/org/scalameta/semanticdb-scalac_2.13.14/4.9.9/semanticdb-scalac_2.13.14-4.9.9-sources.jar" + }, { "coord": "org.scalatest:scalatest-compatible:3.2.19", "dependencies": [], diff --git a/tests/plugins/semanticdb/A.scala b/tests/plugins/semanticdb/A.scala new file mode 100644 index 00000000..83d15dc7 --- /dev/null +++ b/tests/plugins/semanticdb/A.scala @@ -0,0 +1 @@ +class A diff --git a/tests/plugins/semanticdb/B.scala b/tests/plugins/semanticdb/B.scala new file mode 100644 index 00000000..179f0d27 --- /dev/null +++ b/tests/plugins/semanticdb/B.scala @@ -0,0 +1 @@ +class B diff --git a/tests/plugins/semanticdb/BUILD b/tests/plugins/semanticdb/BUILD new file mode 100644 index 00000000..293ed382 --- /dev/null +++ b/tests/plugins/semanticdb/BUILD @@ -0,0 +1,47 @@ +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") + +configure_zinc_scala( + name = "scala_2_13_with_semanticdb", + compiler_bridge = "@annex//:org_scala_sbt_compiler_bridge_2_13", + compiler_classpath = [ + "@annex//:org_scala_lang_scala_compiler", + "@annex//:org_scala_lang_scala_reflect", + "@annex//:org_scala_lang_scala_library", + ], + global_plugins = ["@annex_test//:org_scalameta_semanticdb_scalac_2_13_14"], + runtime_classpath = ["@annex//:org_scala_lang_scala_library"], + semanticdb_bundle = False, + version = scala_2_13_version, +) + +configure_zinc_scala( + name = "scala_3_with_semanticdb", + compiler_bridge = "@annex//:org_scala_lang_scala3_sbt_bridge", + compiler_classpath = [ + "@annex//:org_scala_lang_scala3_compiler_3", + "@annex//:org_scala_lang_scala3_library_3", + ], + runtime_classpath = [ + "@annex//:org_scala_lang_scala3_library_3", + "@annex//:org_scala_lang_scala3_interfaces", + "@annex//:org_scala_lang_tasty_core_3", + ], + semanticdb_bundle = False, + use_ijar = True, + version = scala_3_version, +) + +scala_library( + name = "semanticdb-2_13", + srcs = glob(["*.scala"]), + scala = ":scala_2_13_with_semanticdb", + tags = ["manual"], +) + +scala_library( + name = "semanticdb-3", + srcs = glob(["*.scala"]), + scala = ":scala_3_with_semanticdb", + tags = ["manual"], +) diff --git a/tests/plugins/semanticdb/test b/tests/plugins/semanticdb/test new file mode 100755 index 00000000..261bd37e --- /dev/null +++ b/tests/plugins/semanticdb/test @@ -0,0 +1,18 @@ +#!/bin/bash -e +. "$(dirname "$0")"/../../common.sh + +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" + + if [ ! -f "$path" ]; then + echo "Error: $path doesn't exist" + exit 1 + fi + done +} + +bazel build :semanticdb-2_13 +check_for_semanticdb_files 2_13 +bazel build :semanticdb-3 +check_for_semanticdb_files '3' diff --git a/tests/workspace.bzl b/tests/workspace.bzl index 4c0842ab..5f91b2d8 100644 --- a/tests/workspace.bzl +++ b/tests/workspace.bzl @@ -9,6 +9,7 @@ def test_artifacts(): "org.scala-sbt:compiler-interface:1.10.0", "org.scalacheck:scalacheck_2.13:1.18.0", "org.scalactic:scalactic_2.13:3.2.19", + "org.scalameta:semanticdb-scalac_2.13.14:4.9.9", "org.scalatest:scalatest_2.13:3.2.19", "org.specs2:specs2-common_2.13:4.20.7", "org.specs2:specs2-core_2.13:4.20.7",