sbt-scala3-migrate
是 sbt 外掛程式,可在將 sbt 專案遷移至 Scala 3 時提供協助。它包含四個 sbt 指令
migrateDependencies
可協助您更新libraryDependencies
清單migrateScalacOptions
可協助您更新scalacOptions
清單migrateSyntax
可修正 Scala 2.13 和 Scala 3 之間的許多語法不相容性migrateTypes
會嘗試編譯您的程式碼至 Scala 3,方法是在需要時推論類型並解析隱含式。
以下會詳細說明每個指令。
需求
- Scala 2.13,建議使用 2.13.13
- sbt 1.5 或更高版本
- 免責聲明:此工具無法遷移包含巨集的函式庫。
建議
在遷移之前,請在您的 scalac 選項中加入
-Xsource:3
以在 Scala 2 編譯器中啟用 Scala 3 遷移警告。請參閱 使用 -Xsource:3 的 Scala 2 頁面,以取得更多詳細資料。
在本教學課程中,我們將遷移 scalacenter/scala3-migration-example 中的專案。若要深入了解遷移,並自行訓練,您可以複製這個儲存庫,並遵循教學課程步驟。
1. 安裝
在您的 sbt 專案的 project/plugins.sbt
檔案中加入 sbt-scala3-migrate
。
// project/plugins.sbt
addSbtPlugin("ch.epfl.scala" % "sbt-scala3-migrate" % "0.6.1")
2. 選擇模組
如果您的專案包含多個模組,第一步是選擇要優先遷移的模組。
由於 Scala 2.13 和 Scala 3 之間具有互通性,因此您可以從任何模組開始。然而,從依賴項最少的模組開始可能會比較簡單。
sbt-scala3-migrate
一次只處理一個模組。請確定您選擇的模組不是聚合。
3. 遷移依賴項
本教學課程中的所有指令都必須在 sbt shell 中執行。
用法: migrateDependencies <project>
在本教學課程中,我們將考慮下列建置組態
//build.sbt
lazy val main = project
.in(file("."))
.settings(
scalaVersion := "2.13.11",
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-core" % "2.4.0",
"io.github.java-diff-utils" % "java-diff-utils" % "4.12",
"org.scalameta" %% "parsers" % "4.8.9",
"org.scalameta" %% "munit" % "0.7.23" % Test,
"com.softwaremill.scalamacrodebug" %% "macros" % "0.4.1" % Test
),
addCompilerPlugin(("org.typelevel" %% "kind-projector" % "0.13.2").cross(CrossVersion.full)),
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1")
)
執行 migrateDependencies main
會產生輸出
sbt:main> migrateDependencies main
[info]
[info] Starting migration of libraries and compiler plugins of project main
[info]
[info] Valid dependencies:
[info] "io.github.java-diff-utils" % "java-diff-utils" % "4.12"
[warn]
[warn] Versions to update:
[warn] "org.typelevel" %% "cats-core" % "2.6.1" (Other versions: 2.7.0, ..., 2.10.0)
[warn] "org.scalameta" %% "munit" % "0.7.25" % Test (Other versions: 0.7.26, ..., 1.0.0-M8)
[warn]
[warn] For Scala 3 use 2.13:
[warn] ("org.scalameta" %% "parsers" % "4.8.9").cross(CrossVersion.for3Use2_13)
[warn]
[warn] Integrated compiler plugins:
[warn] addCompilerPlugin(("org.typelevel" %% "kind-projector" % "0.13.2").cross(CrossVersion.full))
[warn] replaced by scalacOptions += "-Ykind-projector"
[error]
[error] Incompatible Libraries:
[error] "com.softwaremill.scalamacrodebug" %% "macros" % "0.4.1" % Test (Macro Library)
[error] addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1") (Compiler Plugin)
[info]
[success] Total time: 0 s, completed Aug 28, 2023 9:18:04 AM
讓我們仔細檢視此輸出訊息的每個部分。
有效的依賴項
有效的依賴項與 Scala 3 相容,原因可能是它們是標準 Java 函式庫,或是它們已跨版本發布到 Scala 3。
[info] Valid dependencies:
[info] "io.github.java-diff-utils" % "java-diff-utils" % "4.12"
您可以保持它們的原樣。
需要更新的版本
這些函式庫已在後續版本中跨版本發布到 Scala 3。您需要更新它們的版本。
[warn] Versions to update:
[warn] "org.typelevel" %% "cats-core" % "2.6.1" (Other versions: 2.7.0, ..., 2.10.0)
[warn] "org.scalameta" %% "munit" % "0.7.25" % Test (Other versions: 0.7.26, ..., 1.0.0-M8)
在給定的範例中,我們需要將 cats-core 的版本提升至 2.6.1,並將 munit 的版本提升至 0.7.25。
輸出訊息的
其他版本
部分指出 Scala 3 中有哪些其他版本可用。如果您願意,您可以提升至其中一個最新版本,但請小心選擇來源相容的版本。根據 語意版本化架構,修補程式或次要版本提升是安全的,但主要版本提升則不然。
對於 Scala 3,請使用 2.13
這些函式庫尚未跨版本發布到 Scala 3,但它們是跨版本相容的。您可以使用它們的 2.13 版本編譯至 Scala 3。
在函式庫中加入 .cross(CrossVersion.for3Use2_13)
,指示 sbt 使用 _2.13
字尾,而非 _3
。
[warn] For Scala 3 use 2.13:
[warn] ("org.scalameta" %% "parsers" % "4.8.9").cross(CrossVersion.for3Use2_13)
關於
CrossVersion.for3Use2_13
的免責聲明
- 它可能會導致傳遞依賴項的
_2.13
和_3
字尾發生衝突。在這種情況下,sbt 將無法解析依賴項,並會顯示明確的錯誤訊息。- 發布依賴於 Scala 2.13 函式庫的 Scala 3 函式庫通常是不安全的。否則,函式庫的使用者可能會在同一個依賴項上遇到衝突的
_2.13
和_3
字尾。
整合式編譯器外掛程式
一些編譯器外掛程式已整合到 Scala 3 編譯器本身。在 Scala 3 中,您不需要將它們解析為依賴項,但可以使用編譯器標記來啟用它們。
[warn] Integrated compiler plugins:
[warn] addCompilerPlugin(("org.typelevel" %% "kind-projector" % "0.13.2").cross(CrossVersion.full))
[warn] replaced by scalacOptions += "-Ykind-projector"
例如,您可以將 -Ykind-projector
加入 scalacOptions
清單中,來啟用 kind-projector。
在遷移過程中,重要的是要維持與 Scala 2.13 的相容性。後續的 migrateSyntax
和 migrateTypes
指令會使用 Scala 2.13 編譯自動重寫程式碼的某些部分。
您可以使用以下方式以跨相容的方式設定 kind-projector
// add kind-projector as a dependency on Scala 2
libraryDependencies ++= {
if (scalaVersion.value.startsWith("3.")) Seq.empty
else Seq(
compilerPlugin(("org.typelevel" %% "kind-projector" % "0.13.2").cross(CrossVersion.full))
)
},
// activate kind-projector in Scala 3
scalacOptions ++= {
if (scalaVersion.value.startsWith("3.")) Seq("-Ykind-projector")
else Seq.empty
}
不相容的函式庫
有些巨集函式庫或編譯器外掛程式與 Scala 3 不相容。
[error] Incompatible Libraries:
[error] "com.softwaremill.scalamacrodebug" %% "macros" % "0.4.1" % Test (Macro Library)
[error] addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1") (Compiler Plugin)
要解決這些不相容性,您可以
- 向維護人員確認他們是否計畫將它們移植到 Scala 3,並可能協助他們進行移植。
- 從您的建置中移除這些依賴項,並適當地調整程式碼。
更新後的建置
更新建置後,它應該如下所示
//build.sbt
lazy val main = project
.in(file("."))
.settings(
scalaVersion := "2.13.11",
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-core" % "2.6.1",
"io.github.java-diff-utils" % "java-diff-utils" % "4.12",
("org.scalameta" %% "parsers" % "4.8.9").cross(CrossVersion.for3Use2_13),
"org.scalameta" %% "munit" % "0.7.25" % Test
),
libraryDependencies ++= {
if (scalaVersion.value.startsWith("3.")) Seq.empty
else Seq(
compilerPlugin(("org.typelevel" %% "kind-projector" % "0.13.2").cross(CrossVersion.full))
)
},
scalacOptions ++= {
if (scalaVersion.value.startsWith("3.")) Seq("-Ykind-projector")
else Seq.empty
}
)
重新載入 sbt,確認專案編譯(為 Scala 2.13),確認測試成功執行,並提交您的變更。現在您可以遷移編譯器選項。
4. 遷移編譯器選項
用法: migrateScalacOptions <project>
Scala 3 編譯器不包含與 Scala 2 編譯器完全相同的選項組。您可以查看 編譯器選項表格,以取得所有編譯器選項的完整比較。
migrateScalacOptions
將協助您更新建置中的 scalacOptions
清單。
在本教學課程中,我們將考慮下列建置組態
lazy val main = project
.in(file("."))
.settings(
scalaVersion := "2.13.11",
scalacOptions ++= Seq(
"-encoding",
"UTF-8",
"-target:jvm-1.8",
"-Xsource:3",
"-Wunused:imports,privates,locals",
"-explaintypes"
)
)
執行 migrateScalacOptions main
輸出
sbt:main> migrateScalacOptions main
[info]
[info] Starting migration of scalacOptions in main
[info]
[info] Valid scalacOptions:
[info] -encoding UTF-8
[info] -Wunused:imports,privates,locals
[warn]
[warn] Renamed scalacOptions:
[warn] -target:jvm-1.8 -> -Xunchecked-java-output-version:8
[warn] -explaintypes -> -explain
[warn]
[warn] Removed scalacOptions:
[warn] -Xsource:3
[warn] -Yrangepos
[success] Total time: 0 s, completed Aug 29, 2023 2:00:57 PM
有些 scalac 選項仍然有效,有些必須重新命名,有些必須移除。
有些選項會出現在
migrateScalacOptions
的輸出中,但不會出現在您的build.sbt
中。它們是由 sbt 或某些 sbt 外掛程式新增的。請務必使用最新版本的 sbt 和 sbt 外掛程式。它們應該能夠自動將新增的編譯器選項調整為 Scala 版本。
再次強調,與 Scala 2.13 保持相容性非常重要,因為 migrateSyntax
和 migrateTypes
指令會使用 Scala 2.13 編譯自動套用一些修補程式。
以下是我們如何更新 scalacOptions 清單的方法
lazy val main = project
.in(file("."))
.settings(
scalaVersion := "2.13.11",
scalacOptions ++= {
if (scalaVersion.value.startsWith("3.")) scala3Options
else scala2Options
}
)
lazy val sharedScalacOptions =
Seq("-encoding", "UTF-8", "-Wunused:imports,privates,locals")
lazy val scala2Options = sharedScalacOptions ++
Seq("-target:jvm-1.8", "-Xsource:3", "-explaintypes")
lazy val scala3Options = sharedScalacOptions ++
Seq("-Xunchecked-java-output-version:8", "-explain")
重新載入 sbt,檢查專案是否編譯(為 Scala 2.13),檢查測試是否成功執行,並提交您的變更。現在您可以準備好移轉語法。
5. 移轉語法
用法: migrateSyntax <project>
此指令會執行多個 Scalafix 規則來修補一些已捨棄的語法。
已套用 Scalafix 規則的清單如下
- ProcedureSyntax
- fix.scala213.ExplicitNullaryEtaExpansion
- migrate.ParensAroundLambda
- fix.scala213.ExplicitNonNullaryApply
- fix.scala213.Any2StringAdd
- ExplicitResultTypes
如需有關 Scala 2.13 和 Scala 3 之間語法變更的詳細資訊,您可以參閱 不相容性表。
migrateSyntax 未修正 不相容性表 中列出的某些不相容性。其中大部分並不常見,而且可以輕鬆手動修正。如果您想透過 Scalafix 重寫規則提供協助,我們非常樂意將其新增到
migrateSyntax
指令中。
執行 migrateSyntax main
輸出
sbt:main> migrateSyntax main
[info] Starting migration of syntax in main
[info] Run syntactic rules in 7 Scala sources successfully
[info] Applied 3 patches in src/main/scala/example/SyntaxRewrites.scala
[info] Run syntactic rules in 8 Scala sources successfully
[info] Applied 1 patch in src/test/scala/example/SyntaxRewritesTests.scala
[info] Migration of syntax in main succeeded.
[success] Total time: 2 s, completed Aug 31, 2023 11:23:51 AM
查看已套用的變更,檢查專案是否仍然編譯,檢查測試是否成功執行,並提交變更。下一步也是最後一步是移轉類型。
6. 移轉類型
用法: migrateTypes <project>
Scala 3 編譯器使用略有不同的類型推論演算法。它有時會無法推論出與 Scala 2 編譯器相同的類型,這可能會導致編譯錯誤。此最後步驟將新增必要的類型註記,以使程式碼編譯為 Scala 3。
執行 migrateTypes main
輸出
sbt:main> migrateTypes main
[info] compiling 8 Scala sources to /home/piquerez/github/scalacenter/scala3-migration-example/target/scala-2.13/classes ...
[warn] 1 deprecation; re-run with -deprecation for details
[warn] one warning found
[info] compiling 8 Scala sources to /home/piquerez/github/scalacenter/scala3-migration-example/target/scala-2.13/test-classes ...
[warn] 2 deprecations; re-run with -deprecation for details
[warn] one warning found
[success] Total time: 7 s, completed Aug 31, 2023 11:26:25 AM
[info] Defining scalaVersion
[info] The new value will be used by Compile / bspBuildTarget, Compile / dependencyTreeCrossProjectId and 68 others.
[info] Run `last` for details.
[info] Reapplying settings...
[info] set current project to main (in build file:/home/piquerez/github/scalacenter/scala3-migration-example/)
[info]
[info] Migrating types in main / Compile
[info]
[info] Found 3 patches in 1 Scala source
[info] Starting migration of src/main/scala/example/TypeIncompat.scala
[info] 3 remaining candidates
[info] 1 remaining candidate
[info] Found 1 required patch in src/main/scala/example/TypeIncompat.scala
[info] Compiling to Scala 3 with -source:3.0-migration -rewrite
[info] compiling 1 Scala source to /home/piquerez/github/scalacenter/scala3-migration-example/target/scala-3.3.1/classes ...
[info]
[info] Migrating types in main / Test
[info]
[info] Found 4 patches in 1 Scala source
[info] Starting migration of src/test/scala/example/TypeIncompatTests.scala.scala
[info] 4 remaining candidates
[info] 3 remaining candidates
[info] 2 remaining candidates
[info] Found 1 required patch in src/test/scala/example/TypeIncompatTests.scala.scala
[info] Compiling to Scala 3 with -source:3.0-migration -rewrite
[info]
[info] You can safely upgrade main to Scala 3:
[info] scalaVersion := "3.3.1"
[success] Total time: 18 s, completed Aug 31, 2023 11:26:45 AM
[info] Defining scalaVersion
[info] The new value will be used by Compile / bspBuildTarget, Compile / dependencyTreeCrossProjectId and 68 others.
[info] Run `last` for details.
[info] Reapplying settings...
[info] set current project to main (in build file:/home/piquerez/github/scalacenter/scala3-migration-example/)
sbt:main>
migrateTypes main
找到 2 個必要的修補程式:一個在 src/test/scala/example/TypeIncompatTests.scala.scala
,另一個在 src/main/scala/example/TypeIncompat.scala
。它套用它們,然後使用 -source:3.0-migration -rewrite
編譯為 Scala 3 以完成遷移。
恭喜!您的專案現在可以編譯為 Scala 3。
接下來要做什麼?
如果您的專案只包含一個模組,您可以設定 scalaVersion := 3.3.1
。
如果您有多個模組,您可以從 3. 遷移相依關係 重新開始,使用另一個模組。
一旦您完成所有模組,您可以從 project/plugins.abt
中移除 sbt-scala3-migrate
,以及所有 Scala 2.13 相關設定。
歡迎提供意見回饋和貢獻
每一個意見回饋都能幫助我們改善 sbt-scala3-migrate
:錯字、更清晰的記錄訊息、更好的文件、錯誤報告、功能建議。請不要猶豫,在 GitHub 提出問題。