Cross compilation support
June 12, 2026 · View on GitHub
Read Quick start for information on how to use cross compilation. The remaining sections contain more detailed information, useful especially for toolchain and rule developers.
Quick start
The scala_config module extension (orWORKSPACE macro) accepts two parameters
that specify the the Scala versions supported within the project:
scala_version– a single default versionscala_versions– a list of versions supported or required by the project
MODULE.bazel
# MODULE.bazel
scala_config = use_extension(
"@rules_scala//scala/extensions:config.bzl",
"scala_config",
)
scala_config.settings(
scala_version = "2.13.18",
# No need to include `scala_version` in `scala_versions`.
scala_versions = [
"2.11.12",
"2.12.21",
"3.1.3",
"3.2.2",
"3.3.8",
],
)
WORKSPACE (Legacy support)
load("@rules_scala//:scala_config.bzl", "scala_config")
scala_config(
scala_version = "3.1.3",
# You _must_ include `scala_version` in `scala_versions`.
scala_versions = [
"2.11.12",
"2.12.21",
"2.13.18",
"3.1.3",
"3.2.2",
"3.3.8",
],
)
The first parameter, scala_version, defines the default version of Scala to
use when building the project. Values from scala_versions can override the
default in one of two ways:
- The
--repo_env=SCALA_VERSION=...command line flag overrides the default for the entire build. - The
scala_versionbuild rule attribute overrides the Scala version used for a specific target and its dependencies.
Multiple rules, such as:
support scala_version overrides, e.g.:
scala_library(
name = ...
...
scala_version = "2.12.18",
...
)
The above scala_library and all its dependencies will use the Scala 2.12.18
compiler, unless explicitly overridden by another target that depends on this
one.
Version configuration
The scala_config module extension (or WORKSPACE macro) creates the
@rules_scala_config repository. Its generated config.bzl file contains
several variables, including:
SCALA_VERSION– representing the default Scala version, e.g.,"3.3.1"SCALA_VERSIONS– representing all configured Scala versions, e.g.,["2.12.18", "3.3.1"]
Build settings
Each element of SCALA_VERSIONS corresponds to an allowed build
setting
value.
scala_version
The root package of @rules_scala_config defines the following build setting
(specifically, a 'string_setting()' from '@bazel_skylib'):
string_setting(
name = "scala_version",
build_setting_default = "3.3.1",
values = ["2.12.18", "3.3.1"],
visibility = ["//visibility:public"],
)
This defines values allowed by the custom user-defined transition described in the Requesting a specific version for a custom 'rule' section below.
Config settings
For each Scala version in the above string_setting(), we have a config
setting:
config_setting(
name = "scala_version_3_3_1",
flag_values = {":scala_version": "3.3.1"},
)
The name of the config_setting corresponds to "scala_version" + version_suffix(scala_version). One may use this config setting in select()
(e.g., to provide dependencies relevant to a currently used Scala version).
Version-dependent behavior
Don't rely on SCALA_VERSION as it represents the default Scala version, not
necessarily the one that is currently requested.
There are two scenarios for customizing behavior based on a specific Scala version.
From toolchain
@rules_scala//scala:toolchain_type provides the scala_version field:
def _rule_impl(ctx):
...
ctx.toolchains["@rules_scala//scala:toolchain_type"].scala_version
...
From config setting
In BUILD files, you need to use the config settings with select(). The
majority of use cases are covered by the select_for_scala_version() utility
macro. If more flexibility is needed, you can always write the select manually.
Using the select_for_scala_version() macro
load(
"@rules_scala//:scala_cross_version_select.bzl",
"select_for_scala_version",
)
scala_library(
...
srcs = select_for_scala_version(
before_3_1 = [
# for Scala version < 3.1
],
between_3_1_and_3_2 = [
# for 3.1 ≤ Scala version < 3.2
],
between_3_2_and_3_3_1 = [
# for 3.2 ≤ Scala version < 3.3.1
],
since_3_3_1 = [
# for 3.3.1 ≤ Scala version
],
)
...
)
See the complete documentation in the scala_cross_version_select.bzl file
Using a manually crafted select()
deps = select({
"@rules_scala_config//:scala_version_3_3_1": [...],
...
})
For more complex logic, define a macro taking a scala_version argument in a
.bzl file:
def srcs(scala_version):
if scala_version.startswith("2"):
...
...
and then load() the macro in a BUILD file:
load(":my_macros.bzl", "srcs")
load("@rules_scala//:scala_cross_version.bzl", "version_suffix")
load("@rules_scala_config//:config.bzl", "SCALA_VERSIONS")
scala_library(
...
srcs = select({
"@rules_scala_config//:scala_version" + version_suffix(v): srcs(v)
for v in SCALA_VERSIONS
}),
...
)
Requesting a specific version for a custom rule
To enable a rule to use a version of Scala other than the default, first
assign the desired alternative versions to the scala_versions configuration
parameter. scala_version_transition from scala/scala_cross_version.bzl then selects one of the scala_versions so
configured.
def _scala_version_transition_impl(settings, attr):
if attr.scala_version:
return {"@rules_scala_config//:scala_version": attr.scala_version}
else:
return {}
scala_version_transition = transition(
implementation = _scala_version_transition_impl,
inputs = [],
outputs = ["@rules_scala_config//:scala_version"],
)
In your own rule definition,
assign the scala_version_transition to the cfg attribute and include the
toolchain_transition_attr elements in attrs. For an example, see
make_scala_library() from scala/private/rules/scala_library.bzl:
load(
"@rules_scala//scala:scala_cross_version.bzl",
"scala_version_transition",
"toolchain_transition_attr",
)
...
_scala_library_attrs.update(toolchain_transition_attr)
def make_scala_library(*extras):
return rule(
attrs = _dicts.add(
_scala_library_attrs,
...
),
...
cfg = scala_version_transition,
...
)
Now your rule can take a scala_version parameter to ensure it builds with a
specific Scala version. See test_cross_build/version_specific/BUILD for examples of this, such as:
scala_library(
name = "since_3_3",
srcs = ["since_3_3.scala"],
scala_version = "3.3.8",
)
scala_library(
name = "before_3_3",
srcs = ["before_3_3.scala"],
scala_version = "3.2.2",
)
# What's new in 3.2
scala_library(
name = "since_3_2",
srcs = ["since_3_2.scala"],
scala_version = "3.2.2",
)
scala_library(
name = "before_3_2",
srcs = ["before_3_2.scala"],
scala_version = "3.1.3",
)
Toolchains
The standard toolchain resolution procedure determines which toolchain to use for Scala targets.
Each toolchain should declare its compatibility with a specific Scala version by
using the target_settings attribute of the toolchain rule:
toolchain(
...
target_settings = ["@rules_scala_config//:scala_version_3_3_1"],
...
)
Cross-build support tiers
rules_scala consists of many toolchains implementing various toolchain types.
Their support level for cross-build setups varies.
We can distinguish following tiers:
-
No
target_settingsset – not migrated, will work on the defaultSCALA_VERSION; undefined behavior on other versions.- (all toolchains not mentioned elsewhere)
-
target_settingsset to theSCALA_VERSION– not fully migrated; will work only on the defaultSCALA_VERSIONand will fail the toolchain resolution on other versions.- (no development in progress)
-
Multiple toolchain instances with
target_settingscorresponding to each ofSCALA_VERSIONS– fully migrated; will work in cross-build setup.