1. 建構定義

建構定義 

此頁面描述 sbt 建構定義,包括一些「理論」和 build.sbt 的語法。它假設您已安裝最新版本的 sbt,例如 sbt 1.9.8,知道如何使用 sbt,並且已閱讀「入門指南」中的前幾頁。

此頁面討論 build.sbt 建構定義。

指定 sbt 版本 

在建構定義中,您將指定建構使用的 sbt 版本。這允許具有不同 sbt 啟動器版本的人員以一致的結果建構相同的專案。為此,請建立一個名為 project/build.properties 的檔案,其中指定 sbt 版本如下

sbt.version=1.9.8

如果本機沒有可用的必要版本,sbt 啟動器會為您下載。如果沒有此檔案,sbt 啟動器將選擇任意版本,這是不建議的,因為它會使您的建構無法移植。

什麼是建構定義? 

建構定義build.sbt 中定義,它由一組專案(類型為 Project)組成。由於術語 專案 可能含糊不清,我們在本指南中經常將其稱為 子專案

例如,在 build.sbt 中,您可以像這樣定義位於目前目錄中的子專案

lazy val root = (project in file("."))
  .settings(
    name := "Hello",
    scalaVersion := "2.12.7"
  )

每個子專案都由鍵值對組態。

例如,一個鍵是 name,它對應到一個字串值,即您的子專案的名稱。鍵值對列在 .settings(...) 方法下,如下所示

lazy val root = (project in file("."))
  .settings(
    name := "Hello",
    scalaVersion := "2.12.7"
  )

build.sbt 如何定義設定 

build.sbt 定義子專案,其中包含一系列稱為設定運算式的鍵值對,使用 build.sbt 特定領域語言(DSL)

ThisBuild / organization := "com.example"
ThisBuild / scalaVersion := "2.12.18"
ThisBuild / version      := "0.1.0-SNAPSHOT"

lazy val root = (project in file("."))
  .settings(
    name := "hello"
  )

讓我們仔細看看 build.sbt DSL:設定運算式

每個項目都稱為設定運算式。其中一些項目也稱為任務運算式。我們將在本頁稍後詳細介紹其差異。

設定運算式包含三個部分

  1. 左側是
  2. 運算子,在本例中為 :=
  3. 右側稱為主體設定主體

在左側,nameversionscalaVersion。鍵是 SettingKey[T]TaskKey[T]InputKey[T] 的實例,其中 T 是預期的值類型。鍵的種類說明如下。

由於鍵 name 的類型為 SettingKey[String],因此 name 上的 := 運算子也特定類型為 String。如果您使用錯誤的值類型,則建構定義將無法編譯

lazy val root = (project in file("."))
  .settings(
    name := 42  // will not compile
  )

build.sbt 也可能散佈著 vallazy valdef。在 build.sbt 中不允許使用最上層的 objectclass。它們應作為 Scala 原始檔放在 project/ 目錄中。

 

類型 

鍵有三種形式

  • SettingKey[T]:僅評估一次的值的鍵(值在載入子專案時計算,並保留)。
  • TaskKey[T]:值的鍵,稱為任務,每次引用時都會評估(類似於 scala 函數),可能會產生副作用。
  • InputKey[T]:以命令列引數作為輸入的任務的鍵。查看 輸入任務 以了解更多詳細資訊。

內建鍵 

內建鍵只是名為 Keys 的物件中的欄位。 build.sbt 隱式具有 import sbt.Keys._,因此 sbt.Keys.name 可以稱為 name

自訂鍵 

可以使用各自的建立方法來定義自訂鍵:settingKeytaskKeyinputKey。每個方法都期望與鍵相關聯的值的類型以及描述。鍵的名稱取自指派給該鍵的 val。例如,若要為名為 hello 的新任務定義鍵,

lazy val hello = taskKey[Unit]("An example task")

在此,我們使用了 .sbt 檔案除了設定外還可以包含 valdef 的事實。所有這些定義都會在設定之前進行評估,無論它們在檔案中的定義位置。

注意:通常,使用 lazy val 而不是 val 來避免初始化順序問題。

任務與設定鍵 

TaskKey[T] 據說定義了任務。任務是諸如 compilepackage 之類的操作。它們可能會傳回 UnitUnit 是 Scala 的 void),或者它們可能會傳回與任務相關的值,例如 packageTaskKey[File],並且其值是它建立的 jar 檔案。

每次您啟動任務執行時,例如在互動式 sbt 提示字元中鍵入 compile,sbt 都會重新執行任何涉及的任務一次。

