本教程是为 sbt 编写的。然而,只要它支持 Scala 3,该方法对任何其他构建工具都非常相似。
在跳到 Scala 3 之前,请确保您使用的是最新的 Scala 2.13.x 和 sbt 1.5.x 版本。
现在,让我们了解将整个项目移植到 Scala 3 所需的步骤。
1. 检查项目先决条件
确保您的项目已准备好移植
- 它不得依赖尚未移植到 Scala 3 的宏库。
- 它不得使用 Scala 3 中没有等效项的编译器插件。
- 它不得依赖
scala-reflect
。
这些先决条件在前一页中有更详细的描述。
2. 选择一个模块
由于 Scala 2.13 和 Scala 3 之间的互操作性,您可以从任何模块开始。然而,从依赖项最少的模块开始可能更简单。
如果您在内部使用宏定义或宏注释,则必须首先移植它们。
3. 设置交叉构建
代码库迁移的两个主要挑战是
- 使代码编译
- 确保运行时行为保持不变
我们推荐交叉构建策略,即使用 Scala 3 和 Scala 2.13 编译代码。背后的逻辑是能够在每次修复后使用 Scala 2.13 运行测试,从而确保运行时行为保持不变。这对于避免在修复不兼容性时可能发生的错误至关重要。
在 sbt 中配置交叉构建就像
scalaVersion := "3.3.1"
crossScalaVersions ++= Seq("2.13.11", "3.3.1")
此配置表示
- 默认版本是
3.3.1
。 - 可以通过运行
++2.13.11
命令加载 2.13.11。 - 可以通过运行
++3.3.1
命令加载 3.3.1。
请注意,reload
命令将始终加载默认版本,此处为 3.3.1。
4. 准备依赖项
在此阶段,如果您运行 compile
,sbt 可能会抱怨找不到某些依赖项。这是因为依赖项的声明版本未针对 Scala 3 发布。
您需要将依赖项升级到较新版本,或告诉 sbt 使用该库的 Scala 2.13 版本。
当您更改库依赖项时,请确保在项目的各个模块中应用相同的更改。
检查是否有该库的可用 Scala 3 版本。为此,您可以在 Scaladex 中使用版本矩阵。转到库的项目页面,单击版本矩阵按钮,按 Scala 3 和 Scala 2.13 筛选。
1. 有该库的 Scala 3 版本
我们强烈建议使用一个可用版本。确保您选择的版本不会带来任何重大更改。
2. 没有该库的 Scala 3 版本
您可以使用该库的 Scala 2.13 版本。语法为
("com.lihaoyi" %% "os-lib" % "0.7.7").cross(CrossVersion.for3Use2_13)
或对于 Scala.js 依赖项
("com.lihaoyi" %%% "os-lib" % "0.7.7").cross(CrossVersion.for3Use2_13)
修复所有未解决的依赖项后,您可以检查测试是否仍在 Scala 2.13 中通过
5. 配置 Scala 3 编译器
Scala 3 编译器选项不同于 Scala 2.13 选项:一些选项已重命名,另一些选项尚未得到支持。您可以参考 编译器选项查找 页面,以调整 scalacOptions
列表以适应 Scala 3。
您应该提出一个通用选项列表、一个特定于 Scala 2.13 的选项列表和一个特定于 Scala 3 的选项列表。
典型的配置如下所示
scalacOptions ++= {
Seq(
"-encoding",
"UTF-8",
"-feature",
"-language:implicitConversions",
// disabled during the migration
// "-Xfatal-warnings"
) ++
(CrossVersion.partialVersion(scalaVersion.value) match {
case Some((3, _)) => Seq(
"-unchecked",
"-source:3.0-migration"
)
case _ => Seq(
"-deprecation",
"-Xfatal-warnings",
"-Wunused:imports,privates,locals",
"-Wvalue-discard"
)
})
}
添加 -source:3.0-migration
选项以启用 Scala 3 迁移模式。
您还应该禁用 -Xfatal-warnings
以充分利用迁移模式和自动重写。
6. 解决不兼容性
现在是尝试在 Scala 3 中编译的时候了
example / compile
编译示例项目的main
源。它严格等同于example / Compile / compile
。
example / Test / compile
编译test
源。
编译器生成两个不同级别的诊断
- 迁移警告:这些警告可以通过编译器使用
-rewrite
选项自动修补。 - 错误:一段代码无法再编译。
您可以忽略迁移警告,因为编译器会自动修复它们。但是必须手动处理不兼容错误。
许多已知的不兼容性列在 不兼容性表 中。您可以在其中找到错误的描述和一些建议的解决方案。
如果可能,您应该尝试找到最能保留代码二进制兼容性的修复方法。如果您的项目是已发布的库,这一点尤其重要。
宏不兼容性无法轻松解决。必须从头开始重写大量代码。请参阅 元编程。
修复不兼容性后,您可以通过在 Scala 2.13 中运行测试来验证解决方案。
考虑定期提交您的更改。
修复所有错误后,您应该能够在 Scala 3 中成功编译。只有迁移警告仍然存在。您可以通过使用 -source:3.0-migration -rewrite
选项进行编译来自动修补它们。
你现在应该移除 -source:3.0-migration
选项,并且可以再次添加 -Xfatal-warnings
选项。不要忘记重新加载。
7. 验证迁移
在极少数情况下,不同的隐式值可能会被解析并改变程序的运行时行为。良好的测试是防止此类错误不被注意到的唯一保证。
确保测试在 Scala 2.13 和 Scala 3 中都通过。
如果你有持续集成管道,现在是为 Scala 3 设置它的时候了。
8. 完成迁移
恭喜!你已成功将模块移植到 Scala 3。可以对每个模块重复相同的过程,直到项目完全迁移到 Scala 3。
你可以保留或删除 Scala 2.13 交叉构建配置,具体取决于是否要交叉发布你的程序。
sbt 项目迁移的演练到此结束。