-
Notifications
You must be signed in to change notification settings - Fork 278
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 scala_doc rule #760
Add scala_doc rule #760
Changes from 9 commits
a841c81
4d1597c
3cf7a25
1691e8d
85450ee
3fb233e
15ef44a
9f3c69e
a381a43
10b0af4
48fc3d4
c3197a4
25c7b48
87cd04f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# scala_doc | ||
|
||
```python | ||
scala_binary( | ||
name, | ||
deps, | ||
) | ||
``` | ||
|
||
`scala_doc` generates [Scaladoc](https://docs.scala-lang.org/style/scaladoc.html) for sources | ||
for targets, including sources from upstream deps. Readily hostable HTML is written to a `name.html` output folder. | ||
|
||
Scaladoc can be somewhat slow to build. In that case, you can tell Bazel to build this target manually, | ||
i.e. only when named explicitly and not through wildcards: `tags = ["manual"]`. | ||
|
||
## Example | ||
|
||
```python | ||
scala_doc( | ||
name = "scala_docs", | ||
tags = ["manual"], | ||
deps = [ | ||
":target1", | ||
":target2", | ||
":anothertarget", | ||
], | ||
) | ||
|
||
# Use pkg_tar to tarball up | ||
# https://docs.bazel.build/versions/master/be/pkg.html#pkg_tar | ||
pkg_tar( | ||
name = "scala_docs_archive", | ||
srcs = [":scala_docs"], | ||
extension = "tar.gz", | ||
) | ||
``` | ||
|
||
## Attributes | ||
|
||
| Attribute name | Description | | ||
| --------------------- | ----------------------------------------------------- | | ||
| name | `Name, required` <br> A unique name for this target. | ||
| deps | `List of labels, optional` <br> Labels for which you want to create scaladoc. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
"""Scaladoc support""" | ||
|
||
load("@io_bazel_rules_scala//scala/private:common.bzl", "collect_plugin_paths") | ||
|
||
ScaladocAspectInfo = provider(fields = [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this can be private, add a |
||
"src_files", | ||
"compile_jars", | ||
"plugins", | ||
]) | ||
|
||
def _scaladoc_aspect_impl(target, ctx): | ||
"""Collect source files and compile_jars from JavaInfo-returning deps.""" | ||
|
||
# We really only care about visited targets with srcs, so only look at those. | ||
if hasattr(ctx.rule.attr, "srcs"): | ||
# Collect only Java and Scala sources enumerated in visited targets. | ||
src_files = depset(direct = [file for file in ctx.rule.files.srcs if file.extension.lower() in ["java", "scala"]]) | ||
|
||
# Collect compile_jars from visited targets' deps. | ||
compile_jars = depset(transitive = [dep[JavaInfo].compile_jars for dep in ctx.rule.attr.deps if JavaInfo in dep]) | ||
|
||
plugins = depset() | ||
if hasattr(ctx.rule.attr, "plugins"): | ||
plugins = depset(direct = ctx.rule.attr.plugins) | ||
|
||
return [ScaladocAspectInfo( | ||
src_files = src_files, | ||
compile_jars = compile_jars, | ||
plugins = plugins, | ||
)] | ||
else: | ||
return [] | ||
|
||
scaladoc_aspect = aspect( | ||
implementation = _scaladoc_aspect_impl, | ||
attr_aspects = ["deps"], | ||
required_aspect_providers = [ | ||
[JavaInfo], | ||
], | ||
) | ||
|
||
def _scala_doc_impl(ctx): | ||
# scaladoc warns if you don't have the output directory already created, which is annoying. | ||
output_path = ctx.actions.declare_directory("{}.html".format(ctx.attr.name)) | ||
|
||
# Collect all source files and compile_jars to pass to scaladoc by way of an aspect. | ||
src_files = depset(transitive = [dep[ScaladocAspectInfo].src_files for dep in ctx.attr.deps]) | ||
compile_jars = depset(transitive = [dep[ScaladocAspectInfo].compile_jars for dep in ctx.attr.deps]) | ||
|
||
# Get the 'real' paths to the plugin jars. | ||
plugins = collect_plugin_paths(depset(transitive = [dep[ScaladocAspectInfo].plugins for dep in ctx.attr.deps])) | ||
|
||
# Construct the full classpath depset since we need to add compiler plugins too. | ||
classpath = depset(transitive = [plugins, compile_jars]) | ||
|
||
# Construct scaladoc args, which also include scalac args. | ||
# See `scaladoc -help` for more information. | ||
args = ctx.actions.args() | ||
args.add("-usejavacp") | ||
args.add("-d", output_path.path) | ||
args.add_all(plugins, format_each = "-Xplugin:%s") | ||
args.add_joined("-classpath", classpath, join_with = ":") | ||
args.add_all(src_files) | ||
|
||
# Run the scaladoc tool! | ||
ctx.actions.run( | ||
inputs = depset(transitive = [src_files, classpath]), | ||
outputs = [output_path], | ||
executable = ctx.attr._scaladoc.files_to_run.executable, | ||
mnemonic = "ScalaDoc", | ||
progress_message = "scaladoc {}".format(ctx.label), | ||
arguments = [args], | ||
) | ||
|
||
return [DefaultInfo(files = depset(direct = [output_path]))] | ||
|
||
scala_doc = rule( | ||
attrs = { | ||
"deps": attr.label_list( | ||
aspects = [scaladoc_aspect], | ||
providers = [JavaInfo], | ||
), | ||
"_scaladoc": attr.label( | ||
cfg = "host", | ||
executable = True, | ||
default = Label("//src/scala/io/bazel/rules_scala/scaladoc_support:scaladoc_generator"), | ||
), | ||
}, | ||
doc = "Generate Scaladoc HTML documentation for source files in from the given dependencies.", | ||
implementation = _scala_doc_impl, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
load("//scala:scala.bzl", "scala_binary") | ||
|
||
# A simple scala_binary to run scaladoc. | ||
# `bazel run` this target with "-help" as a param for usage text: | ||
# bazel run -- "//src/scala/io/bazel/rules_scala/scaladoc_support:scaladoc_generator" -help | ||
scala_binary( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I put this target in Let me know if this should be moved to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is fine. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool - happy to keep this here in case we do need to introduce a real Scala program. |
||
name = "scaladoc_generator", | ||
main_class = "scala.tools.nsc.ScalaDoc", | ||
visibility = ["//visibility:public"], | ||
runtime_deps = [ | ||
"//external:io_bazel_rules_scala/dependency/scala/parser_combinators", | ||
"//external:io_bazel_rules_scala/dependency/scala/scala_compiler", | ||
"//external:io_bazel_rules_scala/dependency/scala/scala_library", | ||
"//external:io_bazel_rules_scala/dependency/scala/scala_reflect", | ||
"//external:io_bazel_rules_scala/dependency/scala/scala_xml", | ||
], | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm following a pattern I see in other rules, in which you basically re-export rules that are loaded from other .bzl files, makes it such that a user can load multiple rules from the same .bzl file.