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