天天看點

sbt筆記五 作用域

關于keys的完整故事

之前我們假設,一個key(就像name)相當于sbt的鍵值對映射的一個條目。其實是簡化的。 事實上,每一個key在多個上下文中分别有一個關聯的值,稱作“scope”(作用域)。 這是具體的例子:

  • 如果建構定義中包含多個項目,key在每個項目中可以有不同的值。
  • 針對main源碼和測試源碼,如果想用不同的方式編譯他們,compile鍵可以有不同的值。
  • 打包class檔案(package-bin)或打包源碼(package-src),package-options鍵(包含建立jar包的選項)可以有不同的值。

對于給定的鍵名不會有唯一的值,因為值會根據作用域而改變。 然而,對于給定的scoped鍵,值是唯一的。 如果考慮sbt處理setting清單來生成鍵值映射來描述這個項目,如先前所述,這些在鍵值映射中的鍵就是scoped鍵。在建構定義(如在build.sbt中)中定義的setting也适用于scoped鍵。 通常作用域是隐含的或有一個預設值,但如果預設值有誤,你需要在build.sbt中說明希望的作用域。

Scope axes

scope axis是一種類型,這種類型的每一個執行個體都可以定義自身的作用域(就是說,對于keys,每個執行個體都擁有自己的唯一值) 有三個scope axes:

  • Projects
  • Configurations
  • Tasks

project axis範圍界定

如果把多個項目放到一個build中,每個項目要有自己的settings。也就是說,keys可以根據項目劃分作用域。 project axis也可以設為"entire build",這樣setting應用于entire build而非單一項目。當一個項目沒有定義特定項目setting,建構級别settings通常用作後備。

configuration axis範圍界定

一個configuration定義了一個建構的味道,可能用它自身的classpath,sources,生成的包,或其他。configuration概念來自于ivy,sbt用來管理依賴(從MavenScopes)。 sbt中可以看到的一些configuration:

  • Compile 定義了main建構(src/main/scala)
  • Test 定義了如何建構測試(src/test/scala)
  • Runtime 定義了run任務的classpath

預設情況下,所有與編譯,打包,和運作相關聯的key,都是局限于configuration的,是以在每個configuration中可以做不同的工作。最明顯的例子是任務鍵compile,package和run;但所有對這些key産生影響的其他key(比如source-directories,scalac-options或full-classpath)也局限于configuration。

task axis範圍界定

setting可以影響一個任務的執行。例如package-src任務受package-options設定的影響。 為了支援這個,一個任務key(比如package-src)可以作為另一個key(比如package-options)的作用域。 各種各樣的打包任務(package-src, package-bin, package-doc)可以共享與打包相關的key,比如artifact-name和package-options。這些key對于每個打包任務可以有不同的值。

全局範圍

每個scope axis可以被axis類型的執行個體填充(例如task axis可以被任務填充),或被特殊值Global填充。 Global意味着你所期望的:setting的值應用于所有axis的執行個體。例如如果task axis是Global,那麼這個setting将應用于所有任務。

委托

如果在它的作用域内沒有與之關聯的值,那麼一個限定了作用域的key可能是未定義的。 對于每個作用域,sbt有一個由其他作用域構成的回調搜尋路徑。典型的,如果一個key在更具體的作用域内沒有關聯的值,sbt将嘗試從更一般的作用域内獲得值,比如Global作用域,或完整建構作用域。 這個特性允許你為更一般的作用域設定一個值,允許許多更具體的作用域繼承這個值。 你可以用inspect指令來檢視一個key的回調搜尋路徑(fallback search path)或“delegates”,如下所述。

Referring to scoped keys when running sbt

在指令行和互動模式下,sbt這樣展示(并解析)範圍限定key:

{<build-uri>}<project-id>/config:intask::key
           
  • {<build-uri>}<project-id> 辨別project axis。如果project axis有“完整建構(entire build)”作用域,<project-id>部分将丢失。
  • config 辨別configuration axis。
  • intask 辨別task axis。
  • key 辨別key存在作用域(identifies the key being scoped)。

* 可以為每一個axis顯示,參考Global作用域。 如果省略部分範圍限定key,它将按如下方式推斷:

  • 如果省略項目,将使用目前項目
  • 如果省略configuration或task,一個依賴鍵configuration會被自動發現。

更多細節,請檢視 Interacting with the Configuration System。

Examples of scoped key notation

  • full-classpath: just a key, so the default scopes are used: current project, a key-dependent configuration, and global task scope.
  • test:full-classpath: specifies the configuration, so this is full-classpath in the test configuration, with defaults for the other two scope axes.
  • *:full-classpath: specifies Global for the configuration, rather than the default configuration.
  • doc::full-classpath: specifies the full-classpath key scoped to the doc task, with the defaults for the project and configuration axes.
  • {file:/home/hp/checkout/hello/}default-aea33a/test:full-classpath specifies a project, {file:/home/hp/checkout/hello/}default-aea33a, where the project is identified with the build {file:/home/hp/checkout/hello/} and then a project id inside that build default-aea33a. Also specifies configuration test, but leaves the default task axis.
  • {file:/home/hp/checkout/hello/}/test:full-classpath sets the project axis to "entire build" where the build is {file:/home/hp/checkout/hello/}
  • {.}/test:full-classpath sets the project axis to "entire build" where the build is {.}. {.} can be written ThisBuild in Scala code.
  • {file:/home/hp/checkout/hello/}/compile:doc::full-classpath sets all three scope axes.

Inspecting scopes

In sbt's interactive mode, you can use the inspect command to understand keys and their scopes. Try inspect test:full-classpath:

