配置文件 .scala

sbt的递归性

build.sbt 是非常简单的,其隐藏了sbt真正工作的一些细节,sbt 是由Scala语言编写的,其自身也需要构建,那么由什么好的办法来实现呢?

project目录是在构建项目中的另一个项目,它负责整个项目的构建定义,理论上在project目录下还可以有另一个project项目(递归),其构建的是sbt项目本身用来支撑上级项目的构建。

例如,你可以在构建项目下再次创建一个项目,以下是目录层级结构:

  1. hello/ # your project's base directory
  2. Hello.scala # a source file in your project (could be in
  3. # src/main/scala too)
  4. build.sbt # build.sbt is part of the source code for the
  5. # build definition project inside project/
  6. project/ # base directory of the build definition project
  7. Build.scala # a source file in the project/ project,
  8. # that is, a source file in the build definition
  9. build.sbt # this is part of a build definition for a project
  10. # in project/project ; build definition's build
  11. # definition
  12. project/ # base directory of the build definition project
  13. # for the build definition
  14. Build.scala # source file in the project/project/ project

不用担心,大部分情况下是不需要创建这个的,但是理解这个概念对运用sbt很有帮助。

顺便说一下,任何以 .sbt.scala后缀的定义文件都会被用到,经常说的build.sbt或Build.scala命名只是为了方便而已,也就是说sbt配置支持多文件配置。

.scala 配置文件定义

.sbt文件定义将被合并到子目录project中,例如如下项目目录结构:

  1. hello/ # your project's base directory
  2. build.sbt # build.sbt is part of the source code for the
  3. # build definition project inside project/
  4. project/ # base directory of the build definition project
  5. Build.scala # a source file in the project/ project,
  6. # that is, a source file in the build definition

在build.sbt中的 Scala 配置表达式将会被编译合并到Build.scala中(或者是在project目录下的任何.scala文件中)。
在项目根目录的*.sbt 文件将会变成在根目录下project构建项目定义的一部分。.sbt只是为了定义项目方便。

build.sbt 和 Build.scala 的关系

对于在构建项目定义中混合的 .sbt.scala定义,你需要理解它们之间的关系,以下是两个文件的例子,首先,假设一个项目 hello, 创建hello/project/Build.scala如下:

  1. import sbt._
  2. import Keys._
  3. object HelloBuild extends Build {
  4. val sampleKeyA = settingKey[String]("demo key A")
  5. val sampleKeyB = settingKey[String]("demo key B")
  6. val sampleKeyC = settingKey[String]("demo key C")
  7. val sampleKeyD = settingKey[String]("demo key D")
  8. override lazy val settings = super.settings ++
  9. Seq(
  10. sampleKeyA := "A: in Build.settings in Build.scala",
  11. resolvers := Seq()
  12. )
  13. lazy val root = Project(id = "hello",
  14. base = file("."),
  15. settings = Seq(
  16. sampleKeyB := "B: in the root project settings in Build.scala"
  17. ))
  18. }

创建hello/build.sbt如下:

  1. sampleKeyC in ThisBuild := "C: in build.sbt scoped to ThisBuild"
  2. sampleKeyD := "D: in build.sbt"

启动sbt的交互模式,输入inspect sampleKeyA将会看到:

  1. [info] Setting: java.lang.String = A: in Build.settings in Build.scala
  2. [info] Provided by:
  3. [info] {file:/home/hp/checkout/hello/}/*:sampleKeyA

然后输入inspect sampleKeyC 显示如下:

  1. [info] Setting: java.lang.String = C: in build.sbt scoped to ThisBuild
  2. [info] Provided by:
  3. [info] {file:/home/hp/checkout/hello/}/*:sampleKeyC

“Provided by” 显示这两个参数配置的作用域是相同的,在 .sbt文件中配置sampleKeyC in ThisBuild等同于在 .scala文件中的Build.settings 中配置的值,在上述两个地方配置的值得作用域都是工程级别的作用域。

现在,在查看sampleKeyB:

  1. [info] Setting: java.lang.String = B: in the root project settings in Build.scala
  2. [info] Provided by:
  3. [info] {file:/home/hp/checkout/hello/}hello/*:sampleKeyB

sampleKeyB 的作用域是项目维度的({file:/home/hp/checkout/hello/}hello)不再是工程级别的作用域。

你可能猜到inspect sampleKeyD 和sampleKeyB的一样:

  1. [info] Setting: java.lang.String = D: in build.sbt
  2. [info] Provided by:
  3. [info] {file:/home/hp/checkout/hello/}hello/*:sampleKeyD

sbt 在 .sbt 文件中追加配置Build.settings 和 Project.settings 配置的优先级比.scala文件的高,所以配置在 .sbt 文件中的sampleC 或 sampleD会修改Build.scala的配置。

另一个需要注意的是:sampleKeyC 和 sampleKeyD 可以定义在build.sbt 中定义,这是因为sbt会将Build 对象自动隐式的导入到.sbt文件中,例如这个例子sbt 会将 HelloBuild._ 隐式的导入到build.sbt 文件中。

总结:

  • .scala文件中,可以定义 Build.settings f配置项供sbt查找,其作用域自动为工程级别的作用域
  • .scala文件中,可以定义 Project.settings配置项供sbt查找,其作用域自动为项目维度的作用域
  • .scala文件中的任何Build对象,都会自动的导入到所有的.sbt文件中
  • .sbt 文件中的配置将会被追加到.scala 文件中
  • 配置在.sbt文件中的配置默认是项目维度的作用域,除非手动指定其他作用域

什么时候用到 .scala 配置文件

.scala 配置文件可以用任何的Scala代码编写,包括顶级的类和对象,由于它是标准的Scala语法,所以也没有必须添加空行来分割配置的限制

在配置参数配置推荐用.sbt配置文件,当要实现一些任务配置或者要定义一些复用配置供多个.sbt文件使用的情况推荐使用.scala配置文件

在交互模式下构建项目

你可以在交互模式下,切换到在project目录下的构建工程项目的项目中, 当切换到该项目中后可以执行一些操作,如reload plugins.

  1. > reload plugins
  2. [info] Set current project to default-a0e8e4 (in build file:/home/hp/checkout/hello/project/)
  3. > show sources
  4. [info] ArrayBuffer(/home/hp/checkout/hello/project/Build.scala)
  5. > reload return
  6. [info] Loading project definition from /home/hp/checkout/hello/project
  7. [info] Set current project to hello (in build file:/home/hp/checkout/hello/)
  8. > show sources
  9. [info] ArrayBuffer(/home/hp/checkout/hello/hw.scala)
  10. >

正如上面的例子,你可以用reload return命令来退出当前工程构建项目的项目,回到常规的项目中。

不可变配置

sbt 配置可能被错误的理解为在build.sbt的配置将会被添加到Build 或Project 对象的settings 字段中,其实settings 是 Build 和 Project的列表,build.sbt中的配置将会被和一个不可变的配置列表连接起来生成一个新的列表供sbt使用的, Build 和 Project中的“不可变配置”列表仅仅是完成sbt配置的一部分。

事实上,还有其他的配置文件,它们将会按照如下顺序添加:

  • 配置在.scala文件中的Build.settings和Project.settings配置项
  • 用户全局配置,例如在~/.sbt/0.13/global.sbt文件中可以配置影响所有工程构建的配置项
  • 通过被插件注入的配置项
  • 项目中.sbt文件配置的配置项
  • 构建工程项目的项目(例如:所有项目中的project项目)从全局插件中添加的配置项

后面的配置将会重载前面的配置,最后生成一个配置列表。