Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SemanticDB support - cont. #1508

Merged
merged 42 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
5257a5e
Add SemanticDB support
olafurpg Jan 7, 2022
85c72b2
Semanticdb output works
ricochet1k Aug 18, 2022
5d3e64b
Merge pull request #1 from ThriveFinancial/mpeterson/semanticdb
aishfenton Jan 14, 2023
cdff7ff
fixup to make work with scalaopt phase
aishfenton Jan 15, 2023
713a6c8
Merge branch 'bazelbuild:master' into master
aishfenton Jan 22, 2023
83ffcc7
fixed tests
Jan 29, 2023
a2e9d40
added tests
Jan 29, 2023
74b1ab0
fix
Jan 29, 2023
72173be
Add Scalac3 semanticdb options
Jan 29, 2023
2cef51f
Fix scala3 failures
Jan 30, 2023
6f32c73
fix formating
Jan 30, 2023
e010a81
fix scala3 support; add scala3 test
Jan 31, 2023
779e5fa
clean up dead code
Jan 31, 2023
110442d
bump, to retrigger PR checks
Jan 31, 2023
8fcbbc9
bump scalameta; refactor version string
Feb 4, 2023
166c1f0
more scala 3.1, refactor version string
Feb 4, 2023
42265a5
Merge branch 'bazelbuild:master' into master
aishfenton Feb 4, 2023
993aa30
update hashes
Feb 4, 2023
cb07f15
bump scalafmt
Feb 4, 2023
39b10b2
Revert "bump, to retrigger PR checks"
Feb 4, 2023
f9bf085
Revert "Revert "bump, to retrigger PR checks""
Feb 4, 2023
3f9cf08
Revert "bump scalafmt"
Feb 4, 2023
c8b5a7e
Revert "update hashes"
Feb 4, 2023
b33b751
Revert "more scala 3.1, refactor version string"
Feb 4, 2023
73bdbce
Revert "bump scalameta; refactor version string"
Feb 4, 2023
fde8c33
Revert "Bump jmh version: 1.20 -> 1.36 (#1466)"
Feb 4, 2023
6bd7ad4
Bump jmh version: 1.20 -> 1.36 (#1466)
dkomanov Jan 30, 2023
4b1d96a
add to predefined outputs
Feb 6, 2023
0760c42
revert predefined output
Feb 6, 2023
9bdd57a
include semanticdb in class jar
Feb 12, 2023
9ddca47
Merge branch 'bazelbuild:master' into master
aishfenton Feb 12, 2023
8e4817e
Merge remote-tracking branch 'upstream/master'
Mar 5, 2023
7f9696e
fix merge
Mar 5, 2023
2e4b448
fix tests
Mar 5, 2023
b7c1231
lint
Mar 5, 2023
c041949
move to phases
Mar 6, 2023
c86b3ff
fix bug
Mar 6, 2023
54b4837
force rebuild
Mar 12, 2023
6de925d
lint fix
Mar 12, 2023
10793ae
Merge branch 'master' into semanticdb
crt-31 Aug 4, 2023
d721134
Semanticdb to use phase, and other improvements
crt-31 Aug 5, 2023
fe7111f
semanticdb: addressed pr review comments
crt-31 Sep 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,4 @@ Here's a (non-exhaustive) list of companies that use `rules_scala` in production
* [Twitter](https://twitter.com/)
* [VSCO](https://vsco.co)
* [Wix](https://www.wix.com/)

24 changes: 23 additions & 1 deletion docs/scala_toolchain.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ scala_register_toolchains()
Allows to configure dependencies lists by configuring <code>DepInfo</code> provider targets.
Currently supported dep ids: <code>scala_compile_classpath</code>,
<code>scala_library_classpath</code>, <code>scala_macro_classpath</code>, <code>scala_xml</code>,
<code>parser_combinators</code>.
<code>parser_combinators</code>
liucijus marked this conversation as resolved.
Show resolved Hide resolved
<code>scala_semanticdb</code>
</p>
</td>
</tr>
Expand Down Expand Up @@ -143,6 +144,27 @@ scala_register_toolchains()
List of target prefixes included for unused deps analysis. Exclude patetrns with '-'
</p>
</td>
</tr>
<tr>
<td><code>enable_semanticdb</code></td>
<td>
<p><code>Boolean; optional (default False)</code></p>
<p>
Enables semanticdb output.
</p>
</td>
</tr>
<tr>
<td><code>semanticdb_bundle_in_jar</code></td>
<td>
<p><code>Boolean; optional (default False)</code></p>
<p>
When False, *.semanticdb files are added to the filesystem in a directory.
</p>
<p>
When True, *.semanticdb files will be bundled inside the jar file.
</p>
</td>
</tr>
</tbody>
</table>
28 changes: 28 additions & 0 deletions examples/semanticdb/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
load("@io_bazel_rules_scala//scala:scala_toolchain.bzl", "scala_toolchain")
load("@io_bazel_rules_scala//scala:scala.bzl", "scala_binary", "scala_library")

scala_toolchain(
name = "semanticdb_toolchain_impl",
enable_semanticdb = True,
semanticdb_bundle_in_jar = False,
visibility = ["//visibility:public"],
)

toolchain(
name = "semanticdb_toolchain",
toolchain = "semanticdb_toolchain_impl",
toolchain_type = "@io_bazel_rules_scala//scala:toolchain_type",
visibility = ["//visibility:public"],
)

scala_library(
name = "hello_lib",
srcs = ["Foo.scala"],
)

scala_binary(
name = "hello",
srcs = ["Main.scala"],
main_class = "main",
deps = [":hello_lib"],
)
3 changes: 3 additions & 0 deletions examples/semanticdb/Foo.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Foo(){
def sayHello: String = "Hello!!"
}
7 changes: 7 additions & 0 deletions examples/semanticdb/Main.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

object main{
def main(args: Array[String]): Unit = {
val foo = new Foo()
println(foo.sayHello)
}
}
13 changes: 13 additions & 0 deletions examples/semanticdb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# scala_rules Semanticdb example

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.

In this example, note that a scala_toolchain with enable_semanticdb=True is setup in the BUILD file.

This command can be used to run the aspect (and not run the full build)

```
bazel build //... --aspects aspect.bzl%semanticdb_info_aspect --output_groups=json_output_file
liucijus marked this conversation as resolved.
Show resolved Hide resolved
```

The semanticdb_info.json file will be created for each target, and contains the semanticdb info for the target.
45 changes: 45 additions & 0 deletions examples/semanticdb/WORKSPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
workspace(name = "specs2_junit_repositories")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

skylib_version = "1.4.1"

http_archive(
name = "bazel_skylib",
sha256 = "b8a1527901774180afc798aeb28c4634bdccf19c4d98e7bdd1ce79d1fe9aaad7",
type = "tar.gz",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/{}/bazel-skylib-{}.tar.gz".format(skylib_version, skylib_version),
"https://github.com/bazelbuild/bazel-skylib/releases/download/{}/bazel-skylib-{}.tar.gz".format(skylib_version, skylib_version),
],
)

local_repository(
name = "io_bazel_rules_scala",
path = "../..",
)

load("@io_bazel_rules_scala//:scala_config.bzl", "scala_config")

scala_config(scala_version = "2.13.6")

load(
"@io_bazel_rules_scala//scala:scala.bzl",
"rules_scala_setup",
"rules_scala_toolchain_deps_repositories",
)

rules_scala_setup()

rules_scala_toolchain_deps_repositories(fetch_sources = True)

load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")

rules_proto_dependencies()

rules_proto_toolchains()

#Register and use the custom toolchain that has semanticdb enabled
register_toolchains(
"//:semanticdb_toolchain",
)
24 changes: 24 additions & 0 deletions examples/semanticdb/aspect.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#This aspect is an example of exposing semanticdb information for each target into a json file.
# An IDE could use a json file like this to consume the semanticdb data for each target.

load("@io_bazel_rules_scala//scala:semanticdb_provider.bzl", "SemanticdbInfo")

def semanticdb_info_aspect_impl(target, ctx):
if SemanticdbInfo in ctx.attr.dep:
output_struct = struct(
target_label = str(target.label),
semanticdb_target_root = target[SemanticdbInfo].target_root,
semanticdb_pluginjar = target[SemanticdbInfo].plugin_jar,
)

json_output_file = ctx.actions.declare_file("%s_semanticdb_info.json" % target.label.name)
ctx.actions.write(json_output_file, json.encode_indent(output_struct))

return [OutputGroupInfo(json_output_file = depset([json_output_file]))]
return None

semanticdb_info_aspect = aspect(
implementation = semanticdb_info_aspect_impl,
attr_aspects = ["deps"],
toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"],
)
9 changes: 9 additions & 0 deletions scala/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ _PARSER_COMBINATORS_DEPS = ["@io_bazel_rules_scala_scala_parser_combinators"]

_SCALA_XML_DEPS = ["@io_bazel_rules_scala_scala_xml"]

_SCALA_SEMANTICDB_DEPS = ["@org_scalameta_semanticdb_scalac"] if SCALA_MAJOR_VERSION.startswith("2") else []
liucijus marked this conversation as resolved.
Show resolved Hide resolved

setup_scala_toolchain(
name = "default_toolchain",
scala_compile_classpath = _SCALA_COMPILE_CLASSPATH_DEPS,
Expand Down Expand Up @@ -108,3 +110,10 @@ declare_deps_provider(
visibility = ["//visibility:public"],
deps = _PARSER_COMBINATORS_DEPS,
)

declare_deps_provider(
name = "scala_semanticdb_provider",
deps_id = "scala_semanticdb",
visibility = ["//visibility:public"],
deps = _SCALA_SEMANTICDB_DEPS,
)
1 change: 1 addition & 0 deletions scala/private/macros/scala_repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ ARTIFACT_IDS = [
"io_bazel_rules_scala_scala_reflect",
"io_bazel_rules_scala_scala_xml",
"io_bazel_rules_scala_scala_parser_combinators",
"org_scalameta_semanticdb_scalac",
] if SCALA_MAJOR_VERSION.startswith("2") else [
"io_bazel_rules_scala_scala_library",
"io_bazel_rules_scala_scala_compiler",
Expand Down
33 changes: 26 additions & 7 deletions scala/private/macros/setup_scala_toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ def setup_scala_toolchain(
scala_macro_classpath,
scala_xml_deps = None,
parser_combinators_deps = None,
scala_semanticdb_dep = None,
liucijus marked this conversation as resolved.
Show resolved Hide resolved
enable_semanticdb = False,
visibility = ["//visibility:public"],
**kwargs):
scala_xml_provider = "%s_scala_xml_provider" % name
parser_combinators_provider = "%s_parser_combinators_provider" % name
scala_compile_classpath_provider = "%s_scala_compile_classpath_provider" % name
scala_library_classpath_provider = "%s_scala_library_classpath_provider" % name
scala_macro_classpath_provider = "%s_scala_macro_classpath_provider" % name
scala_semanticdb_dep_provider = "%s_scala_semanticdb_dep_provider" % name

declare_deps_provider(
name = scala_compile_classpath_provider,
Expand Down Expand Up @@ -57,15 +60,31 @@ def setup_scala_toolchain(
else:
parser_combinators_provider = "@io_bazel_rules_scala//scala:parser_combinators_provider"

dep_providers = [
scala_xml_provider,
parser_combinators_provider,
scala_compile_classpath_provider,
scala_library_classpath_provider,
scala_macro_classpath_provider,
]

if enable_semanticdb == True:
if scala_semanticdb_dep != None:
declare_deps_provider(
name = scala_semanticdb_dep_provider,
deps_id = "scala_semanticdb",
deps = [scala_semanticdb_dep],
visibility = visibility,
)

dep_providers.append(scala_semanticdb_dep_provider)
else:
dep_providers.append("@io_bazel_rules_scala//scala:scala_semanticdb_provider")

scala_toolchain(
name = "%s_impl" % name,
dep_providers = [
scala_xml_provider,
parser_combinators_provider,
scala_compile_classpath_provider,
scala_library_classpath_provider,
scala_macro_classpath_provider,
],
dep_providers = dep_providers,
enable_semanticdb = enable_semanticdb,
visibility = visibility,
**kwargs
)
Expand Down
19 changes: 16 additions & 3 deletions scala/private/phases/phase_compile.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,18 @@ def _phase_compile(
jars2labels = p.collect_jars.jars2labels.jars_to_labels
deps_providers = p.collect_jars.deps_providers
default_classpath = p.scalac_provider.default_classpath
plugins = ctx.attr.plugins
additional_outputs = []
scalacopts = p.scalacopts

if (hasattr(p, "semanticdb")):
scalacopts += p.semanticdb.scalacopts
plugins = plugins + p.semanticdb.plugin
additional_outputs += p.semanticdb.outputs

out = _compile_or_empty(
ctx,
p.scalacopts,
scalacopts,
manifest,
jars,
srcjars,
Expand All @@ -144,6 +152,8 @@ def _phase_compile(
deps_providers,
default_classpath,
unused_dependency_checker_ignored_targets,
plugins,
additional_outputs,
)

# TODO: simplify the return values and use provider
Expand All @@ -169,7 +179,9 @@ def _compile_or_empty(
dependency_info,
deps_providers,
default_classpath,
unused_dependency_checker_ignored_targets):
unused_dependency_checker_ignored_targets,
plugins,
additional_outputs):
# We assume that if a srcjar is present, it is not empty
if len(ctx.files.srcs) + len(srcjars.to_list()) == 0:
_build_nosrc_jar(ctx)
Expand Down Expand Up @@ -200,7 +212,7 @@ def _compile_or_empty(
jars,
all_srcjars,
transitive_compile_jars,
ctx.attr.plugins,
plugins,
ctx.attr.resource_strip_prefix,
ctx.files.resources,
ctx.files.resource_jars,
Expand All @@ -212,6 +224,7 @@ def _compile_or_empty(
ctx.executable._scalac,
dependency_info,
unused_dependency_checker_ignored_targets,
additional_outputs,
)

# build ijar if needed
Expand Down
78 changes: 78 additions & 0 deletions scala/private/phases/phase_semanticdb.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
load(
"@io_bazel_rules_scala//scala/private/toolchain_deps:toolchain_deps.bzl",
"find_deps_info_on",
)
load("@io_bazel_rules_scala//scala:semanticdb_provider.bzl", "SemanticdbInfo")
load("@io_bazel_rules_scala_config//:config.bzl", "SCALA_MAJOR_VERSION")
load("@bazel_skylib//lib:paths.bzl", "paths")

def phase_semanticdb(ctx, p):
#semanticdb_bundle_in_jar feature: enables bundling the semanticdb files within the output jar.

#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.

#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.

toolchain = ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"]
toolchain_type_label = "@io_bazel_rules_scala//scala:toolchain_type"

if toolchain.enable_semanticdb == True:
scalacopts = []
semanticdb_deps = []
output_files = []
plugin_jar_path = ""

target_output_path = paths.dirname(ctx.outputs.jar.path)

semanticdb_intpath = "_scalac/" + ctx.label.name + "/classes" if toolchain.semanticdb_bundle_in_jar == True else "semanticdb/" + ctx.label.name

semanticdb_target_root = "%s/%s" % (target_output_path, semanticdb_intpath)

#declare all the semanticdb files
if (not toolchain.semanticdb_bundle_in_jar):
semanticdb_outpath = "META-INF/semanticdb"

for currSrc in ctx.files.srcs:
if currSrc.extension == "scala":
outputfilename = "%s/%s/%s.semanticdb" % (semanticdb_intpath, semanticdb_outpath, currSrc.path)
output_files.append(ctx.actions.declare_file(outputfilename))

if SCALA_MAJOR_VERSION.startswith("2"):
semanticdb_deps = find_deps_info_on(ctx, toolchain_type_label, "scala_semanticdb").deps

if len(semanticdb_deps) == 0:
fail("semanticdb enabled, but semanticdb plugin jar not specified in scala_toolchain")
if len(semanticdb_deps) != 1:
fail("more than one semanticdb plugin jar was specified in scala_toolchain. Expect a single semanticdb plugin jar")

plugin_jar_path = semanticdb_deps[0][JavaInfo].java_outputs[0].class_jar.path

scalacopts += [
#note: Xplugin parameter handled in scalacworker,
"-Yrangepos",
"-P:semanticdb:failures:error",
"-P:semanticdb:targetroot:" + semanticdb_target_root,
]
else:
#Note: In Scala3, semanticdb is built-in to compiler, so no need for plugin

scalacopts.append("-Ysemanticdb")

if toolchain.semanticdb_bundle_in_jar == False:
scalacopts.append("-semanticdb-target:" + semanticdb_target_root)

semanticdb_provider = SemanticdbInfo(
semanticdb_enabled = True,
target_root = None if toolchain.semanticdb_bundle_in_jar else semanticdb_target_root,
is_bundled_in_jar = toolchain.semanticdb_bundle_in_jar,
plugin_jar = plugin_jar_path,
)

return struct(
scalacopts = scalacopts,
plugin = semanticdb_deps,
outputs = output_files,
external_providers = {"SemanticdbInfo": semanticdb_provider},
)

return None
Loading