交叉构建设置

本页介绍交叉构建设置。有关一般说明,请参阅 交叉构建

使用交叉构建的库

要使用针对多个 Scala 版本构建的库,请将 ModuleID 中的第一个 % 改为 %%。这告诉 sbt 应将用于构建库的当前 Scala 版本追加到依赖名称。例如:

libraryDependencies += "org.typelevel" %% "cats-effect" % "3.5.4"

对于固定 Scala 版本,几乎等效的手动替代方式为:

libraryDependencies += "org.typelevel" % "cats-effect_3" % "3.5.4"

Scala 3 专用交叉版本

若您在 Scala 3 中开发应用,可使用 Scala 2.13 库:

("a" % "b" % "1.0").cross(CrossVersion.for3Use2_13)

这与使用 %% 等效,但当 scalaVersion 为 3.x.y 时解析库的 _2.13 变体。

反之,当 scalaVersion 为 2.13.x 时,可使用 CrossVersion.for2_13Use3 使用库的 _3 变体:

("a" % "b" % "1.0").cross(CrossVersion.for2_13Use3)

Warning

库作者注意: 发布依赖 Scala 2.13 库的 Scala 3 库(或反之)通常不安全。 这可能使最终用户的 classpath 上出现同一库的两个版本,如 scala-xml_2.13scala-xml_3

关于使用交叉构建库的更多说明

您可通过在 ModuleID 上使用 cross 方法,对不同 Scala 版本的行为进行细粒度控制。以下等价:

"a" % "b" % "1.0"
("a" % "b" % "1.0").cross(CrossVersion.disabled)

以下等价:

"a" %% "b" % "1.0"
("a" % "b" % "1.0").cross(CrossVersion.binary)

这会覆盖默认设置,始终使用完整 Scala 版本而非二进制 Scala 版本:

("a" % "b" % "1.0").cross(CrossVersion.full)

CrossVersion.patch 介于 CrossVersion.binaryCrossVersion.full 之间,会移除用于区分变体但二进制兼容的 Scala 工具链构建的任何尾随 -bin-... 后缀。

("a" % "b" % "1.0").cross(CrossVersion.patch)

CrossVersion.constant 固定常量值:

("a" % "b" % "1.0").cross(CrossVersion.constant("2.9.1"))

等价于:

"a" % "b_2.9.1" % "1.0"

项目矩阵

sbt 2.x 引入了项目矩阵,通过以子项目表示交叉构建,使交叉构建可以并行进行。

build.sbt

lazy val scala3 = "3.8.4"
lazy val scala2_13 = "2.13.18"

organization := "com.example"
scalaVersion := scala3
version      := "0.1.0-SNAPSHOT"

lazy val core = (projectMatrix in file("core"))
  .settings(
    name := "core",
  )
  .jvmPlatform(scalaVersions = Seq(scala3, scala2_13))
  .nativePlatform(scalaVersions = Seq(scala3, scala2_13))
  // .jsPlatform(scalaVersions = Seq(scala3))

// optional
lazy val core3 = core.jvm(scala3)
lazy val coreNative3 = core.native(scala3)

project/plugins.sbt

addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.11")

生成的子项目

在加载时,项目矩阵会通过组合平台与 Scala 版本展开为子项目:

sbt:cross-root> projects
[info]     core
[info]     core2_13
[info]     coreNative
[info]     coreNative2_13
[info]   * cross-root

按惯例,矩阵的 JVM Scala 3 变体将使用不带任何后缀的名称,例如 core

sbt:cross-root> core/run
[info] running (fork) example.main
Hello
[success] ok

要在 build.sbt 内引用子项目,可调用 jvm(...)js(...)native(...)

// For Scala
lazy val core3 = core.jvm(scalaVersion = scala3)

// For Java
lazy val intf0 = intf.jvm(autoScalaLibrary = false)

虚拟轴

矩阵中的每个组合称为 ProjectRowProjectRow 表示为 VirtualAxis 的序列。

final class ProjectRow(
    val autoScalaLibrary: Boolean,
    val axisValues: Seq[VirtualAxis],
    val process: Project => Project
)

object VirtualAxis:

  /**
   * WeakAxis allows a row to depend on another row with Zero value.
   * For example, Scala version can be Zero for Java project, and it's ok.
   */
  abstract class WeakAxis extends VirtualAxis

  /** StrongAxis requires a row to depend on another row with the same selected value. */
  abstract class StrongAxis extends VirtualAxis
end VirtualAxis

VirtualAxis 分为 WeakAxisStrongAxis。Scala 版本是弱轴的一例:带 Scala 版本的行可依赖不带 Scala 版本的 Java 行。同时,平台是强轴:一行只能依赖平台完全匹配的另一行。

例如,可按以下方式定义 SparkAxis

project/Axis.scala

import sbt.*

case class SparkAxis(idSuffix: String, directorySuffix: String)
  extends VirtualAxis.WeakAxis

有关如何使用 SparkAxis 的详情,请参阅 在虚拟轴上交叉构建 食谱。

发布约定

我们使用 Scala ABI(应用二进制接口)版本作为后缀,表示用于编译库的 Scala 版本。例如,构件名 cats-effect_2.13 表示使用了 Scala 2.13.x,cats-effect_3 表示使用了 Scala 3.x。这种简单方式可与 Maven、Ant 及其他构建工具的用户互操作。对于 Scala 的预发布版本(如 2.13.0-RC1),完整版本将视为 ABI 版本。

crossVersion 设置可用于覆盖发布约定:

  • CrossVersion.disabled(无后缀)
  • CrossVersion.binary_<scala-abi-version>
  • CrossVersion.full_<scala-version>

默认根据 crossPaths 的值为 CrossVersion.binaryCrossVersion.disabled。由于(与 Scala 库不同)Scala 编译器在补丁版本间不向前兼容,编译器插件应使用 CrossVersion.full