aboutsummaryrefslogtreecommitdiffhomepage
path: root/site/blog/_posts
diff options
context:
space:
mode:
authorGravatar Irina Iancu <elenairina@google.com>2017-03-10 15:30:28 +0000
committerGravatar Vladimir Moskva <vladmos@google.com>2017-03-10 17:40:42 +0000
commite591b0f2e86c30fedc282d7ae3b65035b1ee9f49 (patch)
tree9099c4941e55042e19694b09ecdf184c96f44bf7 /site/blog/_posts
parentb6d961f1cc89514f918843d8d41f3da2cfc27b4b (diff)
Blog post about Bazel Java sandwich and Java compilation in Skylark.
Fixes #2570. -- PiperOrigin-RevId: 149756052 MOS_MIGRATED_REVID=149756052
Diffstat (limited to 'site/blog/_posts')
-rw-r--r--site/blog/_posts/2017-03-07-java-sandwich.md189
1 files changed, 189 insertions, 0 deletions
diff --git a/site/blog/_posts/2017-03-07-java-sandwich.md b/site/blog/_posts/2017-03-07-java-sandwich.md
new file mode 100644
index 0000000000..748174228c
--- /dev/null
+++ b/site/blog/_posts/2017-03-07-java-sandwich.md
@@ -0,0 +1,189 @@
+---
+layout: posts
+title: Skylark and Java rules interoperability
+---
+
+As of Bazel 0.4.4, Java compilation is possible from a Skylark rule. This
+facilitates the Skylark and Java interoperability and allows creating what we
+call _Java sandwiches_ in Bazel.
+
+## What is a Bazel Java sandwich?
+
+A Java sandwich refers to custom rules written in Skylark being able to depend
+on Bazel native rules (e.g. `java_library`) and the other way around. A typical
+Java sandwich in Bazel could be illustrated like this:
+
+```python
+java_library(name = "top", ...)
+java_skylark_library(name = "middle", deps = [":top", ...], ...)
+java_library(name = "bottom", deps = [":middle", ...], ...)
+```
+## Built-in support for Java
+
+In Skylark, an interface to built-in Java functionality is available via the `java_common` module.
+The full API can be found in [the documentation](https://bazel.build/versions/master/docs/skylark/lib/java_common.html).
+
+### `java_common.compile`
+
+Compiles Java source files/jars from the implementation of a Skylark rule and
+returns a `java_common.provider` that encapsulates the compilation details.
+
+### `java_common.merge`
+
+Merges the given providers into a single `java_common.provider`.
+
+
+## Examples
+
+To allow other Java rules (native or custom) to depend on a Skylark rule, the
+Skylark rule should return a `java_common.provider`. All native Java rules
+return `java_common.provider` by default, which makes it possible for any Java
+related Skylark rule to depend on them.
+
+For now, there are 3 ways of creating a `java_common.provider`:
+
+1. The result of `java_common.compile`.
+2. Fetching it from a Java dependency.
+3. Merging multiple `java_common.provider` instances using `java_common.merge`.
+
+### Using the Java sandwich with compilation example
+
+This example illustrates the typical Java sandwich described above, that will
+make use of Java compilation:
+
+```python
+java_library(name = "top", ...)
+java_skylark_library(name = "middle", deps = [":top", ...], ...)
+java_library(name = "bottom", deps = [":middle", ...], ...)
+```
+
+In the BUILD file we load the Skylark rule and have the rules:
+```python
+load(':java_skylark_library.bzl', 'java_skylark_library')
+
+java_library(
+ name = "top",
+ srcs = ["A.java"],
+ deps = [":middle"]
+)
+
+java_skylark_library(
+ name = "middle",
+ srcs = ["B.java"],
+ deps = [":bottom"]
+)
+
+java_library(
+ name = "bottom",
+ srcs = ["C.java"]
+)
+```
+
+The implementation of `java_skylark_library` rule does the following:
+
+1. Collects all the `java_common.provider`s from its dependencies and merges
+them using `java_common.merge`.
+2. Creates an artifact that will be the output jar of the Java compilation.
+3. Compiles the specified Java source files using `java_common.compile`, passing
+as dependencies the collected `java_common.provider`s.
+4. Returns the output jar and the `java_common.provider` resulting from the
+compilation.
+
+```python
+def _impl(ctx):
+ deps = []
+ for dep in ctx.attr.deps:
+ if java_common.provider in dep:
+ deps.append(dep[java_common.provider])
+
+ output_jar = ctx.new_file("lib" + ctx.label.name + ".jar")
+
+ compilation_provider = java_common.compile(
+ ctx,
+ source_files = ctx.files.srcs,
+ output = output_jar,
+ javac_opts = [],
+ deps = deps,
+ strict_deps = "ERROR",
+ java_toolchain = ctx.attr._java_toolchain,
+ host_javabase = ctx.attr._host_javabase
+ )
+ return struct(
+ files = set([output_jar]),
+ providers = [compilation_provider]
+ )
+
+java_skylark_library = rule(
+ implementation = _impl,
+ attrs = {
+ "srcs": attr.label_list(allow_files=True),
+ "deps": attr.label_list(),
+ "_java_toolchain": attr.label(default = Label("@bazel_tools//tools/jdk:toolchain")),
+ "_host_javabase": attr.label(default = Label("//tools/defaults:jdk"))
+ },
+ fragments = ["java"]
+)
+```
+
+### Just passing around information about Java rules example
+
+In some use cases there is no need for Java compilation, but rather just passing
+information about Java rules around. A Skylark rule can have some other
+(irrelevant here) purpose, but if it is placed somewhere between two Java rules
+it should not lose information from bottom to top.
+
+In this example we have the same Bazel sandwich as above:
+
+```python
+java_library(name = "top", ...)
+java_skylark_library(name = "middle", deps = [":top", ...], ...)
+java_library(name = "bottom", deps = [":middle", ...], ...)
+```
+
+only that `java_skylark_library` won't make use of Java compilation, but will
+make sure that all the Java information encapsulated by the Java library
+`bottom` will be passed on to the Java library `top`.
+
+The BUILD file is identical to the one from the previous example.
+
+The implementation of `java_skylark_library` rule does the following:
+
+1. Collects all the `java_common.provider`s from its dependencies
+2. Returns the `java_common.provider` that resulted from merging the collected
+dependencies.
+
+```python
+def _impl(ctx):
+ deps = []
+ for dep in ctx.attr.deps:
+ if java_common.provider in dep:
+ deps.append(dep[java_common.provider])
+ deps_provider = java_common.merge(deps)
+ return struct(
+ providers = [deps_provider]
+ )
+
+java_skylark_library = rule(
+ implementation = _impl,
+ attrs = {
+ "srcs": attr.label_list(allow_files=True),
+ "deps": attr.label_list(),
+ "_java_toolchain": attr.label(default = Label("@bazel_tools//tools/jdk:toolchain")),
+ "_host_javabase": attr.label(default = Label("//tools/defaults:jdk"))
+ },
+ fragments = ["java"]
+)
+```
+## More to come
+
+Right now there is no way of creating a `java_common.provider` that encapsulates
+compiled code (and its transitive dependencies), other than
+`java_common.compile`. For example one may want to create a provider from a
+`.jar` file produced by some other means.
+
+Soon there will be support for use cases like this. Stay tuned!
+
+If you are interested in tracking the progress on Bazel Java sandwich you can
+subscribe to [this Github issue](https://github.com/bazelbuild/bazel/issues/2614).
+
+_[Irina Iancu](https://github.com/iirina), on behalf of the Bazel Java team_