$ sbt
> inspect test:full-classpath
[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:full-classpath
[info] Dependencies:
[info]  test:exported-products
[info]  test:dependency-classpath
[info] Reverse dependencies:
[info]  test:run-main
[info]  test:run
[info]  test:test-loader
[info]  test:console
[info] Delegates:
[info]  test:full-classpath
[info]  runtime:full-classpath
[info]  compile:full-classpath
[info]  *:full-classpath
[info]  {.}/test:full-classpath
[info]  {.}/runtime:full-classpath
[info]  {.}/compile:full-classpath
[info]  {.}/*:full-classpath
[info]  */test:full-classpath
[info]  */runtime:full-classpath
[info]  */compile:full-classpath
[info]  */*:full-classpath
[info] Related:
[info]  compile:full-classpath
[info]  compile:full-classpath(for doc)
[info]  test:full-classpath(for doc)
[info]  runtime:full-classpath
           

On the first line, you can see this is a task (as opposed to a setting, as explained in .sbt build definition). The value resulting from the task will have type scala.collection.Seq[sbt.Attributed[java.io.File]].

"Provided by" points you to the scoped key that defines the value, in this case {file:/home/hp/checkout/hello/}default-aea33a/test:full-classpath (which is the full-classpath key scoped to the test configuration and the {file:/home/hp/checkout/hello/}default-aea33a project).

"Dependencies" may not make sense yet; stay tuned for the next page.

You can also see the delegates; if the value were not defined, sbt would search through:

  • two other configurations (runtime:full-classpath, compile:full-classpath). In these scoped keys, the project is unspecified meaning "current project" and the task is unspecified meaning Global
  • configuration set to Global (*:full-classpath), since project is still unspecified it's "current project" and task is still unspecified so Global
  • project set to {.} or ThisBuild (meaning the entire build, no specific project)
  • project axis set to Global (*/test:full-classpath) (remember, an unspecified project means current, so searching Global here is new; i.e. * and "no project shown" are different for the project axis; i.e. */test:full-classpath is not the same as test:full-classpath)
  • both project and configuration set to Global (**:full-classpath uses Global for all three axes)

Try inspect full-classpath (as opposed to the above example, inspect test:full-classpath) to get a sense of the difference. Because the configuration is omitted, it is autodetected as compile. inspect compile:full-classpath should therefore look the same as inspect full-classpath.

Try inspect *:full-classpath for another contrast. full-classpath is not defined in the Global configuration by default.

Again, for more details, see Interacting with the Configuration System.

Referring to scopes in a build definition

如果你在build.sbt中用一個空鍵(a bare key)建立一個setting,他将适用于目前項目,Global配置(configuration Global)和Global任務(task Global):

name := "hello"
           

運作sbt和inspect name,檢視它所提供的  {file:/home/hp/checkout/hello/}default-aea33a/*:name,也就是說,這個項目是{file:/home/hp/checkout/hello/}default-aea33a,配置是*(全局的意思),并且任務沒有顯示(也是全局的意思)。 build.sbt常常為單一項目定義setting,是以“目前項目”就是特定build.sbt中定義的那個項目。(對于多項目建構,每個項目有自己的build.sbt。) key有一個重載的方法in用于設定作用域。in的參數可以是任何一個scope axes的執行個體。例如,雖然你沒有真正理由這麼做,你可以設定Compile配置範圍限定的name:

name in Compile := "hello"
           

或者你可以設定package-bin任務範圍限定的name(沒有意義,隻是一個例子)

name in packageBin := "hello"
           

或者你可以使用多個scope axes設定name,例如在packageBin任務和Compile配置中。

name in (Compile, packageBin) := "hello"
           

或者你可以用Global(對所有axes):

name in Global := "hello"
           

(name in Global implicitly converts the scope axis Global to a scope with all axes set to Global; the task and configuration are already Global by default, so here the effect is to make the project Global, that is, define */*:name rather than {file:/home/hp/checkout/hello/}default-aea33a/*:name)

如果你不用Scala,一個提醒:了解in和:=僅僅是個方法是很重要的,這不是魔法。Scala允許你用更好的方式來寫它們,但是你也可以用Java風格:

name.in(Compile).:=("hello")
           

沒有理由用這種醜陋的文法,但它說明了它們實際上就是方法。

什麼時候需要指定作用域

如果正在讨論的key通常被範圍限定,那麼你需要指定一個範圍。例如,compile任務預設情況下限定于Compile和Test配置,并且不存在于這些作用域之外。 要改變compile鍵關聯的值,你需要寫compile in Compile或compile in Test。用普通的compile将定義一個新的compile任務,并範圍限定于目前項目,而不是覆寫标準compile任務(範圍限定于一個configuration的)。 如果你得到一個像“ Reference to undefined setting”這樣的錯誤,通常是你指定作用域失敗,或指定一個錯誤的作用域。你正在使用的key可能定義在其他作用域,sbt将嘗試給出建議,作為錯誤消息的一部分(sbt will try to suggest what you meant as part of the error message);尋找“Did you mean compile:compile?”

一種記住它的方式是,name僅僅是一個鍵的一部分。實際上,所有的key既包含name,也有作用域(作用域有3個axes)。換句話說,完整的表達式packageOptions in (Compile, packageBin)是一個鍵名。簡單的packageOptions也是一個鍵名,但卻不是同一個(對于沒有in的key,作用域隐式假定為:目前項目,全局配置,全局任務)。

2013-1-28

這篇怎麼全是字啊,沒幾句代碼,沒咋看懂都,湊合這樣吧。等啥時候我把這塊了解了,再回頭好好看看這些翻譯。中間一大段懶得翻譯,因為不知道它在說啥...

版權聲明:本文為CSDN部落客「weixin_34274029」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。

原文連結:https://blog.csdn.net/weixin_34274029/article/details/92007308