|
| 1 | +# Cross compilation support |
| 2 | + |
| 3 | +Read *Quick start* for an information on how to use cross compilation. |
| 4 | +The remaining sections contain more detailed information, useful especially for toolchain & rule developers. |
| 5 | + |
| 6 | +## Quick start |
| 7 | +`scala_config` repository rule accepts two parameters related to Scala version: |
| 8 | +* `scala_version` – a single, default version; |
| 9 | +* `scala_versions` – a list of versions to make available for use. |
| 10 | + |
| 11 | +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`. |
| 12 | + |
| 13 | +Multiple rules, such as: |
| 14 | +- [scala_library](/scala/private/rules/scala_library.bzl) |
| 15 | +- [scala_binary](/scala/private/rules/scala_binary.bzl) |
| 16 | +- [scala_repl](/scala/private/rules/scala_repl.bzl) |
| 17 | +- [scala_test](/scala/private/rules/scala_test.bzl) |
| 18 | + |
| 19 | +support such override via the `scala_version` attribute, e.g.: |
| 20 | +```starlark |
| 21 | +scala_library( |
| 22 | + name = ... |
| 23 | + ... |
| 24 | + scala_version = "2.12.18", |
| 25 | + ... |
| 26 | +) |
| 27 | +``` |
| 28 | + |
| 29 | +For this library and all its dependencies 2.12.18 compiler will be used, unless explicitly overridden again in another target. |
| 30 | + |
| 31 | +## Version configuration |
| 32 | + |
| 33 | +`scala_config` creates the repository `@io_bazel_rules_scala_config`. |
| 34 | +File created there, `config.bzl`, consists of many variables. In particular: |
| 35 | +* `SCALA_VERSION` – representing the default Scala version, e.g. `"3.3.1"`; |
| 36 | +* `SCALA_VERSIONS` – representing all configured Scala versions, e.g. `["2.12.18", "3.3.1"]`. |
| 37 | + |
| 38 | + |
| 39 | +## Build settings |
| 40 | +Configured `SCALA_VERSIONS` correspond to allowed values of [build setting](https://bazel.build/extending/config#user-defined-build-setting). |
| 41 | + |
| 42 | +### `scala_version` |
| 43 | +`@io_bazel_rules_scala_config` in its root package defines the following build setting: |
| 44 | +```starlark |
| 45 | +string_setting( |
| 46 | + name = "scala_version", |
| 47 | + build_setting_default = "3.3.1", |
| 48 | + values = ["3.3.1"], |
| 49 | + visibility = ["//visibility:public"], |
| 50 | +) |
| 51 | +... |
| 52 | +``` |
| 53 | +This build setting can be subject of change by [transitions](https://bazel.build/extending/config#user-defined-transitions) (within allowed `values`). |
| 54 | + |
| 55 | +### Config settings |
| 56 | +Then for each Scala version we have a [config setting](https://bazel.build/extending/config#build-settings-and-select): |
| 57 | +```starlark |
| 58 | +config_setting( |
| 59 | + name = "scala_version_3_3_1", |
| 60 | + flag_values = {":scala_version": "3.3.1"}, |
| 61 | +) |
| 62 | +... |
| 63 | +``` |
| 64 | +The `name` of `config_setting` corresponds to `"scala_version" + version_suffix(scala_version)`. |
| 65 | +One may use this config setting in `select()` e.g. to provide dependencies relevant to a currently used Scala version. |
| 66 | + |
| 67 | + |
| 68 | +## Version-dependent behavior |
| 69 | +Don't rely on `SCALA_VERSION` as it represents the default Scala version, not necessarily the one that is currently requested. |
| 70 | + |
| 71 | +If you need to customize the behavior for specific Scala version, there are two scenarios. |
| 72 | + |
| 73 | +### From toolchain |
| 74 | +If you have an access to the Scala toolchain (`@io_bazel_rules_scala//scala:toolchain_type`), there is `scala_version` field provided in there: |
| 75 | +```starlark |
| 76 | +def _rule_impl(ctx): |
| 77 | + ... |
| 78 | + ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].scala_version |
| 79 | + ... |
| 80 | +``` |
| 81 | + |
| 82 | +### From config setting |
| 83 | +In BUILD files, you need to use the config settings with `select()`. |
| 84 | +Majority of use cases is covered by the `select_for_scala_version` utility macro. |
| 85 | +If more flexibility is needed, you can always write the select manually. |
| 86 | + |
| 87 | +#### With select macro |
| 88 | +See example usage of the `select_for_scala_version`: |
| 89 | + |
| 90 | +```starlark |
| 91 | +load("@io_bazel_rules_scala//:scala_cross_version_select.bzl", "select_for_scala_version") |
| 92 | + |
| 93 | +scala_library( |
| 94 | + ... |
| 95 | + srcs = select_for_scala_version( |
| 96 | + before_3_1 = [ |
| 97 | + # for Scala version < 3.1 |
| 98 | + ], |
| 99 | + between_3_1_and_3_2 = [ |
| 100 | + # for 3.1 ≤ Scala version < 3.2 |
| 101 | + ], |
| 102 | + between_3_2_and_3_3_1 = [ |
| 103 | + # for 3.2 ≤ Scala version < 3.3.1 |
| 104 | + ], |
| 105 | + since_3_3_1 = [ |
| 106 | + # for 3.3.1 ≤ Scala version |
| 107 | + ], |
| 108 | + ) |
| 109 | + ... |
| 110 | +) |
| 111 | +``` |
| 112 | + |
| 113 | +See complete documentation in the [scala_cross_version_select.bzl](/scala/scala_cross_version_select.bzl) file |
| 114 | + |
| 115 | +#### Manually |
| 116 | +An example usage of `select()` to provide custom dependency for specific Scala version: |
| 117 | +```starlark |
| 118 | +deps = select({ |
| 119 | + "@io_bazel_rules_scala_config//:scala_version_3_3_1": [...], |
| 120 | + ... |
| 121 | +}) |
| 122 | +``` |
| 123 | + |
| 124 | +For more complex logic, you can extract it to a `.bzl` file: |
| 125 | +```starlark |
| 126 | +def srcs(scala_version): |
| 127 | + if scala_version.startswith("2"): |
| 128 | + ... |
| 129 | + ... |
| 130 | +``` |
| 131 | +and then in the `BUILD` file: |
| 132 | +```starlark |
| 133 | +load("@io_bazel_rules_scala//:scala_cross_version.bzl", "version_suffix") |
| 134 | +load("@io_bazel_rules_scala_config//:config.bzl", "SCALA_VERSIONS") |
| 135 | +load("....bzl", "srcs") |
| 136 | + |
| 137 | +scala_library( |
| 138 | + ... |
| 139 | + srcs = select({ |
| 140 | + "@io_bazel_rules_scala_config//:scala_version" + version_suffix(v): srcs(v) |
| 141 | + for v in SCALA_VERSIONS |
| 142 | + }), |
| 143 | + ... |
| 144 | +) |
| 145 | +``` |
| 146 | + |
| 147 | + |
| 148 | +## Requesting specific version |
| 149 | +To use other than default version of Scala, you need to change the current `@io_bazel_rules_scala_config//:scala_version` build setting. |
| 150 | + |
| 151 | +Simple transition, setting the Scala version to one found in `scala_version` attribute: |
| 152 | +```starlark |
| 153 | +def _scala_version_transition_impl(settings, attr): |
| 154 | + if attr.scala_version: |
| 155 | + return {"@io_bazel_rules_scala_config//:scala_version": attr.scala_version} |
| 156 | + else: |
| 157 | + return {} |
| 158 | + |
| 159 | +scala_version_transition = transition( |
| 160 | + implementation = _scala_version_transition_impl, |
| 161 | + inputs = [], |
| 162 | + outputs = ["@io_bazel_rules_scala_config//:scala_version"], |
| 163 | +) |
| 164 | +``` |
| 165 | + |
| 166 | +To use it in a rule, use the `scala_version_transition` as `cfg` and use `toolchain_transition_attr` in `attrs`: |
| 167 | +```starlark |
| 168 | +load("@io_bazel_rules_scala//scala:scala_cross_version.bzl", "scala_version_transition", "toolchain_transition_attr") |
| 169 | + |
| 170 | +_scala_library_attrs.update(toolchain_transition_attr) |
| 171 | + |
| 172 | +def make_scala_library(*extras): |
| 173 | + return rule( |
| 174 | + attrs = _dicts.add( |
| 175 | + ... |
| 176 | + toolchain_transition_attr, |
| 177 | + ... |
| 178 | + ), |
| 179 | + ... |
| 180 | + cfg = scala_version_transition, |
| 181 | + incompatible_use_toolchain_transition = True, |
| 182 | + ... |
| 183 | + ) |
| 184 | +``` |
| 185 | + |
| 186 | + |
| 187 | +## Toolchains |
| 188 | +Standard [toolchain resolution](https://bazel.build/extending/toolchains#toolchain-resolution) procedure determines which toolchain to use for Scala targets. |
| 189 | + |
| 190 | +Toolchain should declare its compatibility with Scala version by using `target_settings` attribute of the `toolchain` rule: |
| 191 | + |
| 192 | +```starlark |
| 193 | +toolchain( |
| 194 | + ... |
| 195 | + target_settings = ["@io_bazel_rules_scala_config//:scala_version_3_3_1"], |
| 196 | + ... |
| 197 | +) |
| 198 | +``` |
| 199 | + |
| 200 | +### Cross-build support tiers |
| 201 | +`rules_scala` consists of many toolchains implementing various toolchain types. |
| 202 | +Their support level for cross-build setup varies. |
| 203 | + |
| 204 | +We can distinguish following tiers: |
| 205 | + |
| 206 | +* No `target_settings` set – not migrated, will work on the default `SCALA_VERSION`; undefined behavior on other versions. |
| 207 | + * (all toolchains not mentioned elsewhere) |
| 208 | +* `target_settings` set to the `SCALA_VERSION` – not fully migrated; will work only on the default `SCALA_VERSION` and will fail the toolchain resolution on other versions. |
| 209 | + * (no development in progress) |
| 210 | +* Multiple toolchain instances with `target_settings` corresponding to each of `SCALA_VERSIONS` – fully migrated; will work in cross-build setup. |
| 211 | + * [the main Scala toolchain](/scala/BUILD) |
| 212 | + * [Scalafmt](/scala/scalafmt/BUILD) |
| 213 | + * [Scalatest](/testing/testing.bzl) |
0 commit comments