Bazel is a tool for building and testing software and can handle large, multi-language projects at scale.
This project defines core build rules for Scala that can be used to build, test, and package Scala projects.
- scala_library
- scala_macro_library
- scala_binary
- scala_test
- scala_repl
- scala_library_suite
- scala_test_suite
- thrift_library
- scala_proto_library
- scala_toolchain
- scala_import
- scala_doc
Install Bazel, preferably using the Bazelisk wrapper. See the compatbile Bazel versions section to select a suitable Bazel version.
Add the following configuration snippet to your WORKSPACE
file and update
versions with their sha256s if needed. This snippet is designed to ensure that
users pick up the correct order of dependencies for rules_scala
. If you want
to override any of the following dependency versions, make sure to load()
them
before calling rules_scala_dependencies()
.
# WORKSPACE
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# See /~https://github.com/bazelbuild/rules_scala/releases for up to date version
# information, including `<VERSION>` and `<SHASUM>` values.
http_archive(
name = "rules_scala", # Can be "io_bazel_rules_scala" if you still need it.
sha256 = "<SHASUM>",
strip_prefix = "rules_scala-<VERSION>",
url = "/~https://github.com/bazelbuild/rules_scala/releases/download/<VERSION>/rules_scala-<VERSION>.tar.gz",
)
load("@rules_scala//scala:deps.bzl", "rules_scala_dependencies")
rules_scala_dependencies()
# In `rules_scala` 7.x, `scala/deps.bzl` imports `rules_java` 7.x. This
# statement will change for `rules_scala` 8.x, which will use `rules_java` 8.x.
load(
"@rules_java//java:repositories.bzl",
"rules_java_dependencies",
"rules_java_toolchains",
)
rules_java_dependencies()
load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
bazel_skylib_workspace()
# If you need a specific `rules_python` version, specify it here.
# Otherwise you may get the version defined in the `com_google_protobuf` repo.
# We use 0.38.0 to maintain compatibility with Bazel 6.5.0; this will change in
# rules_scala 8.0.0.
http_archive(
name = "rules_python",
sha256 = "ca2671529884e3ecb5b79d6a5608c7373a82078c3553b1fa53206e6b9dddab34",
strip_prefix = "rules_python-0.38.0",
url = "/~https://github.com/bazelbuild/rules_python/releases/download/0.38.0/rules_python-0.38.0.tar.gz",
)
load("@rules_python//python:repositories.bzl", "py_repositories")
py_repositories()
# Note that `rules_java` 8.x suggests loading `protobuf_deps()` after
# `rules_java_dependencies` and before `rules_java_toolchains()`:
# - /~https://github.com/bazelbuild/rules_java/releases/tag/8.9.0
#
# `rules_java` 7.x also works with this ordering.
load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
protobuf_deps()
rules_java_toolchains()
load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies")
rules_proto_dependencies()
load("@rules_proto//proto:setup.bzl", "rules_proto_setup")
rules_proto_setup()
load("@rules_proto//proto:toolchains.bzl", "rules_proto_toolchains")
rules_proto_toolchains()
load("@rules_scala//:scala_config.bzl", "scala_config")
# Stores the selected Scala version and other configuration parameters.
#
# 2.12 is the default version. Use other versions by passing them explicitly:
#
# scala_config(scala_version = "2.13.16")
#
# You may define your own custom toolchain using Maven artifact dependencies
# configured by your `WORKSPACE` file, imported using external loader like
# /~https://github.com/bazelbuild/rules_jvm_external.
scala_config()
load(
"@rules_scala//scala:toolchains.bzl",
"scala_register_toolchains",
"scala_toolchains",
)
# Defines a default toolchain repo for the configured Scala version that
# loads Maven deps like the Scala compiler and standard libs. On production
# projects, you may consider defining a custom toolchain to use your project's
# required dependencies instead.
#
# Optional builtin rules_scala toolchains may be configured by setting
# parameters as documented in the `scala_toolchains()` docstring.
scala_toolchains(
scalatest = True,
)
scala_register_toolchains()
The above configuration snippet reflects important changes since rules_scala
v6.x:
-
rules_scala
no longer requires theio_bazel_rules_scala
repository name unless yourBUILD
files or those of your dependencies require it (#1696). -
rules_scala
v7.0.0 introduces a newscala_toolchains()
API that is very different fromrules_scala
6. For details on what's changed, see the New 'scala_toolchains()' API for 'WORKSPACE' section below.
Add the following to your BUILD
files to make the scala_*
rules available:
load(
"@rules_scala//scala:scala.bzl",
"scala_binary",
"scala_library",
"scala_test",
)
To run with a persistent worker (much faster), add the following to
your .bazelrc
file:
build --strategy=Scalac=worker
build --worker_sandboxing
To produce a combined coverage report:
bazel coverage \
--combined_report=lcov \
--coverage_report_generator="@bazel_tools//tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator:Main" \
//...
This should produce a single bazel-out/_coverage/_coverage_report.dat
from all coverage files that are generated.
You can extract information from your coverage reports with
lcov
:
# For a summary:
lcov --summary your-coverage-report.dat
# For details:
lcov --list your-coverage-report.dat
If you prefer an HTML report, then you can use genhtml
provided also by the lcov
package.
Coverage support has been only tested with ScalaTest.
Please check coverage.md for more details on coverage support.
rules_scala
supports the last two released minor versions for each of Scala 2.11, 2.12, 2.13.
Previous minor versions may work but are supported only on a best effort basis.
The Getting started section illustrates how to select the default Scala version and configure its dependencies.
You can define your own custom scala_toolchain by
calling setup_scala_toolchain()
with dependencies that you specify.
Note: Toolchains are a more flexible way to configure dependencies, so you should prefer that way.
Please also note, that the overriden_artifacts
parameter is likely to be removed in the future.
rules_scala
supports configuring multiple Scala versions and offers target-level control of which one to use.
Please check cross-compilation.md for more details on cross-compilation support.
Bazel compatibility is tied directly to the versions of protobuf
required by
Bazel and rules_java
, and their compatibility with scalabp/ScalaPB Maven artifacts. For extensive analysis,
see #1647.
The Bazel versions and dependency versions in the table below represent the maximum available at the time of writing.
-
For the actual versions used by
rules_scala
, see scala/deps.bzl. -
See the configuration file for the exact Bazel versions verified with the continuous-integration builds.
Bazel/Dependency | rules_scala 7.x |
rules_scala 8.x(Coming soon! See #1482 and #1652.) |
---|---|---|
Bazel versions using Bzlmod (Coming soon! See #1482.) |
6.5.0, 7.5.0 | 7.5.0, 8.x |
Bazel versions using WORKSPACE |
6.5.0, 7.5.0 | 6.5.0, 7.5.0, 8.x (see the notes on 6.5.0 compatibility) |
protobuf |
v21.7 (can support up to v25.5) |
v29.3 |
abseil-cpp |
20220623.1 | 20250127.0 |
rules_java |
7.12.4 | 8.x |
ScalaPB |
0.11.17 (0.9.8 for Scala 2.11) |
1.0.0-alpha.1 |
Usage with bazel-deps
Bazel-deps allows you to generate bazel dependencies transitively for maven artifacts. Generally we don't want bazel-deps to fetch
scala artifacts from maven but instead use the ones we get from calling scala_repositories
. The artifacts can be overridden in the
dependencies file used by bazel-deps:
replacements:
org.scala-lang:
scala-library:
lang: scala/unmangled
target: "@io_bazel_rules_scala_scala_library//:io_bazel_rules_scala_scala_library"
scala-reflect:
lang: scala/unmangled
target: "@io_bazel_rules_scala_scala_reflect//:io_bazel_rules_scala_scala_reflect"
scala-compiler:
lang: scala/unmangled
target: "@io_bazel_rules_scala_scala_compiler//:io_bazel_rules_scala_scala_compiler"
org.scala-lang.modules:
scala-parser-combinators:
lang: scala
target:
"@io_bazel_rules_scala_scala_parser_combinators//:io_bazel_rules_scala_scala_parser_combinators"
scala-xml:
lang: scala
target:
"@io_bazel_rules_scala_scala_xml//:io_bazel_rules_scala_scala_xml"
See Publish your Scala Libraries to a Maven Repository.
rules_scala
supports multiple dependency modes including strict and unused dependency tracking. See
Dependency Tracking for more info.
To make the ruleset more flexible and configurable, we introduce a phase architecture. By using a phase architecture, where rule implementations are defined as a list of phases that are executed sequentially, functionality can easily be added (or modified) by adding (or swapping) phases.
Phases provide 3 major benefits:
- Consumers are able to configure the rules to their specific use cases by defining new phases within their workspace without impacting other consumers.
- Contributors are able to implement new functionalities by creating additional default phases.
- Phases give us more clear idea what steps are shared across rules.
See Customizable Phase for more info.
Build main sources only:
bazel build //src/...
Run all smaller tests:
bazel test //test/...
To run the full test suite:
bash test_all.sh
Note: bazel test //...
will not work since we have a sub-folder on the
root folder which is meant to be used in a failure scenario in the integration
tests. Similarly, to only build you should use bazel build //src/...
due to
that folder.
The main objective of rules_scala
7.x is to enable existing users to migrate
to Bazel 7 and Bzlmod. To facilitate a gradual migration, it remains
compatible with both WORKSPACE
and Bzlmod. However, it contains the following
breaking changes when upgrading from rules_scala
6.x.
rules_scala
7.0.0 replaces existing *_repositories()
and *_toolchains()
macros with the combination of rules_scala_dependencies()
,
scala_toolchains()
, and scala_register_toolchains()
.
These macros no longer exist:
jmh_repositories()
junit_repositories()
junit_toolchain()
rules_scala_setup()
rules_scala_toolchain_deps_repositories()
scala_proto_default_repositories()
scala_proto_register_enable_all_options_toolchain()
scala_proto_register_toolchains()
scala_proto_repositories()
scala_register_unused_deps_toolchains()
scala_repositories()
scalafmt_default_config()
scalafmt_repositories()
scalatest_repositories()
scalatest_toolchain()
specs2_junit_repositories()
specs2_repositories()
specs2_version()
twitter_scrooge()
Replace toolchain configurations like the following:
load(
"@rules_scala//scala:scala.bzl",
"rules_scala_setup",
"rules_scala_toolchain_deps_repositories",
)
rules_scala_setup()
rules_scala_toolchain_deps_repositories(fetch_sources = True)
# Other dependency declarations...
load("@rules_scala//:scala_config.bzl", "scala_config")
scala_config(scala_version = "2.13.16")
load(
"//testing:scalatest.bzl",
"scalatest_repositories",
"scalatest_toolchain",
)
scalatest_repositories()
scalatest_toolchain()
load(
"//scala/scalafmt:scalafmt_repositories.bzl",
"scalafmt_default_config",
"scalafmt_repositories",
)
scalafmt_default_config()
scalafmt_repositories()
with calls to rules_scala_dependencies()
, scala_toolchains()
(with the
appropriate parameters set), and scala_register_toolchains()
:
load("@rules_scala//scala:deps.bzl", "rules_scala_dependencies")
rules_scala_dependencies()
# See the `WORKSPACE` configuration snippet from the "Getting started" section
# above for other dependency declarations.
load("@rules_scala//:scala_config.bzl", "scala_config")
scala_config(scala_version = "2.13.16")
load(
"@rules_scala//scala:toolchains.bzl",
"scala_register_toolchains",
"scala_toolchains",
)
# Note that `rules_scala` toolchain repos are _always_ configured.
scala_toolchains(
scalafmt = True,
scalatest = True,
)
scala_register_toolchains()
See the scala_toolchains()
docstring for the
parameter list, which is almost in complete correspondence with parameters from
the previous macros. The WORKSPACE
files in this repository also provide many
examples.
Almost all rules_scala
toolchains are automatically configured and registered by
scala_toolchains()
and scala_register_toolchains()
. There are two toolchain
macro replacements that require special handling.
The first is replacing scala_proto_register_enable_all_options_toolchain()
with the following scala_toolchains()
parameters:
scala_toolchains(
scala_proto = True,
scala_proto_enable_all_options = True,
)
The other is replacing scala_register_unused_deps_toolchains()
with an
explicit register_toolchains()
call:
register_toolchains(
"@rules_scala//scala:unused_dependency_checker_error_toolchain",
)
In WORKSPACE
, this register_toolchains()
call must come before calling
scala_register_toolchains()
to ensure this toolchain takes precedence. The
same exact call will also work in MODULE.bazel
.
The MODULE.bazel
file from rules_scala
will automatically call
register_toolchains()
for toolchains configured via its scala_deps
module
extension. However, you must register explicitly in your MODULE.bazel
file any
toolchains that you want to take precedence over the toolchains configured by
scala_deps
.
Since @io_bazel_rules_scala
is no longer hardcoded in rules_scala
internals,
we've shortened @io_bazel_rules_scala_config
to @rules_scala_config
. This
shouldn't affect most users, but it may break some builds using
@io_bazel_rules_scala_config
to define custom cross-compilation targets.
If you can't fix uses of @io_bazel_rules_scala_config
in your own project
immediately, you can remap @rules_scala_config
via use_repo()
:
scala_config = use_extension(
"@rules_scala//scala/extensions:config.bzl",
"scala_config",
)
use_repo(scala_config, io_bazel_rules_scala_config = "rules_scala_config")
If any of your dependencies still require @io_bazel_rules_scala_config
, use
one of the following mechanisms to override it with @rules_scala_config
:
For bazel_dep()
dependencies, use override_repo()
to
override @io_bazel_rules_scala_config
with @rules_scala_config
:
bazel_dep(name = "foo", version = "1.0.0")
foo_ext = use_extension("@foo//:ext.bzl", "foo_ext")
override_repo(foo_ext, io_bazel_rules_scala_config = "rules_scala_config")
For archive_override()
and git_override()
dependencies, use the
repo_mapping
attribute passed through to the underlying http_archive()
and git_repository()
rules:
archive_override(
...
repo_mapping = {
"@io_bazel_rules_scala_config": "@rules_scala_config",
}
...
)
Use the repo_mapping
attribute of http_archive()
or
git_repository()
:
http_archive(
...
repo_mapping = {
"@io_bazel_rules_scala_config": "@rules_scala_config",
}
...
)
The upcoming Bzlmod implementation will funnel through the scala_toolchains()
macro as well, ensuring maximum compatibility with WORKSPACE
configurations.
The equivalent Bzlmod configuration for the scala_toolchains()
configuration
above would be:
bazel_dep(name = "rules_scala", version = "7.0.0")
scala_config = use_extension(
"@rules_scala//scala/extensions:config.bzl",
"scala_config",
)
scala_config.settings(scala_version = "2.13.16")
scala_deps = use_extension(
"@rules_scala//scala/extensions:deps.bzl",
"scala_deps",
)
scala_deps.toolchains(
scalafmt = True,
scalatest = True,
)
The module extensions will call scala_config()
and scala_toolchains()
respectively. The MODULE.bazel
file for rules_scala
declares its own
dependencies via bazel_dep()
, allowing Bazel to resolve versions according to
the main repository/root module configuration. It also calls
register_toolchains()
, so you don't have to (unless you want to
register a specific toolchain to resolve first).
The MODULE.bazel
files in this repository will also provide many examples
(when they land per #1482).
Any program compiled with an external repo asset in its 'resources' attribute
will need to strip the 'external/' and repo name components from its
path. For example, the path for resources = ["@some_external_repo//:resource.txt"]
would change thus:
- Before:
external/some_external_repo/resource.txt
- After:
resource.txt
This avoids encoding repo names or any other Bazel system knowledge in the compiled artifacts. This is especially important under Bzlmod, because the generated path would otherwise contain the canonical repo name, upon which users should never depend.
Per #1660, rules_java
7.10.0 and later precipitate the
need to replace @bazel_tools//tools/jdk
targets with corresponding
@rules_java//toolchains
targets. Fix any targets broken by this rules_java
upgrade by doing a global search and replace.
However, @bazel_tools//tools/jdk:toolchain_type
dependencies must remain for
now, as there's not yet a corresponding toolchain_type()
target
in @rules_java
.
Under Bzlmod, repos are only visible to the module extension that creates them,
unless the MODULE.bazel
file brings them into scope with
use_repo()
. This can
lead to errors like those from the following example, which originally called
setup_scala_toolchain()
under Bzlmod:
load("@rules_scala//scala:scala.bzl", "setup_scala_toolchain")
setup_scala_toolchain(
name = "custom_scala_toolchain",
scalacopts = [
"-Wunused:all",
],
strict_deps_mode = "error",
unused_dependency_checker_mode = "warn",
)
setup_scala_toolchains
is a macro that can take user specified classpath
targets as described in docs/scala_toolchain.md.
Without explicit *_classpath
or *_deps
arguments, setup_scala_toolchain()
defaults to using dependency repositories generated by rules_scala
itself.
This worked under WORKSPACE
, but breaks under Bzlmod, because the builtin
toolchain dependency repos are no longer in the project's scope by default:
ERROR: no such package
'@@[unknown repo 'org_scala_sbt_compiler_interface_3_3_5'
requested from @@]//':
The repository '@@[unknown repo 'org_scala_sbt_compiler_interface_3_3_5'
requested from @@]' could not be resolved:
No repository visible as '@org_scala_sbt_compiler_interface_3_3_5'
In this case, where the toolchain only sets different compiler options, the best
fix is to use scala_toolchain
directly instead. Its
underlying BUILD
rule uses builtin toolchain dependencies via existing targets
visible within rules_scala
, without forcing users to import them:
load("@rules_scala//scala:scala_toolchain.bzl", "scala_toolchain")
scala_toolchain(
name = "custom_scala_toolchain_impl",
scalacopts = [
"-Ywarn-unused",
],
strict_deps_mode = "error",
unused_dependency_checker_mode = "warn",
)
toolchain(
name = "custom_scala_toolchain",
toolchain = ":custom_scala_toolchain_impl",
toolchain_type = "@rules_scala//scala:toolchain_type",
visibility = ["//visibility:public"],
)
A big part of the Bzlmodification work involved enabling rules_scala
to
generate and register toolchains without forcing users to bring their
dependencies into scope. However, another way to fix this specific problem is to
call use_repo
for every builtin repository needed by the
setup_scala_toolchain()
call.
The main objective of 8.x will be to enable existing users to migrate to Bazel
8 and Bzlmod. To facilitate a gradual migration, it will remain compatible
with both WORKSPACE
and Bzlmod. However, it will contain the following
breaking changes when upgrading from rules_scala
7.x.
This isn't actually a rules_scala
breakage, but a Bazel 8 breakage encountered
while preparing rules_scala
for Bazel 8 in #1652.
bazelbuild/bazel#25198 describes how the semantics of some instances of
$(location)
changed, and how changing these particular instances to
$(rootpath)
fixed them.
The good news is that replacing such instances $(location)
with $(rootpath)
is backwards compatible to Bazel 6.5.0 and 7.5.0. Updating them now will ensure
future compatibility.
rules_scala
8.0.0 will not support Bzlmod with Bazel 6.5.0 because
Bazel 6.5.0 doesn't support 'use_repo_rule', which
'rules_jvm_external' >= 6.3 requires.
WORKSPACE
builds will continue to work with Bazel 6.5.0, but not out of the
box. Per #1647, using Bazel 6.5.0 with rules_scala
8.x
will require adding the following flags to .bazelrc
, required by the newer
abseil-cpp
version used by protobuf
:
common --enable_platform_specific_config
common:linux --cxxopt=-std=c++17
common:linux --host_cxxopt=-std=c++17
common:macos --cxxopt=-std=c++17
common:macos --host_cxxopt=-std=c++17
common:windows --cxxopt=/std=c++17
common:windows --host_cxxopt=/std=c++17
Note that this example uses common:
config settings instead of build:
. This
seems to prevent invalidating the action cache between bazel
runs, which
improves performance.
rules_scala
7.0.0 and 8.0.0 will have different
compatibility_level
values for their module()
directives. This is due to the gap in supported protobuf
versions documented
in #1647 (between v25.5 and v28) and dropping support for
Bazel 6.5.0 Bzlmod builds.
This will ensure any users attempting to mismatch protobuf
and rules_scala
versions will break during module resolution, rather than during a later
execution step. (Though, as described in #1647, there are
now measures in place to cause the build to crash during a mismatch instead of
hanging.)
The concept of proper compatibility_level
usage is still up for discussion in
bazelbuild/bazel#24302. The compatibility_level
for rules_scala
implementation will track major version numbers (per semantic
versioning), and clearly document the reason for the level
bump. If a version bump may break builds for any known reason, we will explain
why up front instead of waiting for users to be surprised.
A comment from #1647 illustrates how 'rules_erlang' fails due to 'compatibility_level' conflicts. The 'rules_erlang' 3.0.0 release notes describe the breaking changes. This seems like a reasonable model to follow.
See CONTRIBUTING.md for more info.
Here's a (non-exhaustive) list of companies that use rules_scala
in production. Don't see yours? You can add it in a PR!