sbt:构建工具
官方中文文档,看到:https://www.scala-sbt.org/1.x/docs/zh-cn/Appending-Values.html
资料 | 网址 |
---|---|
官方中文文档 | https://www.scala-sbt.org/1.x/docs/zh-cn/Getting-Started.html |
sbt-assembly plugin | https://www.scala-sbt.org/sbt-native-packager/recipes/custom.html#sbt-assembly |
===
sbt 常用命令 | 说明 |
---|---|
help <命令> | 显示指定的命令的详细帮助信息。如果没有指定命令,会显示所有命令的简介。 |
sbt | 执行 sbt 不跟任何命令行参数将会进入交互模式 |
package | 将 src/main/resources 下的文件和 src/main/scala 以及 src/main/java 中编译出来的 class 文件打包成一个 jar 文件。 |
compile | |
sbt run | 运行项目 |
exit | |
sbt console | 进入 Scala REPL |
reload | 重新加载构建定义(build.sbt, project/.scala, project/.sbt 这些文件中定义的内容)。在修改了构建定义文件之后需要重新加载。 |
如何在 build.sbt 中定义设置:https://www.scala-sbt.org/1.x/docs/zh-cn/Basic-Def.html
这些表达式可以用 val,lazy val,def 声明。 build.sbt 不允许使用顶层的 object 和 class。它们必须写到 project/ 目录下作为完整的 Scala 源文件。
键(Keys)有一个返回 Setting[T] 的 := 方法。你可以像使用 Java 的语法一样调用该方法:
lazy val root = (project in file("."))
.settings(
name.:=("hello")
)
但是,Scala 允许 name := "hello" 这样调用(在 Scala 中,一个只有单个参数的方法可以使用任何一种语法调用)。
内置的 keys 实际上是对象 Keys 的字段。build.sbt 会隐式包含 import sbt.Keys._,所以可以通过 name 取到 sbt.Keys.name。
vals 和 defs 必须以空行和设置(settings)分隔。
注意: 通常,使用 lazy val 而不是 val 可以避免初始化顺序的问题。
Scala 中使用 val 语句可以定义函数,def 语句定义方法。( https://www.runoob.com/scala/scala-functions.html )
sbt 描述项目的 map 会将设置(setting)保存为固定的字符串,比如像 name;但是它不得不保存 task 的可执行代码,比如 compile — 即使这段可执行的代码最终返回一个字符串,它也需要每次都重新执行。
Tasks 和 Settings 的类型:
从类型系统的角度来讲,通过 task key 创建的 Setting 和通过 setting key 创建的 Setting 有稍微不同。taskKey := 42 的类型是 Setting[Task[T]] 而 settingKey := 42 的类型是 Setting[T]。这对于绝大多数情况并无影响;task key 在执行的时候仍然创建一个类型为 T 的值(value)。
T 类型和 Task[T] 类型的不同的含义是:一个 setting 不能依赖一个 task,因为一个 setting 只会在项目加载的时候计算一次,不会重新计算。
sbt 交互模式中的 Keys:
在 sbt 的交互模式下,你可以输入任何 task 的 name 来执行该 task。这就是为什么输入 compile 就是执行 compile task。compile 就是该 task 的 key。
如果你输入的是一个 setting key 的 name 而不是一个 task key 的 name,setting key 的值(value)会显示出来。输入一个 task key 的 name 会执行该 task 但是不会显示执行结果的值(value);输入 show
了解更多关于任何 key 内容,可以在 sbt 交互模式的命令行里输入 inspect
build.sbt 中的引入:
你可以将 import 语句放在 build.sbt 的顶部;它们可以不用空行分隔。
下面是一些默认的引入:
import sbt._
import Keys._
另外,如果你有 .scala 文件,这些文件中任何 Build 对象或者 Plugin 对象里的内容都会被引入。
任务图:https://www.scala-sbt.org/1.x/docs/zh-cn/Task-Graph.html
声明对其他任务的依赖:
在 build.sbt DSL中,我们使用 .value method 来表示对另一个任务或 setting 的依赖性。 value method 是特殊的,只能在 := 的参数中调用(或 += 或 ++= )。
.value 不是正常的 Scala method 调用。 build.sbt DSL 使用宏将它们提升到任务主体之外。 在任务引擎评估 scalacOptions 的打开 ,无论它出现在主体中的哪一行, update 和 clean 任务都已完成。
Scope (https://www.scala-sbt.org/1.x/docs/zh-cn/Scopes.html#使用+scoped+key+标识的例子)
{.}/test:fullClasspath 将构建为 {.} 的 project 轴设置为全局构建。{.} 可以在 Scala 代码中写成 ThisBuild。
Keys 会调用一个重载的 in 方法设置 scope。传给 in 方法的参数可以是任何 scope 轴的实例。比如说,你可以将 name 局限在 Compile configuration 中,尽管没有真实的理由要这样做:
name in Compile := "hello"
或者你可以把 name 局限在 packageBin task 中(没有什么意义!仅仅是个例子):
name in packageBin := "hello"
或者你可以把 name 局限在多个 scope 轴中,例如在 Compile configuration 的 packageBin task 中:
name in (Compile, packageBin) := "hello"
或者你可以用 Global 表示所有的轴:
name in Global := "hello"
(name in Global 隐式的把 scope 轴的值 Global 转换为 scope 所有轴的值均为 Global;task 和 configuration 默认是 Global,因此这里的效果是将 project 设置成 Global, 也就是说,定义了 /:name 而不是 {file:/home/hp/checkout/hello/}default-aea33a/*:name)
如果你之前不熟悉 Scala,提醒一下:in 和 := 仅仅是方法,不是魔法,理解这点很重要。Scala 让你用一种更好的方式编写它们,但是你也可以用 Java 的风格:
name.in(Compile).:=("hello")
毫无理由使用这种丑陋的语法,但是它阐明这实际上是方法。
如果一个 key 通常的作用域有问题,你需要指定 scope。例如,compile task 默认是在 Compile 和 Test configuration 的 scope 中,而且在这些 scope 之外它并不存在。
为了改变 key compile 的值,你需要写成 compile in Compile 或者 compile in Test。用普通的 compile 会在当前 project 的 scope 中定义一个新的 task,而不是覆盖 configuration 的 scope 标准的 compile task。
如果你遇到像 “引用未定义的设置” 这样的错误,通常是你指定 scope 失败了,或者你指定了一个错误的 scope。你使用的 key 可能定义在其他的 scope 中。sbt 会尝试在错误消息里面提示你的想法是什么;如 “你是指 compile:compile?”
一种方式是你可以这样认为,name 只是 key 的 一部分。实际上,所有的 key 都有 name 和 scope 组成(scope 有三个轴)。换句话说,packageOptions in (Compile, packageBin) 是表示 key name 的完整的表达式。 其简写 packageOptions 也是一个 key name,但是是不同的(对于没有 in 方法的 key,会隐式的假设一个 scope:当前的 project,global config,global task)。