Read Quick start for an information on how to use cross compilation. The remaining sections contain more detailed information, useful especially for toolchain & rule developers.
scala_config
repository rule accepts two parameters related to Scala version:
scala_version
– a single, default version;scala_versions
– a list of versions to make available for use.
The first one, scala_version
, will be used as a default, but it can be overridden for specific targets for any version from the scala_versions
.
Multiple rules, such as:
support such override via the scala_version
attribute, e.g.:
scala_library(
name = ...
...
scala_version = "2.12.18",
...
)
For this library and all its dependencies 2.12.18 compiler will be used, unless explicitly overridden again in another target.
scala_config
creates the repository @io_bazel_rules_scala_config
.
File created there, config.bzl
, consists of many variables. In particular:
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"]
.
Configured SCALA_VERSIONS
correspond to allowed values of build setting.
@io_bazel_rules_scala_config
in its root package defines the following build setting:
string_setting(
name = "scala_version",
build_setting_default = "3.3.1",
values = ["3.3.1"],
visibility = ["//visibility:public"],
)
...
This build setting can be subject of change by transitions (within allowed values
).
Then for each Scala version we have a config setting:
config_setting(
name = "scala_version_3_3_1",
flag_values = {":scala_version": "3.3.1"},
)
...
The name
of 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.
Don't rely on SCALA_VERSION
as it represents the default Scala version, not necessarily the one that is currently requested.
If you need to customize the behavior for specific Scala version, there are two scenarios.
If you have an access to the Scala toolchain (@io_bazel_rules_scala//scala:toolchain_type
), there is scala_version
field provided in there:
def _rule_impl(ctx):
...
ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].scala_version
...
In BUILD files, you need to use the config settings with select()
.
Majority of use cases is covered by the select_for_scala_version
utility macro.
If more flexibility is needed, you can always write the select manually.
See example usage of the select_for_scala_version
:
load("@io_bazel_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 complete documentation in the scala_cross_version_select.bzl file
An example usage of select()
to provide custom dependency for specific Scala version:
deps = select({
"@io_bazel_rules_scala_config//:scala_version_3_3_1": [...],
...
})
For more complex logic, you can extract it to a .bzl
file:
def srcs(scala_version):
if scala_version.startswith("2"):
...
...
and then in the BUILD
file:
load("@io_bazel_rules_scala//:scala_cross_version.bzl", "version_suffix")
load("@io_bazel_rules_scala_config//:config.bzl", "SCALA_VERSIONS")
load("....bzl", "srcs")
scala_library(
...
srcs = select({
"@io_bazel_rules_scala_config//:scala_version" + version_suffix(v): srcs(v)
for v in SCALA_VERSIONS
}),
...
)
To use other than default version of Scala, you need to change the current @io_bazel_rules_scala_config//:scala_version
build setting.
Simple transition, setting the Scala version to one found in scala_version
attribute:
def _scala_version_transition_impl(settings, attr):
if attr.scala_version:
return {"@io_bazel_rules_scala_config//:scala_version": attr.scala_version}
else:
return {}
scala_version_transition = transition(
implementation = _scala_version_transition_impl,
inputs = [],
outputs = ["@io_bazel_rules_scala_config//:scala_version"],
)
To use it in a rule, use the scala_version_transition
as cfg
and use toolchain_transition_attr
in attrs
:
load("@io_bazel_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(
...
toolchain_transition_attr,
...
),
...
cfg = scala_version_transition,
incompatible_use_toolchain_transition = True,
...
)
Standard toolchain resolution procedure determines which toolchain to use for Scala targets.
Toolchain should declare its compatibility with Scala version by using target_settings
attribute of the toolchain
rule:
toolchain(
...
target_settings = ["@io_bazel_rules_scala_config//:scala_version_3_3_1"],
...
)
rules_scala
consists of many toolchains implementing various toolchain types.
Their support level for cross-build setup varies.
We can distinguish following tiers:
- No
target_settings
set – not migrated, will work on the defaultSCALA_VERSION
; undefined behavior on other versions.- (all toolchains not mentioned elsewhere)
target_settings
set to theSCALA_VERSION
– not fully migrated; will work only on the defaultSCALA_VERSION
and will fail the toolchain resolution on other versions.- (no development in progress)
- Multiple toolchain instances with
target_settings
corresponding to each ofSCALA_VERSIONS
– fully migrated; will work in cross-build setup.