前面我們一直簡單的認(rèn)為類似于 name
的 key 將一一對(duì)應(yīng)一個(gè)值,在 sbt 中其實(shí)就是一個(gè) key-value 的map 表,事實(shí)上每個(gè) key 除了關(guān)聯(lián)一個(gè)值外還有一個(gè)上下文關(guān)系,被稱為“作用域”
例如:
compile
這個(gè)key對(duì)應(yīng)的值是不同的packageOptions
(包含打包jar文件的所有參數(shù))key 在打包 class 二進(jìn)制文件和源代碼文件時(shí)的值是不同的對(duì)于 name
這個(gè)key不是只有一個(gè)值,值會(huì)隨作用域的不同而不同,但是在同一個(gè)作用域中一個(gè)key只有一個(gè)值
以前認(rèn)為 sbt 是通過處理一個(gè)配置列表生成 key-value 的一個(gè) map表來描述整個(gè)項(xiàng)目構(gòu)建的,現(xiàn)在可以把這個(gè)map認(rèn)為是一個(gè)由作用域的 map表,在項(xiàng)目構(gòu)建定義中(如build.sbt文件中定義) 每個(gè) key 都有一個(gè)對(duì)應(yīng)的作用域。
一般情況下每個(gè) key 都有一個(gè)隱含或默認(rèn)的作用域,但是如果默認(rèn)的不是想要的需要顯式覆蓋聲明
每個(gè)作用域維度是一個(gè)類型,每個(gè)類型實(shí)例都可以定義自己唯一值得key, 以下是三種作用域維度:
如果在一個(gè)構(gòu)建工程中定義多個(gè)項(xiàng)目,每個(gè)項(xiàng)目擁有自己唯一的配置,那么這時(shí)key的作用域是一個(gè)具有項(xiàng)目維度的作用域。
作用域項(xiàng)目維度可以設(shè)置為“整個(gè)工程”有效(可以稱為該作用域?yàn)楣こ套饔糜颍?,這樣一個(gè)配置將作用于整個(gè)構(gòu)建工程中而不是單一的一個(gè)項(xiàng)目,構(gòu)建級(jí)別的配置經(jīng)常用來當(dāng)做備用,當(dāng)某個(gè)項(xiàng)目中沒有配置該配置的時(shí)候。
一個(gè)配置維度定義一個(gè)構(gòu)建類型,可能有自己的 classpath
、 源代碼目錄、打包發(fā)布等,配置維度這個(gè)概念來源于 Ivy, 由于 Sbt 的包依賴管理用的是 MavenScopes
在 sbt 中的一些配置維度:
默認(rèn)情況下,在編譯、打包、運(yùn)行是所有的key將對(duì)應(yīng)關(guān)聯(lián)一個(gè)配置維度,所以在不同的配置維度下運(yùn)行結(jié)果可能不同。最常見如任務(wù)類型的key run
, compile
, package
, 其實(shí)所有的key都會(huì)受配置維度的作用域的影響,例如sourceDirectories
, scalacOptions
,fullClasspath
等
配置可以影響一個(gè)任務(wù)的執(zhí)行,例如, packageSrc
會(huì)受到配置參數(shù)packageOption
的影響。為了實(shí)現(xiàn)這個(gè)功能在 sbt 中packageSrc
可以當(dāng)做配置參數(shù) packageOption
的作用域
打包構(gòu)建有多個(gè)任務(wù)(packageSrc, packageBin, packageDoc)可以共享和打包相關(guān)的配置參數(shù),例如 artifactName
和packageOptions
, 但是他們的值在不同的任務(wù)維度下是不同。
每個(gè)作用域維度都是由一個(gè)維度類型實(shí)例構(gòu)成(例如任務(wù)維度就是由一個(gè)任務(wù)實(shí)例構(gòu)成), 一個(gè)維度也可以由一個(gè)全局值構(gòu)成
全局的概念正如你所理解的,一個(gè)參數(shù)配置值將被應(yīng)用到所有的維度實(shí)例中,例如一個(gè)任務(wù)維度是全局的,那么這個(gè)配置將在所有任務(wù)中有效。
當(dāng)一個(gè)作用域中沒有定義某個(gè) key, 那么其在該作用域是沒有關(guān)聯(lián)的值。對(duì)于每個(gè)作用域,sbt 通過搜索其他作用域的路勁作為某個(gè)key 的備選作用域,典型的例子:如果一個(gè)key在指定作用域中沒有關(guān)聯(lián)的值,sbt試圖從其他作用域獲取一個(gè)值,例如全局作用域或者工程作用域。
這個(gè)特性允許你在一個(gè)作用域中設(shè)置某個(gè)key,在其他的作用域中繼承該key, 可以使用sbt的inspect命令查看某個(gè)key的搜索作用域詳細(xì)信息。
在交互模式下或命令下, sbt 將用下面的形式表示作用域:
{<build-uri>}<project-id>/config:intask::key
{<build-uri>}/<project-id>
表示一個(gè)項(xiàng)目維度的作用域,當(dāng)表示整個(gè)工程的作用域時(shí) <project-id>
部分可以省略
*
在上述任意段中出現(xiàn)表示在該作用域維度下是一個(gè)全局作用域
如果key省略指定某一部分,將按以下規(guī)則推斷作用域:
*:fullClasspath
指定一個(gè)全局的配置,而不是默認(rèn)配置doc::fullClasspath
配置在doc任務(wù)中fullClasspath參數(shù),項(xiàng)目和配置維度默認(rèn){file:/home/hp/checkout/hello/}default-aea33a/test:fullClasspath
表示在工程{file:/home/hp/checkout/hello/}中的項(xiàng)目default-aea33a下的配置維度為test的fullClasspath配置參數(shù),任務(wù)維度為默認(rèn)的{file:/home/hp/checkout/hello/}/test:fullClasspath
表示是一個(gè)工程級(jí)別的作用域,工程為{file:/home/hp/checkout/hello/}{.}/test:fullClasspath
表示一個(gè)工程級(jí)別的作用域,這塊的 {.}.{.}
在Scala代碼中可以寫成 ThisBuild{file:/home/hp/checkout/hello/}/compile:doc::fullClasspath
表示 fullClasspath 配置參數(shù)設(shè)置所有的作用域維度在 sbt 交互模式下可以使用命令inspect
來檢測一個(gè)配置參數(shù)的作用,例如
> inspect test:fullClasspath
命令執(zhí)行返回結(jié)果如下:
[info] Task: scala.collection.Seq[sbt.Attributed[java.io.File]]
[info] Description:
[info] The exported classpath, consisting of build products and unmanaged and managed, internal and external dependencies.
[info] Provided by:
[info] {file:/home/hp/checkout/hello/}default-aea33a/test:fullClasspath
[info] Dependencies:
[info] test:exportedProducts
[info] test:dependencyClasspath
[info] Reverse dependencies:
[info] test:runMain
[info] test:run
[info] test:testLoader
[info] test:console
[info] Delegates:
[info] test:fullClasspath
[info] runtime:fullClasspath
[info] compile:fullClasspath
[info] *:fullClasspath
[info] {.}/test:fullClasspath
[info] {.}/runtime:fullClasspath
[info] {.}/compile:fullClasspath
[info] {.}/*:fullClasspath
[info] */test:fullClasspath
[info] */runtime:fullClasspath
[info] */compile:fullClasspath
[info] */*:fullClasspath
[info] Related:
[info] compile:fullClasspath
[info] compile:fullClasspath(for doc)
[info] test:fullClasspath(for doc)
[info] runtime:fullClasspath
在第一行中可以看到一個(gè)任務(wù)key, 其值得類型是 scala.collection.Seq[sbt.Attributed[java.io.File]].
"Provided by" 表示該配置參數(shù)定義的作用域: {file:/home/hp/checkout/hello/}default-aea33a/test:fullClasspath
(fullClasspath 配置在作用域任務(wù)維度為test,作用域項(xiàng)目維度為{file:/home/hp/checkout/hello/}default-aea33a的作用域中)
"Dependencies": 配置參數(shù)章節(jié)解釋
"Delegates": 表示如果某個(gè)key沒有定義,將按照以下路勁搜索:
runtime:fullClasspath
, compile:fullClasspath
),在這些作用域中的key,項(xiàng)目維度沒有指定默認(rèn)是當(dāng)前項(xiàng)目,任務(wù)維度沒有指定默認(rèn)是任務(wù)全局作用域*:fullClasspath
),項(xiàng)目維度沒有指定默認(rèn)是當(dāng)前項(xiàng)目,任務(wù)維度沒有指定默認(rèn)是任務(wù)全局作用域{.}
或者ThisBuild (表示工程級(jí)別的作用域,沒有指定項(xiàng)目)*/test:fullClasspath
)(注意:當(dāng)沒有指定項(xiàng)目的時(shí)候表示當(dāng)前項(xiàng)目,這塊代表的意思是全局作用域,比如 */test:fullClasspath
和 test:fullClasspath
代表的意義不一樣)*/*:fullClasspath
), 任務(wù)維度沒有指定,所以當(dāng)設(shè)定為*/*:fullClasspath
作用域時(shí),在作用域的三個(gè)維度上都為全局的運(yùn)行 inspect fullClasspath
(對(duì)比上一個(gè)例子inspect test:fullClasspath
) 會(huì)發(fā)現(xiàn)返回的結(jié)果有所不同,這是因?yàn)楫?dāng)不指定配置維度的作用域時(shí),sbt將inspect fullClasspath
自動(dòng)探測為 compile.inspect compile:fullClasspath
執(zhí)行.
如果單獨(dú)在build.sbt中創(chuàng)建一個(gè)key,這個(gè)key作用域的項(xiàng)目維度將為當(dāng)前項(xiàng)目,配置和任務(wù)維度將為全局作用域:
name := "hello"
運(yùn)行命令inspect name
將看到"Provided by"為:{file:/home/hp/checkout/hello/}default-aea33a/*:name
, 表示作用域項(xiàng)目維度為 {file:/home/hp/checkout/hello/}default-aea33a
, 作用域任務(wù)維度為*
(全局) , 作用域任務(wù)維度沒有指定默認(rèn)代表全局,
build.sbt 定義是針對(duì)單個(gè)項(xiàng)目的,所以“當(dāng)前項(xiàng)目”指的就是當(dāng)前build.sbt定義的項(xiàng)目(對(duì)于多項(xiàng)目構(gòu)建,每個(gè)項(xiàng)目有對(duì)應(yīng)一個(gè)build.sbt)
所有的key都有一個(gè)方法用來設(shè)定作用域,其參數(shù)可以為作用域的任何一個(gè)維度的對(duì)象實(shí)例。例如,可以通過如下方式將name
配置設(shè)置為配置維度為Compile
的作用域:
name in Compile := "hello"
或者可以將name
配置設(shè)置為任務(wù)維度為packageBin
的作用域(當(dāng)然這例子有點(diǎn)不合適)
name in packageBin := "hello"
當(dāng)然了也可以為一個(gè)key指定多個(gè)作用域維度,例如將key name
同時(shí)指定配置維度和任務(wù)維度:
name in (Compile, packageBin) := "hello"
也可以指定一個(gè)key的作用域?yàn)槿郑?/p>
name in Global := "hello"
(name in Global
隱式將作用域轉(zhuǎn)化為一個(gè)對(duì)于所有維度都為全局的作用域,默認(rèn)情況下任務(wù)和配置維度的作用域已經(jīng)是全局的了,但是這塊會(huì)影響項(xiàng)目維度的作用,因?yàn)槠潆[式轉(zhuǎn)化為*/*:name
而不是{file:/home/hp/checkout/hello/}default-aea33a/*:name
)
如果沒有用過Scala語言,理解 in
和 :=
這兩個(gè)方法很重要,推薦用scala語法形式配置,但是也可以用Java語法形式配置:
name.in(Compile).:=("hello")
當(dāng)定義一個(gè)key在默認(rèn)作用域下會(huì)有問題時(shí)需要指定作用域,例如compile
任務(wù)類型的配置,默認(rèn)作用域是在 Compile
或Test
配置維度下,不會(huì)存在于其他作用域中。
如果修改compile
這個(gè)任務(wù)配置的值需要指定其作用域,修改語句如compile in Compile
或compile in Test
, 如果單獨(dú)寫作為compile
sbt 會(huì)重新創(chuàng)建一個(gè)作用域?yàn)楫?dāng)前項(xiàng)目的任務(wù)配置,而不是去修改在配置維度作用域下的標(biāo)準(zhǔn)compile任務(wù)配置。
如果得到一個(gè)錯(cuò)誤信息“Reference to undefined setting”, 一般情況下是因?yàn)榕渲庙?xiàng)指定作用域失敗或者指定了一個(gè)錯(cuò)誤的作用域。當(dāng)定義的key可能在其他作用域中已經(jīng)定義會(huì)接收到“Did you mean compile:compile?”錯(cuò)誤提示信息
一般會(huì)簡單的認(rèn)為配置項(xiàng)就是一個(gè)key-value鍵值對(duì),其實(shí)對(duì)于所有的配置項(xiàng)都會(huì)包含一個(gè)key-value 和一個(gè)對(duì)應(yīng)的作用域(作用域有三個(gè)維度),如配置表達(dá)式:packageOptions in (Compile, packageBin)
, 當(dāng)配置為packageOptions
也是一個(gè)合法的配置項(xiàng),只是該配置項(xiàng)的作用域?qū)⑹悄J(rèn)的作用域(項(xiàng)目維度為當(dāng)前項(xiàng)目,任務(wù)和配置維度為全局)