Skip to content

Commit ee6c91d

Browse files
crt-31olafurpgricochet1kaishfentonAish Fenton
authored
Add SemanticDB support - cont. (#1508)
* Add SemanticDB support * Semanticdb output works * fixup to make work with scalaopt phase * fixed tests * added tests * fix * Add Scalac3 semanticdb options * Fix scala3 failures * fix formating * fix scala3 support; add scala3 test * clean up dead code * bump, to retrigger PR checks * bump scalameta; refactor version string * more scala 3.1, refactor version string * update hashes * bump scalafmt * Revert "bump, to retrigger PR checks" This reverts commit 110442d. * Revert "Revert "bump, to retrigger PR checks"" This reverts commit 39b10b2. * Revert "bump scalafmt" This reverts commit cb07f15. * Revert "update hashes" This reverts commit 993aa30. * Revert "more scala 3.1, refactor version string" This reverts commit 166c1f0. * Revert "bump scalameta; refactor version string" This reverts commit 8fcbbc9. * Revert "Bump jmh version: 1.20 -> 1.36 (#1466)" This reverts commit 1e4df56. * Bump jmh version: 1.20 -> 1.36 (#1466) Also bump versions of its dependencies: * org.ow2.asm:asm: 6.1.1 -> 9.0 * net.sf.jopt-simple:jopt-simple: 4.6 -> 5.0.4 * add to predefined outputs * revert predefined output * include semanticdb in class jar * fix merge * fix tests * lint * move to phases * fix bug * force rebuild * lint fix * Semanticdb to use phase, and other improvements * semanticdb: addressed pr review comments - fixed semanticdb example - added prefix '_' to semanticdb output path - renamed certain vars to not prepend with "scala_" - changed depsprovider var name to be plural instead of singular --------- Co-authored-by: Ólafur Páll Geirsson <[email protected]> Co-authored-by: Matt Peterson <[email protected]> Co-authored-by: Aish <[email protected]> Co-authored-by: Aish Fenton <[email protected]> Co-authored-by: Dmitry Komanov <[email protected]>
1 parent 0391ef4 commit ee6c91d

37 files changed

+616
-20
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,4 @@ Here's a (non-exhaustive) list of companies that use `rules_scala` in production
283283
* [Twitter](https://twitter.com/)
284284
* [VSCO](https://vsco.co)
285285
* [Wix](https://www.wix.com/)
286+

docs/scala_toolchain.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ scala_register_toolchains()
7979
Allows to configure dependencies lists by configuring <code>DepInfo</code> provider targets.
8080
Currently supported dep ids: <code>scala_compile_classpath</code>,
8181
<code>scala_library_classpath</code>, <code>scala_macro_classpath</code>, <code>scala_xml</code>,
82-
<code>parser_combinators</code>.
82+
<code>parser_combinators</code>,
83+
<code>semanticdb</code>
8384
</p>
8485
</td>
8586
</tr>
@@ -143,6 +144,27 @@ scala_register_toolchains()
143144
List of target prefixes included for unused deps analysis. Exclude patetrns with '-'
144145
</p>
145146
</td>
147+
</tr>
148+
<tr>
149+
<td><code>enable_semanticdb</code></td>
150+
<td>
151+
<p><code>Boolean; optional (default False)</code></p>
152+
<p>
153+
Enables semanticdb output.
154+
</p>
155+
</td>
156+
</tr>
157+
<tr>
158+
<td><code>semanticdb_bundle_in_jar</code></td>
159+
<td>
160+
<p><code>Boolean; optional (default False)</code></p>
161+
<p>
162+
When False, *.semanticdb files are added to the filesystem in a directory.
163+
</p>
164+
<p>
165+
When True, *.semanticdb files will be bundled inside the jar file.
166+
</p>
167+
</td>
146168
</tr>
147169
</tbody>
148170
</table>

examples/semanticdb/BUILD

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
load("@io_bazel_rules_scala//scala:scala_toolchain.bzl", "scala_toolchain")
2+
load("@io_bazel_rules_scala//scala:scala.bzl", "scala_binary", "scala_library")
3+
4+
scala_toolchain(
5+
name = "semanticdb_toolchain_impl",
6+
enable_semanticdb = True,
7+
semanticdb_bundle_in_jar = False,
8+
visibility = ["//visibility:public"],
9+
)
10+
11+
toolchain(
12+
name = "semanticdb_toolchain",
13+
toolchain = "semanticdb_toolchain_impl",
14+
toolchain_type = "@io_bazel_rules_scala//scala:toolchain_type",
15+
visibility = ["//visibility:public"],
16+
)
17+
18+
scala_library(
19+
name = "hello_lib",
20+
srcs = ["Foo.scala"],
21+
)
22+
23+
scala_binary(
24+
name = "hello",
25+
srcs = ["Main.scala"],
26+
main_class = "main",
27+
deps = [":hello_lib"],
28+
)

examples/semanticdb/Foo.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class Foo(){
2+
def sayHello: String = "Hello!!"
3+
}

examples/semanticdb/Main.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
object main{
3+
def main(args: Array[String]): Unit = {
4+
val foo = new Foo()
5+
println(foo.sayHello)
6+
}
7+
}

examples/semanticdb/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# scala_rules Semanticdb example
2+
3+
This example demonstrates using an aspect to access the semanticdb info for one or more targets. In this example, an aspect is used to generate a json file that contains the semanticdb info that could be consumed by a consumer such as an IDE.
4+
5+
In this example, note that a scala_toolchain with enable_semanticdb=True is setup in the BUILD file.
6+
7+
This command can be used to run the aspect (and not run the full build)
8+
9+
```
10+
bazel build //... --aspects aspect.bzl%semanticdb_info_aspect --output_groups=json_output_file
11+
```
12+
13+
The semanticdb_info.json file will be created for each target, and contains the semanticdb info for the target.

examples/semanticdb/WORKSPACE

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
workspace(name = "specs2_junit_repositories")
2+
3+
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
4+
5+
skylib_version = "1.4.1"
6+
7+
http_archive(
8+
name = "bazel_skylib",
9+
sha256 = "b8a1527901774180afc798aeb28c4634bdccf19c4d98e7bdd1ce79d1fe9aaad7",
10+
type = "tar.gz",
11+
urls = [
12+
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/{}/bazel-skylib-{}.tar.gz".format(skylib_version, skylib_version),
13+
"https://github.com/bazelbuild/bazel-skylib/releases/download/{}/bazel-skylib-{}.tar.gz".format(skylib_version, skylib_version),
14+
],
15+
)
16+
17+
local_repository(
18+
name = "io_bazel_rules_scala",
19+
path = "../..",
20+
)
21+
22+
load("@io_bazel_rules_scala//:scala_config.bzl", "scala_config")
23+
24+
scala_config(scala_version = "2.13.6")
25+
26+
load(
27+
"@io_bazel_rules_scala//scala:scala.bzl",
28+
"rules_scala_setup",
29+
"rules_scala_toolchain_deps_repositories",
30+
)
31+
32+
rules_scala_setup()
33+
34+
rules_scala_toolchain_deps_repositories(fetch_sources = True)
35+
36+
load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
37+
38+
rules_proto_dependencies()
39+
40+
rules_proto_toolchains()
41+
42+
#Register and use the custom toolchain that has semanticdb enabled
43+
register_toolchains(
44+
"//:semanticdb_toolchain",
45+
)

examples/semanticdb/aspect.bzl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#This aspect is an example of exposing semanticdb information for each target into a json file.
2+
# An IDE could use a json file like this to consume the semanticdb data for each target.
3+
4+
load("@io_bazel_rules_scala//scala:semanticdb_provider.bzl", "SemanticdbInfo")
5+
6+
def semanticdb_info_aspect_impl(target, ctx):
7+
if SemanticdbInfo in target:
8+
output_struct = struct(
9+
target_label = str(target.label),
10+
semanticdb_target_root = target[SemanticdbInfo].target_root,
11+
semanticdb_pluginjar = target[SemanticdbInfo].plugin_jar,
12+
)
13+
14+
json_output_file = ctx.actions.declare_file("%s_semanticdb_info.json" % target.label.name)
15+
ctx.actions.write(json_output_file, json.encode_indent(output_struct))
16+
17+
return [OutputGroupInfo(json_output_file = depset([json_output_file]))]
18+
return []
19+
20+
semanticdb_info_aspect = aspect(
21+
implementation = semanticdb_info_aspect_impl,
22+
attr_aspects = ["deps"],
23+
toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"],
24+
)

scala/BUILD

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ _PARSER_COMBINATORS_DEPS = ["@io_bazel_rules_scala_scala_parser_combinators"]
3434

3535
_SCALA_XML_DEPS = ["@io_bazel_rules_scala_scala_xml"]
3636

37+
_SEMANTICDB_DEPS = ["@org_scalameta_semanticdb_scalac"] if SCALA_MAJOR_VERSION.startswith("2") else []
38+
3739
setup_scala_toolchain(
3840
name = "default_toolchain",
3941
scala_compile_classpath = _SCALA_COMPILE_CLASSPATH_DEPS,
@@ -108,3 +110,10 @@ declare_deps_provider(
108110
visibility = ["//visibility:public"],
109111
deps = _PARSER_COMBINATORS_DEPS,
110112
)
113+
114+
declare_deps_provider(
115+
name = "semanticdb_provider",
116+
deps_id = "semanticdb",
117+
visibility = ["//visibility:public"],
118+
deps = _SEMANTICDB_DEPS,
119+
)

scala/private/macros/scala_repositories.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ ARTIFACT_IDS = [
128128
"io_bazel_rules_scala_scala_reflect",
129129
"io_bazel_rules_scala_scala_xml",
130130
"io_bazel_rules_scala_scala_parser_combinators",
131+
"org_scalameta_semanticdb_scalac",
131132
] if SCALA_MAJOR_VERSION.startswith("2") else [
132133
"io_bazel_rules_scala_scala_library",
133134
"io_bazel_rules_scala_scala_compiler",

scala/private/macros/setup_scala_toolchain.bzl

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ def setup_scala_toolchain(
88
scala_macro_classpath,
99
scala_xml_deps = None,
1010
parser_combinators_deps = None,
11+
semanticdb_deps = None,
12+
enable_semanticdb = False,
1113
visibility = ["//visibility:public"],
1214
**kwargs):
1315
scala_xml_provider = "%s_scala_xml_provider" % name
1416
parser_combinators_provider = "%s_parser_combinators_provider" % name
1517
scala_compile_classpath_provider = "%s_scala_compile_classpath_provider" % name
1618
scala_library_classpath_provider = "%s_scala_library_classpath_provider" % name
1719
scala_macro_classpath_provider = "%s_scala_macro_classpath_provider" % name
20+
semanticdb_deps_provider = "%s_semanticdb_deps_provider" % name
1821

1922
declare_deps_provider(
2023
name = scala_compile_classpath_provider,
@@ -57,15 +60,31 @@ def setup_scala_toolchain(
5760
else:
5861
parser_combinators_provider = "@io_bazel_rules_scala//scala:parser_combinators_provider"
5962

63+
dep_providers = [
64+
scala_xml_provider,
65+
parser_combinators_provider,
66+
scala_compile_classpath_provider,
67+
scala_library_classpath_provider,
68+
scala_macro_classpath_provider,
69+
]
70+
71+
if enable_semanticdb == True:
72+
if semanticdb_deps != None:
73+
declare_deps_provider(
74+
name = semanticdb_deps_provider,
75+
deps_id = "semanticdb",
76+
deps = [semanticdb_deps],
77+
visibility = visibility,
78+
)
79+
80+
dep_providers.append(semanticdb_deps_provider)
81+
else:
82+
dep_providers.append("@io_bazel_rules_scala//scala:semanticdb_provider")
83+
6084
scala_toolchain(
6185
name = "%s_impl" % name,
62-
dep_providers = [
63-
scala_xml_provider,
64-
parser_combinators_provider,
65-
scala_compile_classpath_provider,
66-
scala_library_classpath_provider,
67-
scala_macro_classpath_provider,
68-
],
86+
dep_providers = dep_providers,
87+
enable_semanticdb = enable_semanticdb,
6988
visibility = visibility,
7089
**kwargs
7190
)

scala/private/phases/phase_compile.bzl

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,18 @@ def _phase_compile(
129129
jars2labels = p.collect_jars.jars2labels.jars_to_labels
130130
deps_providers = p.collect_jars.deps_providers
131131
default_classpath = p.scalac_provider.default_classpath
132+
plugins = ctx.attr.plugins
133+
additional_outputs = []
134+
scalacopts = p.scalacopts
135+
136+
if (hasattr(p, "semanticdb")):
137+
scalacopts += p.semanticdb.scalacopts
138+
plugins = plugins + p.semanticdb.plugin
139+
additional_outputs += p.semanticdb.outputs
132140

133141
out = _compile_or_empty(
134142
ctx,
135-
p.scalacopts,
143+
scalacopts,
136144
manifest,
137145
jars,
138146
srcjars,
@@ -144,6 +152,8 @@ def _phase_compile(
144152
deps_providers,
145153
default_classpath,
146154
unused_dependency_checker_ignored_targets,
155+
plugins,
156+
additional_outputs,
147157
)
148158

149159
# TODO: simplify the return values and use provider
@@ -169,7 +179,9 @@ def _compile_or_empty(
169179
dependency_info,
170180
deps_providers,
171181
default_classpath,
172-
unused_dependency_checker_ignored_targets):
182+
unused_dependency_checker_ignored_targets,
183+
plugins,
184+
additional_outputs):
173185
# We assume that if a srcjar is present, it is not empty
174186
if len(ctx.files.srcs) + len(srcjars.to_list()) == 0:
175187
_build_nosrc_jar(ctx)
@@ -200,7 +212,7 @@ def _compile_or_empty(
200212
jars,
201213
all_srcjars,
202214
transitive_compile_jars,
203-
ctx.attr.plugins,
215+
plugins,
204216
ctx.attr.resource_strip_prefix,
205217
ctx.files.resources,
206218
ctx.files.resource_jars,
@@ -212,6 +224,7 @@ def _compile_or_empty(
212224
ctx.executable._scalac,
213225
dependency_info,
214226
unused_dependency_checker_ignored_targets,
227+
additional_outputs,
215228
)
216229

217230
# build ijar if needed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
load(
2+
"@io_bazel_rules_scala//scala/private/toolchain_deps:toolchain_deps.bzl",
3+
"find_deps_info_on",
4+
)
5+
load("@io_bazel_rules_scala//scala:semanticdb_provider.bzl", "SemanticdbInfo")
6+
load("@io_bazel_rules_scala_config//:config.bzl", "SCALA_MAJOR_VERSION")
7+
load("@bazel_skylib//lib:paths.bzl", "paths")
8+
9+
def phase_semanticdb(ctx, p):
10+
#semanticdb_bundle_in_jar feature: enables bundling the semanticdb files within the output jar.
11+
12+
#Scala 2: Uses the semanticdb compiler plugin. Will output semanticdb files into the specified 'targetroot' which defaults to be under the '_scalac/classes' dir. When targetroot is under the _scalac/classes dir scalac bundles the *.semanticdb files into the jar.
13+
14+
#Scala3: Semanticdb is built into scalac. Currently, if semanticdb-target is used, the semanticdb files are written and not bundled, otherwise, the semanticdb files are not written as files and only available inside the jar.
15+
16+
toolchain = ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"]
17+
toolchain_type_label = "@io_bazel_rules_scala//scala:toolchain_type"
18+
19+
if toolchain.enable_semanticdb == True:
20+
scalacopts = []
21+
semanticdb_deps = []
22+
output_files = []
23+
plugin_jar_path = ""
24+
25+
target_output_path = paths.dirname(ctx.outputs.jar.path)
26+
27+
semanticdb_intpath = "_scalac/" + ctx.label.name + "/classes" if toolchain.semanticdb_bundle_in_jar == True else "_semanticdb/" + ctx.label.name
28+
29+
semanticdb_target_root = "%s/%s" % (target_output_path, semanticdb_intpath)
30+
31+
#declare all the semanticdb files
32+
if (not toolchain.semanticdb_bundle_in_jar):
33+
semanticdb_outpath = "META-INF/semanticdb"
34+
35+
for currSrc in ctx.files.srcs:
36+
if currSrc.extension == "scala":
37+
outputfilename = "%s/%s/%s.semanticdb" % (semanticdb_intpath, semanticdb_outpath, currSrc.path)
38+
output_files.append(ctx.actions.declare_file(outputfilename))
39+
40+
if SCALA_MAJOR_VERSION.startswith("2"):
41+
semanticdb_deps = find_deps_info_on(ctx, toolchain_type_label, "semanticdb").deps
42+
43+
if len(semanticdb_deps) == 0:
44+
fail("semanticdb enabled, but semanticdb plugin jar not specified in scala_toolchain")
45+
if len(semanticdb_deps) != 1:
46+
fail("more than one semanticdb plugin jar was specified in scala_toolchain. Expect a single semanticdb plugin jar")
47+
48+
plugin_jar_path = semanticdb_deps[0][JavaInfo].java_outputs[0].class_jar.path
49+
50+
scalacopts += [
51+
#note: Xplugin parameter handled in scalacworker,
52+
"-Yrangepos",
53+
"-P:semanticdb:failures:error",
54+
"-P:semanticdb:targetroot:" + semanticdb_target_root,
55+
]
56+
else:
57+
#Note: In Scala3, semanticdb is built-in to compiler, so no need for plugin
58+
59+
scalacopts.append("-Ysemanticdb")
60+
61+
if toolchain.semanticdb_bundle_in_jar == False:
62+
scalacopts.append("-semanticdb-target:" + semanticdb_target_root)
63+
64+
semanticdb_provider = SemanticdbInfo(
65+
semanticdb_enabled = True,
66+
target_root = None if toolchain.semanticdb_bundle_in_jar else semanticdb_target_root,
67+
is_bundled_in_jar = toolchain.semanticdb_bundle_in_jar,
68+
plugin_jar = plugin_jar_path,
69+
)
70+
71+
return struct(
72+
scalacopts = scalacopts,
73+
plugin = semanticdb_deps,
74+
outputs = output_files,
75+
external_providers = {"SemanticdbInfo": semanticdb_provider},
76+
)
77+
78+
return None

0 commit comments

Comments
 (0)