鍍金池/ 教程/ 物聯(lián)網(wǎng)/ 作用域
目錄結(jié)構(gòu)
Linux 平臺(tái)安裝 SBT
總結(jié)
安裝 SBT
簡單例子:Hello World
多項(xiàng)目構(gòu)建
作用域
配置文件 .sbt
Lib 庫依賴
運(yùn)行 SBT
Windows 平臺(tái)安裝 SBT
插件使用
Mac 平臺(tái)安裝 SBT
配置文件 .scala
配置參數(shù)的方法
手動(dòng)安裝 SBT
自定義配置和任務(wù)

作用域

深入 Keys

前面我們一直簡單的認(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)系,被稱為“作用域”

例如:

  • 如果在一個(gè)項(xiàng)目構(gòu)建定義中含有多個(gè)項(xiàng)目,那么一個(gè) key 可能有不同的值在不同的項(xiàng)目中
  • 對(duì)于項(xiàng)目代碼和項(xiàng)目測試代碼中 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, 以下是三種作用域維度:

  • 項(xiàng)目維度
  • 配置維度
  • 任務(wù)維度

作用域的項(xiàng)目維度

如果在一個(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 中的一些配置維度:

  • Compile : 定義編譯項(xiàng)目配置 (src/main/scala)
  • Test : 定義測試項(xiàng)目的配置(src/test/scala)
  • Runtime : 定義運(yùn)行一個(gè)工程時(shí)的配置

默認(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

作用域的任務(wù)維度

配置可以影響一個(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ù),例如 artifactNamepackageOptions, 但是他們的值在不同的任務(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ì)信息。

作用域在運(yùn)行sbt時(shí)相關(guān)解釋

在交互模式下或命令下, sbt 將用下面的形式表示作用域:

{<build-uri>}<project-id>/config:intask::key
  • {<build-uri>}/<project-id> 表示一個(gè)項(xiàng)目維度的作用域,當(dāng)表示整個(gè)工程的作用域時(shí) <project-id> 部分可以省略
  • config 表示作用域的配置維度
  • intask 表示作用域的任務(wù)維度
  • key 表示配置的key

*在上述任意段中出現(xiàn)表示在該作用域維度下是一個(gè)全局作用域

如果key省略指定某一部分,將按以下規(guī)則推斷作用域:

  • 如果省略項(xiàng)目維度將被認(rèn)為當(dāng)前的項(xiàng)目
  • 如果一個(gè)依賴配置維度的key在省略配置或任務(wù)維度時(shí)將自動(dòng)探測

作用域的例子詳解

  • *: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沒有定義,將按照以下路勁搜索:

  • 兩個(gè)配置作用域(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ù)全局作用域
  • 項(xiàng)目維度設(shè)置{.}或者ThisBuild (表示工程級(jí)別的作用域,沒有指定項(xiàng)目)
  • 項(xiàng)目維度設(shè)置為全局作用域(*/test:fullClasspath)(注意:當(dāng)沒有指定項(xiàng)目的時(shí)候表示當(dāng)前項(xiàng)目,這塊代表的意思是全局作用域,比如 */test:fullClasspathtest:fullClasspath代表的意義不一樣)
  • 項(xiàng)目和配置維度都為全局的作用域 (*/*: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í)行.

如何在工程構(gòu)建中定義作用域

如果單獨(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")

什么時(shí)候指定作用域

當(dāng)定義一個(gè)key在默認(rèn)作用域下會(huì)有問題時(shí)需要指定作用域,例如compile任務(wù)類型的配置,默認(rèn)作用域是在 CompileTest配置維度下,不會(huì)存在于其他作用域中。

如果修改compile這個(gè)任務(wù)配置的值需要指定其作用域,修改語句如compile in Compilecompile 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ù)和配置維度為全局)

上一篇:配置文件 .scala下一篇:運(yùn)行 SBT