2019-04-04 11:33:38 +03:00
|
|
|
# Bazel JVM Porting Guide
|
|
|
|
|
|
|
|
This document explains how to port an existing JVM project (Java or Scala)
|
|
|
|
to Bazel.
|
|
|
|
|
|
|
|
If you are not intending to port a project to Bazel but rather use Bazel on an
|
|
|
|
already ported project, then please refer to the [Bazel User
|
|
|
|
Guide][bazel_user_guide].
|
|
|
|
|
|
|
|
This guide assumes familiarity with Bazel's core concepts and a functioning
|
|
|
|
Bazel setup. If you are not yet familiar with Bazel, need a refresher, or need
|
|
|
|
to setup Bazel, please refer to the [Bazel User Guide][bazel_user_guide].
|
|
|
|
|
|
|
|
[bazel_user_guide]: ./BAZEL.md
|
|
|
|
|
|
|
|
This guide is based on the experience of porting an SBT project to Bazel. If
|
|
|
|
you are not porting an SBT project but a Maven project instead, then you should
|
|
|
|
also have a look at the Maven porting guide in the [official Bazel
|
|
|
|
documentation][bazel_maven_guide]. However, in order to arrive at a homogenous
|
2022-11-09 18:55:12 +03:00
|
|
|
build system and project structure you should follow the suggestions in this
|
2019-04-04 11:33:38 +03:00
|
|
|
guide where possible.
|
|
|
|
|
|
|
|
[bazel_maven_guide]: https://docs.bazel.build/versions/master/migrate-maven.html
|
|
|
|
|
|
|
|
The structure of this guide is as follows: First, we describe how to define
|
|
|
|
external dependencies to your project in Bazel. This includes additional Bazel
|
|
|
|
rules that you may need to import from external workspaces, Maven JAR
|
|
|
|
dependencies, or other artifacts that need to be fetched from Artifactory.
|
|
|
|
Then, we describe how to build a JVM project with Bazel. This covers the
|
|
|
|
project structure, in particular when coming from SBT, and the details of
|
|
|
|
defining Java and Scala targets in Bazel. In the very end we talk about the
|
|
|
|
Java runtime and toolchain that Bazel uses and provide links to Bazel API
|
|
|
|
documentation.
|
|
|
|
|
|
|
|
|
|
|
|
## External Dependencies
|
|
|
|
|
|
|
|
External dependencies are targets that your project depends on, but that are
|
|
|
|
not built, or otherwise generated, within the local workspace. Such external
|
|
|
|
dependencies are defined in the `WORKSPACE` file or in Bazel macros that are
|
|
|
|
called from the `WORKSPACE` file.
|
|
|
|
|
|
|
|
### Bazel Dependencies
|
|
|
|
|
|
|
|
Bazel can be extended with custom rules, for example to support languages for
|
|
|
|
which Bazel provides no builtin rules. Extensions that are defined outside the
|
|
|
|
current workspace can be included from an external workspace. Bazel provides
|
|
|
|
the workspace rules `http_archive`, `get_repository`, or `local_repository` in
|
|
|
|
order to define an external workspace. Refer to the [official
|
|
|
|
documentation][bazel_workspace_rules] for details. An example is presented in
|
|
|
|
the Scala section below.
|
|
|
|
|
|
|
|
[bazel_workspace_rules]: https://docs.bazel.build/versions/master/be/workspace.html#workspace-rules
|
|
|
|
|
|
|
|
#### Java
|
|
|
|
|
|
|
|
Bazel has builtin support for Java. No external workspaces need to be imported
|
|
|
|
to support Java projects at the time of writing.
|
|
|
|
|
|
|
|
#### Scala
|
|
|
|
|
|
|
|
Bazel does not have builtin Scala support. In this repository we use
|
|
|
|
[`rules_scala`][rules_scala], which defines Bazel rules to build Scala projects.
|
|
|
|
First, we need to import the external workspace:
|
|
|
|
|
|
|
|
```
|
|
|
|
http_archive(
|
|
|
|
name = 'io_bazel_rules_scala',
|
|
|
|
url = 'https://github.com/bazelbuild/rules_scala/...',
|
|
|
|
...
|
|
|
|
)
|
|
|
|
```
|
|
|
|
|
|
|
|
This will make `rules_scala` available under the label
|
|
|
|
`@io_bazel_rules_scala//`. Next, we define the exact version of the Scala
|
|
|
|
compiler and core libraries to use:
|
|
|
|
|
|
|
|
```
|
|
|
|
load('@io_bazel_rules_scala//scala:scala.bzl', 'scala_repositories')
|
2020-08-07 13:28:14 +03:00
|
|
|
scala_repositories((
|
2020-10-13 15:42:14 +03:00
|
|
|
"2.12.12",
|
2020-08-07 13:28:14 +03:00
|
|
|
{
|
2020-10-13 15:42:14 +03:00
|
|
|
"scala_compiler": "9dfa682ad7c2859cdcf6a31b9734c8f1ee38e7e391aeafaef91967b6ce819b6b",
|
|
|
|
"scala_library": "1673ffe8792021f704caddfe92067ed1ec75229907f84380ad68fe621358c925",
|
|
|
|
"scala_reflect": "3c502791757c0c8208f00033d8c4d778ed446efa6f49a6f89b59c6f92b347774",
|
2020-08-07 13:28:14 +03:00
|
|
|
},
|
|
|
|
))
|
2019-04-04 11:33:38 +03:00
|
|
|
load('@io_bazel_rules_scala//scala:toolchains.bzl', 'scala_register_toolchains')
|
|
|
|
scala_register_toolchains()
|
|
|
|
```
|
|
|
|
|
|
|
|
If you need to update the Scala version, make sure to also update the
|
2020-08-07 13:28:14 +03:00
|
|
|
corresponding SHA-256 hashes in hexadecimal encoding. If you don't know the
|
|
|
|
hash, set it to 64 zeroes and Bazel will correct you.
|
2019-04-04 11:33:38 +03:00
|
|
|
|
|
|
|
See the [`rules_scala` setup guide][rules_scala_setup] for further details.
|
|
|
|
|
|
|
|
[rules_scala]: https://github.com/bazelbuild/rules_scala
|
|
|
|
[rules_scala_setup]: https://github.com/bazelbuild/rules_scala#getting-started
|
|
|
|
|
|
|
|
### Maven JAR Dependencies
|
|
|
|
|
|
|
|
Next, we consider the most common case of Maven JARs which are distributed on
|
|
|
|
Artifactory. We distinguish direct and transitive dependencies. Direct
|
|
|
|
dependencies are explicitly defined on targets in the local workspace.
|
|
|
|
Transitive dependencies are introduced implicitly as dependencies of direct
|
|
|
|
dependencies. It is preferable to only have to define direct dependencies and
|
|
|
|
let a dependency resolver determine all transitive dependencies.
|
|
|
|
|
2019-10-28 15:53:14 +03:00
|
|
|
The [`rules_jvm_external`][rules_jvm_external] rules set can read a list of
|
|
|
|
direct JAR dependencies and then use [Coursier][coursier] to perform dependency
|
|
|
|
resolution and generate the whole transitive closure of dependencies.
|
2019-04-04 11:33:38 +03:00
|
|
|
|
2019-10-28 15:53:14 +03:00
|
|
|
Direct dependencies are manually defined in the file `bazel-java-deps.bzl` at
|
|
|
|
the repository root. This file also defines Bazel macros that must be called in
|
2019-04-04 11:33:38 +03:00
|
|
|
the `WORKSPACE` file in order to import the external JAR dependencies.
|
|
|
|
|
|
|
|
```
|
2019-10-28 15:53:14 +03:00
|
|
|
load("//:bazel-java-deps.bzl", "install_java_deps")
|
|
|
|
install_java_deps()
|
|
|
|
load("@maven//:defs.bzl", "pinned_maven_install")
|
|
|
|
pinned_maven_install()
|
2019-04-04 11:33:38 +03:00
|
|
|
```
|
|
|
|
|
2019-10-28 15:53:14 +03:00
|
|
|
The macro `install_java_deps` defines the direct dependencies and the tools to
|
|
|
|
load and update pinned versions. The macro `pinned_maven_install` loads the
|
|
|
|
pinned artifacts into Bazel. Within `bazel-java-deps.bzl` we call
|
|
|
|
`maven_install` where we define all direct Maven dependencies.
|
2019-04-04 11:33:38 +03:00
|
|
|
|
|
|
|
```
|
2019-10-28 15:53:14 +03:00
|
|
|
maven_install(
|
|
|
|
artifacts = [
|
|
|
|
"com.google.guava:guava:24.0-jre",
|
|
|
|
"org.scalaz:scalaz-core_2.12:7.2.24",
|
|
|
|
...
|
|
|
|
],
|
|
|
|
...
|
|
|
|
)
|
2019-04-04 11:33:38 +03:00
|
|
|
```
|
|
|
|
|
2019-10-28 15:53:14 +03:00
|
|
|
Dependencies are defined using their Maven coordinates. Note that Scala
|
|
|
|
packages have the Scala version appended to their artifact id.
|
2019-04-04 11:33:38 +03:00
|
|
|
|
2019-10-28 15:53:14 +03:00
|
|
|
In some cases we need to override the automatic dependency resolution performed
|
|
|
|
by Coursier. E.g. the `org.lang-scala:*` packages need to exactly match the
|
|
|
|
version of the Scala compiler registered with `rules_scala`. To that end we use
|
|
|
|
the `override_targets` attribute:
|
2019-04-04 11:33:38 +03:00
|
|
|
|
2019-10-28 15:53:14 +03:00
|
|
|
```
|
|
|
|
override_targets = {
|
|
|
|
"org.scala-lang:scala-compiler": "@io_bazel_rules_scala_scala_compiler//:io_bazel_rules_scala_scala_compiler",
|
|
|
|
...
|
|
|
|
}
|
|
|
|
```
|
2019-04-04 11:33:38 +03:00
|
|
|
|
2019-10-28 15:53:14 +03:00
|
|
|
This maps Maven artifacts to Bazel targets which should be used instead.
|
2019-04-04 11:33:38 +03:00
|
|
|
|
2019-10-28 15:53:14 +03:00
|
|
|
Finally, `pinned_maven_install` generates Bazel targets for the imported JARs.
|
|
|
|
The naming scheme is as follows `@maven//:group_id_artifact_id`, where all
|
|
|
|
symbols are replaced by `_` characters. E.g. the artifact
|
|
|
|
`org.scalaz:scalaz-core_2.12:7.2.24` is available as
|
|
|
|
`@maven//:org_scalaz_scalaz_core_2_12`.
|
2019-04-04 11:33:38 +03:00
|
|
|
|
2020-12-14 14:42:26 +03:00
|
|
|
Adding, changing, or removing a Maven dependency requires two steps:
|
|
|
|
First, you need to modify the `artifacts` attribute to
|
|
|
|
`maven_install`, second, you need to execute `bazel run
|
|
|
|
@unpinned_maven//:pin` to update `maven_install.json`. You should
|
|
|
|
also run `@unpinned_maven//:pin` if you change other attributes to
|
2021-11-17 20:38:43 +03:00
|
|
|
`maven_install`.
|
2019-10-30 13:19:01 +03:00
|
|
|
|
2019-10-28 15:53:14 +03:00
|
|
|
Refer to the [`rules_jvm_external` documentation][rules_jvm_external] for
|
|
|
|
further information.
|
2019-04-04 11:33:38 +03:00
|
|
|
|
2019-10-28 15:53:14 +03:00
|
|
|
[rules_jvm_external]: https://github.com/bazelbuild/rules_jvm_external#rules_jvm_external
|
2019-04-04 11:33:38 +03:00
|
|
|
[coursier]: https://github.com/coursier/coursier
|
|
|
|
|
|
|
|
#### Conflicting Dependencies
|
|
|
|
|
|
|
|
Following the goal of HEAD based development all projects should strive to
|
2019-10-28 15:53:14 +03:00
|
|
|
share common dependencies in one place, such as `bazel-java-deps.bzl` for JAR
|
2019-04-04 11:33:38 +03:00
|
|
|
dependencies. If different projects have conflicting dependencies, then these
|
|
|
|
conflicts should be fixed so that both projects can share the same common
|
|
|
|
dependencies.
|
|
|
|
|
|
|
|
A valid exception to this rule are external tools that are defined in Bazel and
|
|
|
|
used for build or development. These may have conflicting dependencies with the
|
|
|
|
projects in this repository and changing their code to rectify these conflicts
|
|
|
|
may be out of scope or infeasible.
|
|
|
|
|
2019-10-28 15:53:14 +03:00
|
|
|
In such cases multiple calls to `maven_install` are supported, see the
|
|
|
|
[`rules_jvm_external` documentation][rules_jvm_external_multi].
|
|
|
|
|
2022-10-11 15:49:43 +03:00
|
|
|
#### Outdated Dependencies
|
|
|
|
|
|
|
|
`rules_jvm_external` provides a tool to check for outdated dependencies. To do
|
|
|
|
so, run `bazel run @maven//:outdated`, which prints to stdout something like the following:
|
|
|
|
|
|
|
|
```
|
|
|
|
Checking for updates of 153 artifacts against the following repositories:
|
|
|
|
https://repo1.maven.org/maven2
|
|
|
|
|
|
|
|
com.auth0:java-jwt [3.10.3 -> 4.0.0]
|
|
|
|
com.auth0:jwks-rsa [0.11.0 -> 0.21.2]
|
|
|
|
com.chuusai:shapeless_2.13 [2.3.3 -> 2.4.0-M1]
|
|
|
|
...
|
|
|
|
```
|
|
|
|
|
2019-10-28 15:53:14 +03:00
|
|
|
[rules_jvm_external_multi]: https://github.com/bazelbuild/rules_jvm_external#multiple-maven_installjson-files
|
2019-04-04 11:33:38 +03:00
|
|
|
|
|
|
|
## Building a Project with Bazel
|
|
|
|
|
|
|
|
Recall, that a Bazel workspace consists of packages and targets. A package is a
|
|
|
|
directory that contains a `BUILD.bazel` file. A `BUILD.bazel` file can define
|
|
|
|
rule targets. Additionally, regular files underneath a package are targets as
|
|
|
|
well.
|
|
|
|
|
|
|
|
### Project Structure
|
|
|
|
|
|
|
|
When structuring a Bazel project you should strive for small targets and small
|
|
|
|
packages. Bazel's incremental builds will work best when targets are as small
|
|
|
|
as possible. Additionally, you should make target visibility as tight as
|
|
|
|
possible in favour of a clean dependency graph.
|
|
|
|
|
|
|
|
If you are porting a JVM component in the `da` repository it is likely that you
|
|
|
|
will be coming from an SBT project structure similar to the following.
|
|
|
|
|
|
|
|
```
|
|
|
|
da
|
|
|
|
├── WORKSPACE
|
|
|
|
└── my_component
|
|
|
|
├── build.sbt
|
|
|
|
├── module1
|
|
|
|
│ ├── build.sbt
|
|
|
|
│ └── src
|
|
|
|
│ ├── main
|
|
|
|
│ │ └── scala
|
2020-04-05 20:49:57 +03:00
|
|
|
│ │ └── com/daml/module
|
2019-04-04 11:33:38 +03:00
|
|
|
│ │ ├── Main.scala
|
|
|
|
│ │ ⋮
|
|
|
|
│ └── test
|
|
|
|
│ └── scala
|
2020-04-05 20:49:57 +03:00
|
|
|
│ └── com/daml/module
|
2019-04-04 11:33:38 +03:00
|
|
|
│ ├── SomeSpec.scala
|
|
|
|
│ ⋮
|
|
|
|
├── module2
|
|
|
|
│ ├── build.sbt
|
|
|
|
⋮ ⋮
|
|
|
|
└── project
|
|
|
|
⋮
|
|
|
|
```
|
|
|
|
|
|
|
|
In this case `da/my_component/build.sbt` defines properties of the whole
|
|
|
|
component and the interdependencies between the modules (subprojects in SBT).
|
|
|
|
The files `da/my_component/moduleX/build.sbt` define properties of a particular
|
|
|
|
module, and its external dependencies. Finally, the folder
|
|
|
|
`da/my_component/project` contains additional files defining further properties
|
|
|
|
of the component, SBT plugins, etc.
|
|
|
|
|
|
|
|
SBT associates tasks with a project, such as `compile` or `test`. It is then
|
|
|
|
possible to specify dependencies only for certain tasks. E.g. test-only
|
|
|
|
dependencies. Furthermore, if `module2` depends on `module1`, then `module2`'s
|
|
|
|
tests can depend on test classes defined in `module1`.
|
|
|
|
|
|
|
|
In Bazel things are structured differently. A library, a test-only library, or
|
|
|
|
a test-suite are all separate Bazel targets. Dependencies are defined at each
|
|
|
|
individual target. Repetition can be reduced by transitive dependencies or by
|
|
|
|
giving a name to a list of targets and assigning that list as a dependency
|
|
|
|
|
|
|
|
Bazel is not extended by a plugin mechanism like SBT plugins. Instead Bazel is
|
|
|
|
extended by user defined rules or macros. It is not often the case that one has
|
|
|
|
to define custom rules or macros. In most cases existing ones can be reused.
|
|
|
|
|
|
|
|
#### Fine Grained Bazel Project Structure
|
|
|
|
|
|
|
|
As mentioned before one should strive for as small Bazel targets as possible.
|
|
|
|
Translated to Bazel the above project might take the following form:
|
|
|
|
|
|
|
|
```
|
|
|
|
da
|
|
|
|
├── WORKSPACE
|
|
|
|
└── my_component
|
|
|
|
├── BUILD.bazel
|
|
|
|
├── module1
|
|
|
|
│ ├── BUILD.bazel
|
|
|
|
│ └── src
|
|
|
|
│ ├── main
|
|
|
|
│ │ └── scala
|
2020-04-05 20:49:57 +03:00
|
|
|
│ │ └── com/daml/module
|
2019-04-04 11:33:38 +03:00
|
|
|
│ │ ├── BUILD.bazel
|
|
|
|
│ │ ├── Main.scala
|
|
|
|
│ │ ⋮
|
|
|
|
│ └── test
|
|
|
|
│ └── scala
|
2020-04-05 20:49:57 +03:00
|
|
|
│ └── com/daml/module
|
2019-04-04 11:33:38 +03:00
|
|
|
│ ├── BUILD.bazel
|
|
|
|
│ ├── SomeSpec.scala
|
|
|
|
│ ⋮
|
|
|
|
└── module2
|
|
|
|
├── BUILD.bazel
|
|
|
|
⋮
|
|
|
|
```
|
|
|
|
|
|
|
|
Here `da/my_component/moduleX/src/.../BUILD.bazel` would define small library,
|
|
|
|
test, and executable targets. `da/my_component/moduleX/BUILD.bazel` would
|
|
|
|
bundle these small targets into larger ones to make module interdependencies
|
|
|
|
easier to manage. Finally, `da/my_component/BUILD.bazel` would bundle the
|
|
|
|
module targets to ease management of component interdependencies or component
|
|
|
|
release.
|
|
|
|
|
|
|
|
#### Coarse Grained Intermediate Bazel Project Structure
|
|
|
|
|
|
|
|
However, coming from SBT such a fine grained structure may not immediately be
|
|
|
|
possible without change in the corresponding Java or Scala code. For example
|
|
|
|
due to cyclic dependencies between larger sets of source files. In this case
|
|
|
|
the following structure can be used as an intermediate step:
|
|
|
|
|
|
|
|
```
|
|
|
|
da
|
|
|
|
├── WORKSPACE
|
|
|
|
└── my_component
|
|
|
|
├── BUILD.bazel
|
|
|
|
├── module1
|
|
|
|
│ ├── BUILD.bazel
|
|
|
|
│ └── src
|
|
|
|
│ ├── main
|
|
|
|
│ │ └── scala
|
2020-04-05 20:49:57 +03:00
|
|
|
│ │ └── com/daml/module
|
2019-04-04 11:33:38 +03:00
|
|
|
│ │ ├── Main.scala
|
|
|
|
│ │ ⋮
|
|
|
|
│ └── test
|
|
|
|
│ └── scala
|
2020-04-05 20:49:57 +03:00
|
|
|
│ └── com/daml/module
|
2019-04-04 11:33:38 +03:00
|
|
|
│ ├── SomeSpec.scala
|
|
|
|
│ ⋮
|
|
|
|
└── module2
|
|
|
|
├── BUILD.bazel
|
|
|
|
⋮
|
|
|
|
```
|
|
|
|
|
|
|
|
Here, `da/my_component/moduleX/BUILD.bazel` defines targets for what was
|
|
|
|
previously an SBT subproject. Note, that a single SBT project will already
|
|
|
|
separate into multiple Bazel targets at this step. Notably, regular library
|
|
|
|
targets and test-only library targets will be separate. Test-cases should be
|
|
|
|
made as small targets as possible at this stage already. Bazel test-suite
|
|
|
|
macros (see below) should be used to reduce boilerplate.
|
|
|
|
|
|
|
|
### Java Targets
|
|
|
|
|
|
|
|
This section will outline how to define common Java targets. Please refer to
|
|
|
|
the [Bazel API documentation][bazel-api-documentation] for further details.
|
|
|
|
|
|
|
|
#### Libraries
|
|
|
|
|
|
|
|
Java libraries can be defined using the builtin `java_library` rule. It will
|
|
|
|
generate a JAR file containing the compiled Java classes and a source JAR
|
|
|
|
containing the input Java sources. For example:
|
|
|
|
|
|
|
|
```
|
|
|
|
java_library(
|
|
|
|
name = "example",
|
|
|
|
# JAR dependencies for compiletime and runtime classpath
|
|
|
|
deps = [
|
|
|
|
"//some/package:some_target",
|
|
|
|
":some_local_target",
|
|
|
|
...
|
|
|
|
],
|
|
|
|
# JAR dependencies for runtime class path only
|
|
|
|
runtime_deps = [ ... ],
|
|
|
|
# JAR dependencies that should be forwarded to anything that depends on this target.
|
|
|
|
exports = [ ... ],
|
|
|
|
# Java source files
|
|
|
|
srcs = glob(["src/main/java/.../my_component/**/*.java"]),
|
|
|
|
# Resource files
|
|
|
|
resources = glob(["src/.../resources/**"]),
|
|
|
|
# Keep target visibility tight.
|
|
|
|
visibility = ["//current_component:__pkg__"],
|
|
|
|
)
|
|
|
|
```
|
|
|
|
|
|
|
|
#### Tests
|
|
|
|
|
|
|
|
Java tests can be defined using the builtin `java_test` rule. It will generate
|
|
|
|
an executable wrapper around the given test code that executes the test
|
|
|
|
runner's main method. Test targets can be executed using the `bazel test`
|
|
|
|
command.
|
|
|
|
|
|
|
|
Bazel can cache test results, which can greatly reduce test-suite execution
|
|
|
|
time. For maximum benefit test targets should be made as small as possible,
|
|
|
|
ideally a single Java source file.
|
|
|
|
|
|
|
|
Use the `java_test_suite` macro to automatically define one test target for
|
|
|
|
every given source file and bundle them in one test-suite target. If test-cases
|
|
|
|
depend on utility classes defined in other Java sources, then you should define
|
|
|
|
a Java library target for these utility classes and let the test targets depend
|
|
|
|
on this library.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
```
|
|
|
|
load('//bazel_tools/java_testing:java_test_suite.bzl', 'java_test_suite')
|
|
|
|
|
|
|
|
test_utils = glob(["src/test/java/.../utils/**/*.java"])
|
|
|
|
|
|
|
|
java_library(
|
|
|
|
name = "test-utils",
|
|
|
|
srcs = test_utils,
|
|
|
|
...
|
|
|
|
)
|
|
|
|
|
|
|
|
java_test_suite(
|
|
|
|
name = "tests",
|
|
|
|
srcs = glob(
|
|
|
|
["src/test/java/**/*.java"],
|
|
|
|
exclude = test_utils,
|
|
|
|
),
|
|
|
|
# Expected runtime and resource requirements.
|
|
|
|
size = "small",
|
|
|
|
...
|
|
|
|
)
|
|
|
|
```
|
|
|
|
|
|
|
|
The `size` attribute is used to determine the default timeout and resource
|
|
|
|
requirements. Refer to the [official documentation][bazel_test_size] for
|
|
|
|
details about test size and other common test attributes.
|
|
|
|
|
|
|
|
[bazel_test_size]: https://docs.bazel.build/versions/master/be/common-definitions.html#common-attributes-tests
|
|
|
|
|
|
|
|
#### Executables
|
|
|
|
|
|
|
|
Java executables can be defined using the builtin `java_binary` rule. It will
|
|
|
|
generate a JAR and executable wrapper script that defines the classpath and
|
|
|
|
executes the Java runtime. Executable targets can be executed using the `bazel
|
|
|
|
run` command.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
```
|
|
|
|
java_binary(
|
|
|
|
name = "example",
|
|
|
|
# The srcs attribute is optional.
|
|
|
|
srcs = [ ... ],
|
|
|
|
# The deps attribute is only allowed if srcs are specified.
|
|
|
|
deps = [ ... ],
|
|
|
|
# Name of the class that contains the entrypoint `main()`.
|
|
|
|
# The main class can originate from srcs, deps, or runtime_deps.
|
|
|
|
# A missing main class causes runtime failure.
|
|
|
|
main_class = ...,
|
|
|
|
# A list of flags to pass to the Java runtime.
|
|
|
|
jvm_flags = [ ... ],
|
|
|
|
# A list of files that should be present in the runtime path at runtime.
|
|
|
|
data = [ ... ],
|
|
|
|
...
|
|
|
|
)
|
|
|
|
```
|
|
|
|
|
|
|
|
### Scala Targets
|
|
|
|
|
|
|
|
This section will outline how to define common Scala targets. Please refer to
|
|
|
|
the [Bazel API documentation][bazel-api-documentation] for further details.
|
|
|
|
|
|
|
|
This repository uses `rules_scala` to build Scala code in Bazel, which provides
|
|
|
|
rules such as `scala_library`, or `scala_test_suite`. Additionally, wrapper
|
|
|
|
macros are provided in `bazel_tools/scala.bzl`, such as `da_scala_library`, or
|
|
|
|
`da_scala_test_suite`. These apply common compiler flags, plugins, and linter
|
|
|
|
configuration. Please prefer the wrapper macros over the underlying
|
|
|
|
`rules_scala` rules.
|
|
|
|
|
|
|
|
If the project you are porting uses a different set of default flags, plugins,
|
|
|
|
etc., then see if these could be merged with the common defaults. The goal is
|
|
|
|
to converge on one shared set of common compiler flags.
|
|
|
|
|
|
|
|
#### Libraries
|
|
|
|
|
|
|
|
Scala libraries can be defined using the `da_scala_library` rule. It will
|
|
|
|
generate a JAR file containing the compiled Scala classes and a source JAR
|
|
|
|
containing the input Scala sources. For example:
|
|
|
|
|
|
|
|
```
|
|
|
|
load("//bazel_tools:scala.bzl", "da_scala_library")
|
|
|
|
|
|
|
|
da_scala_library(
|
|
|
|
name = "example",
|
|
|
|
# JAR dependencies for compiletime and runtime classpath
|
|
|
|
deps = [
|
|
|
|
"//some/package:some_target",
|
|
|
|
":some_local_target",
|
|
|
|
...
|
|
|
|
],
|
|
|
|
# JAR dependencies for runtime class path only
|
|
|
|
runtime_deps = [ ... ],
|
|
|
|
# JAR dependencies that should be forwarded to anything that depends on this target.
|
|
|
|
exports = [ ... ],
|
|
|
|
# Scala source files
|
|
|
|
srcs = glob(["src/main/java/.../my_component/**/*.scala"]),
|
|
|
|
# Resource files
|
|
|
|
resources = glob(["src/.../resources/**"]),
|
|
|
|
# Keep target visibility tight.
|
|
|
|
visibility = ["//current_component:__pkg__"],
|
|
|
|
# Unused dependencies cause an error at build time.
|
|
|
|
unused_dependency_checker_mode = "error",
|
|
|
|
|
|
|
|
# If the library uses Scala macros you need to add the following attributes.
|
|
|
|
scalacopts = ['-Xplugin-require:macroparadise'],
|
|
|
|
plugins = [
|
|
|
|
# Plugins have to be specified as JAR targets.
|
|
|
|
'//external:jar/org/scalameta/paradise_2_12_6',
|
|
|
|
],
|
|
|
|
)
|
|
|
|
```
|
|
|
|
|
|
|
|
Strive to make library targets as small as possible. In a situation where
|
|
|
|
multiple Scala sources have no interdependencies you can use the
|
|
|
|
`da_scala_library_suite` macro to automatically generate one library target per
|
|
|
|
Scala source file, and bundle them in one target. This rule takes the same
|
|
|
|
attributes as `da_scala_library` with the exception of
|
|
|
|
`unused_dependency_checker_mode` which will always be disabled.
|
|
|
|
|
|
|
|
#### Tests
|
|
|
|
|
|
|
|
Scala tests can be defined using the `da_scala_test` rule. It will generate an
|
|
|
|
executable wrapper around the given test code that executes the test runner's
|
|
|
|
main method. Test targets can be executed using the `bazel test` command.
|
|
|
|
|
|
|
|
Bazel can cache test results, which can greatly reduce test-suite execution
|
|
|
|
time. For maximum benefit test targets should be made as small as possible,
|
|
|
|
ideally a single Scala source file.
|
|
|
|
|
|
|
|
Use the `da_scala_test_suite` macro to automatically define one test target for
|
|
|
|
every given source file and bundle them in one test-suite target. If test-cases
|
|
|
|
depend on utility classes defined in other Scala sources, then you should
|
|
|
|
define a Scala library target for these utility classes and let the test
|
|
|
|
targets depend on this library.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
```
|
|
|
|
load("//bazel_tools:scala.bzl", "da_scala_library", "da_scala_test_suite")
|
|
|
|
|
|
|
|
test_utils = glob(["src/test/scala/.../utils/**/*.scala"])
|
|
|
|
|
|
|
|
da_scala_library(
|
|
|
|
name = "test-utils",
|
|
|
|
srcs = test_utils,
|
|
|
|
...
|
|
|
|
)
|
|
|
|
|
|
|
|
da_scala_test_suite(
|
|
|
|
name = "tests",
|
|
|
|
srcs = glob(
|
|
|
|
["src/test/scala/**/*.scala"],
|
|
|
|
exclude = test_utils,
|
|
|
|
),
|
|
|
|
# Expected runtime and resource requirements.
|
|
|
|
size = "small",
|
|
|
|
...
|
2020-04-01 22:14:20 +03:00
|
|
|
|
|
|
|
# You can adjust the heap size as follows:
|
|
|
|
initial_heap_size = "512m",
|
|
|
|
max_heap_size = "2g",
|
2019-04-04 11:33:38 +03:00
|
|
|
)
|
|
|
|
```
|
|
|
|
|
|
|
|
The `size` attribute is used to determine the default timeout and resource
|
|
|
|
requirements. Refer to the [official documentation][bazel_test_size] for
|
|
|
|
details about test size and other common test attributes.
|
|
|
|
|
2020-04-01 22:14:20 +03:00
|
|
|
A couple of arguments have been added:
|
|
|
|
|
|
|
|
* `initial_heap_size` is translated to `-Xms`, and defaults to `512m`, and
|
|
|
|
* `max_heap_size` is translated to `-Xmx`, and defaults to `2g`.
|
|
|
|
|
2019-04-04 11:33:38 +03:00
|
|
|
#### Executables
|
|
|
|
|
|
|
|
Scala executables can be defined using the `da_scala_binary` rule. It will
|
|
|
|
generate a JAR and executable wrapper script that defines the classpath and
|
|
|
|
executes the Java runtime. Executable targets can be executed using the `bazel
|
|
|
|
run` command.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
```
|
|
|
|
load("//bazel_tools:scala.bzl", "da_scala_binary")
|
|
|
|
|
|
|
|
da_scala_binary(
|
|
|
|
name = "example",
|
|
|
|
# The srcs attribute is optional.
|
|
|
|
srcs = [ ... ],
|
|
|
|
# The deps attribute is only allowed if srcs are specified.
|
|
|
|
deps = [ ... ],
|
|
|
|
# Name of the class that contains the entrypoint `main()`.
|
|
|
|
# The main class can originate from srcs, deps, or runtime_deps.
|
|
|
|
# A missing main class causes runtime failure.
|
|
|
|
main_class = ...,
|
|
|
|
# A list of flags to pass to the Java runtime.
|
|
|
|
jvm_flags = [ ... ],
|
|
|
|
# A list of files that should be present in the runtime path at runtime.
|
|
|
|
data = [ ... ],
|
|
|
|
...
|
2020-04-01 22:14:20 +03:00
|
|
|
|
|
|
|
# You can adjust the heap size as follows:
|
|
|
|
initial_heap_size = "512m",
|
|
|
|
max_heap_size = "2g",
|
2019-04-04 11:33:38 +03:00
|
|
|
)
|
|
|
|
```
|
|
|
|
|
|
|
|
## SBT Plugins
|
|
|
|
|
|
|
|
If you are porting an SBT project to Bazel then you may encounter SBT plugins
|
|
|
|
that the project's build depends on, or that are important for developer
|
|
|
|
workflow, or project release. SBT's plugin mechanism is very flexible, and it
|
|
|
|
is not possible to describe a general procedure on how to port dependence on
|
|
|
|
any SBT plugin to Bazel.
|
|
|
|
|
|
|
|
In the following we list examples of SBT plugins that have been encountered and
|
|
|
|
ported to Bazel so far. If the SBT plugin you require has already been ported
|
|
|
|
to Bazel then you should be able to use the ported version in your project as
|
|
|
|
well. Otherwise, you should check if the plugin is similar to one of the
|
|
|
|
previously encountered plugins and port it in an analogous way.
|
|
|
|
|
|
|
|
### Built-In Bazel Features
|
|
|
|
|
|
|
|
It is worth checking whether the functionality provided by an SBT plugin is
|
|
|
|
already built into Bazel or a particular Bazel rule set. For instance, Bazel
|
|
|
|
has built-in support for generating dependency graphs, and `rules_scala` has
|
|
|
|
built-in support for generating deplyment JARs. Refer to the [user
|
|
|
|
guide][bazel_user_guide] for details.
|
|
|
|
|
|
|
|
### Compiler Plugins
|
|
|
|
|
|
|
|
Various SBT plugins make use of compiler plugins, or of components that can be
|
|
|
|
used as compiler plugins. `rules_scala` allows to define compiler plugins on
|
|
|
|
Scala targets. Therefore, such SBT plugins can be ported to Bazel by activating
|
|
|
|
the corresponding compiler plugin.
|
|
|
|
|
|
|
|
One example is the [wartremover plugin][wartremover_plugin], which provides
|
|
|
|
linting for Scala code. In the Bazel build the wartremover compiler plugin is
|
|
|
|
activated on all Scala targets by default. It is configured in the [Scala rule
|
|
|
|
wrappers][wartremover_config] used in the daml repository.
|
|
|
|
|
|
|
|
[wartremover_plugin]: https://github.com/wartremover/wartremover
|
2019-08-26 11:50:11 +03:00
|
|
|
[wartremover_config]: https://github.com/digital-asset/daml/blob/ea02814b343e4754c70a8718cb14657d6c51915f/bazel_tools/scala.bzl#L71
|
2019-04-04 11:33:38 +03:00
|
|
|
|
|
|
|
### Command-Line Tools
|
|
|
|
|
|
|
|
If an SBT plugin relies on a tool that can be called as a standalone
|
|
|
|
command-line application, then it can be ported to Bazel by defining a custom
|
|
|
|
rule that calls that command-line application.
|
|
|
|
|
|
|
|
For example, the [`scalafmt` Scala formatting checker][scalafmt] can be invoked
|
|
|
|
as a command-line tool. A custom Bazel rule
|
|
|
|
[`scala_format_test`][scala_format_test] is defined in the daml repository, that
|
|
|
|
generates a test-case that will run `scalafmt` on the specified Scala source
|
|
|
|
files.
|
|
|
|
|
|
|
|
[scalafmt]: https://github.com/scalameta/scalafmt
|
|
|
|
[scala_format_test]: https://github.com/DACH-NY/da/blob/e904c8eac1427633ef20b6106906a59f590de5a6/bazel_tools/scalafmt/scalafmt.bzl#L31
|
|
|
|
|
2021-01-08 15:50:15 +03:00
|
|
|
### Daml
|
2019-04-04 11:33:38 +03:00
|
|
|
|
|
|
|
The SBT build of the `ledger-client` component defines a custom SBT plugin for
|
2021-01-08 15:50:15 +03:00
|
|
|
handling Daml code. It covers compilation to LF, packaging to DAR, Scala code
|
|
|
|
generation, and executing the Daml sandbox. This plugin was ported to Bazel as
|
2019-04-04 11:33:38 +03:00
|
|
|
a set of custom Bazel rules defined in [`rules_daml`][rules_daml]. Refer to the
|
|
|
|
[user guide][bazel_user_guide] or the [API docs][bazel-api-documentation] for
|
|
|
|
details.
|
|
|
|
|
2019-08-26 11:50:11 +03:00
|
|
|
[rules_daml]: https://github.com/digital-asset/daml/tree/ea02814b343e4754c70a8718cb14657d6c51915f/rules_daml
|
2019-04-04 11:33:38 +03:00
|
|
|
|
|
|
|
## Java Runtime and Toolchain
|
|
|
|
|
|
|
|
Bazel is itself written in Java. By default Bazel will use its own Java runtime
|
|
|
|
and toolchain to build and execute JVM targets. At the time of writing the
|
|
|
|
Bazel executable provided in the dev-env uses the same Oracle JDK version 8 as
|
|
|
|
the rest of the dev-env.
|
|
|
|
|
|
|
|
Bazel supports specifying a different Java runtime and toolchain for JVM
|
|
|
|
targets via the rules `java_runtime` and `java_toolchain`. Refer to the
|
|
|
|
[official documentation][bazel_java_rules] for further information. Note, at
|
|
|
|
the time of writing the documentation does not display these rules, see [issue
|
|
|
|
6619][bazel_issue_6619]. They can be viewed on the [web
|
|
|
|
archive][bazel_java_rules_webarchive] instead.
|
|
|
|
|
|
|
|
[bazel_java_rules]: https://docs.bazel.build/versions/master/be/java.html#java-rules
|
|
|
|
[bazel_issue_6619]: https://github.com/bazelbuild/bazel/issues/6619
|
|
|
|
[bazel_java_rules_webarchive]: https://web.archive.org/web/20181021162843/https://docs.bazel.build/versions/master/be/java.html
|
|
|
|
|
|
|
|
## Bazel API Documentation
|
|
|
|
|
|
|
|
[bazel-api-documentation]: #bazel-api-documentation
|
|
|
|
|
|
|
|
- The Bazel API documentation to Bazel rules defined in this repository can be
|
|
|
|
viewed by executing the following command:
|
|
|
|
```
|
|
|
|
$ bazel-api-docs
|
|
|
|
```
|
|
|
|
And browsing to http://localhost:8000.
|
|
|
|
See `bazel-api-docs -h` for further instructions.
|
|
|
|
- External Bazel rules API documentation is listed in the
|
|
|
|
[official Bazel documentation][bazel_encyclopedia].
|
|
|
|
|
|
|
|
[bazel_encyclopedia]: https://docs.bazel.build/versions/master/be/overview.html
|