sbt 描述子專案的鍵值對可以為諸如名稱之類的設定保留固定的字串值,但它必須為諸如 compile 之類的任務保留一些可執行的程式碼 — 即使該可執行程式碼最終傳回字串,也必須每次都重新執行。

給定的鍵始終是指向任務或純粹設定。也就是說,「任務性」(是否每次重新執行)是鍵的屬性,而不是值。

列出所有可用的設定鍵和任務鍵 

目前在您的建構定義中存在的設定鍵清單可以透過在 sbt 提示字元中鍵入 settingssettings -v 來取得。

同樣,目前定義的任務鍵清單可以透過鍵入 taskstasks -v 來取得。您也可以查看命令列參考,以討論 sbt 提示字元中常用的內建任務。
如果符合以下條件,則鍵將列印在結果清單中

  • 它是內建的 sbt(例如上述範例中的 namescalaVersion
  • 您將其建立為自訂鍵
  • 您匯入了將其帶入建構定義的外掛程式。

您也可以在 sbt 提示字元中鍵入 help <key> 以取得更多資訊。

定義任務和設定 

使用 :=,您可以將值指派給設定,將計算指派給任務。對於設定,值會在專案載入時計算一次。對於任務,每次執行任務時都會重新執行計算。

例如,若要實作上一節中的 hello 任務

lazy val hello = taskKey[Unit]("An example task")

lazy val root = (project in file("."))
  .settings(
    hello := { println("Hello!") }
  )

當我們定義專案名稱時,已經看過定義設定的範例,

lazy val root = (project in file("."))
  .settings(
    name := "hello"
  )

任務和設定的類型 

從型別系統的角度來看,從任務鍵建立的 Setting 與從設定鍵建立的 Setting 略有不同。taskKey := 42 會產生 Setting[Task[T]],而 settingKey := 42 會產生 Setting[T]。在大多數情況下,這沒有區別;當任務執行時,任務鍵仍然會產生型別為 T 的值。

TTask[T] 型別的差異有以下含義:設定不能依賴於任務,因為設定僅在專案載入時評估一次,且不會重新執行。關於這點,詳見任務圖

sbt shell 中的鍵 

在 sbt shell 中,您可以輸入任何任務的名稱來執行該任務。這就是為什麼輸入 compile 會執行 compile 任務的原因。compile 是一個任務鍵。

如果您輸入的是設定鍵的名稱而不是任務鍵的名稱,則會顯示設定鍵的值。輸入任務鍵的名稱會執行任務,但不會顯示結果值;若要查看任務的結果,請使用 show <任務名稱>,而不是單純的 <任務名稱>。鍵的命名慣例是使用 camelCase,以便命令列名稱和 Scala 識別符號相同。

若要了解有關任何鍵的更多資訊,請在 sbt 互動式提示字元中輸入 inspect <鍵名稱>inspect 顯示的一些資訊可能還不明白,但在頂部它會顯示設定的值類型以及設定的簡短描述。

build.sbt 中的 import 

您可以將 import 語句放在 build.sbt 的頂部;它們不必以空行分隔。

有一些隱含的預設 import,如下所示

import sbt._
import Keys._

(此外,如果您有自動外掛程式,則會匯入標記在 autoImport 下的名稱。)

裸 .sbt 建置定義 

設定可以直接寫入 build.sbt 檔案,而不是將它們放在 .settings(...) 呼叫中。我們稱此為「裸樣式」。

ThisBuild / version := "1.0"
ThisBuild / scalaVersion := "2.12.18"

建議將此語法用於 ThisBuild 範圍的設定和新增外掛程式。請參閱後面的章節,了解有關範圍和外掛程式的資訊。

新增程式庫相依性 

若要依賴第三方程式庫,有兩種選擇。第一種是將 jar 檔案放入 lib/ (非託管相依性),另一種是新增託管相依性,這在 build.sbt 中會如下所示

val derby = "org.apache.derby" % "derby" % "10.4.1.3"

ThisBuild / organization := "com.example"
ThisBuild / scalaVersion := "2.12.18"
ThisBuild / version      := "0.1.0-SNAPSHOT"

lazy val root = (project in file("."))
  .settings(
    name := "Hello",
    libraryDependencies += derby
  )

這是在 Apache Derby 程式庫 10.4.1.3 版上新增託管相依性的方式。

libraryDependencies 鍵涉及兩個複雜性:+= 而不是 :=,以及 % 方法。+= 會附加到鍵的舊值,而不是替換它,這在任務圖中有說明。% 方法用於從字串建構 Ivy 模組 ID,這在程式庫相依性中有說明。

我們將在「入門指南」稍後的內容中略過程式庫相依性的詳細資訊。稍後會有一整頁面涵蓋它。