TL;DR sbt 这个名字不代表任何东西,它就是“sbt”,并且应该这样写。
当 Mark Harrah (@harrah) 第一次创建这个项目时,他将其命名为“Simple Build Tool”,但在他的 首次公开发布中,他已将其称为“sbt”。随着时间的推移,有些人重新定义 sbt 为“Scala Build Tool”,但我们认为这也不准确,因为它可以用于构建仅包含 Java 的项目。
如今,我们只称 sbt 为“sbt”,为了强调这个名字不再是 首字母缩略词,我们总是将其全部小写。但是,我们很乐意接受 酢豚(subuta)作为昵称。
sbt 1.9.8 默认情况下会抑制大多数堆栈跟踪和调试信息。它有一个很好的副作用,就是减少了屏幕上的噪音,但对于新手来说,这可能会让你找不到解释。要查看命令上一次执行的输出,并以更高的详细程度查看,请输入 last <task>
,其中 <task>
是失败的任务,或者你想要查看详细输出的任务。例如,如果你发现你的 update
无法按预期加载所有依赖项,你可以输入
> last update
它将显示 update
命令上一次运行的完整输出。
有时 sbt 无法检测到 ANSI 代码不受支持,你就会看到类似于
[0m[ [0minfo [0m] [0mSet current project to root
这样的输出,或者 ANSI 代码受支持,但你想要禁用彩色输出。要完全禁用 ANSI 代码,请传递 -no-colors
选项
$ sbt -no-colors
在 sbt 的 shell 中运行 console
。
:=
、+=
和 ++=
方法是什么? 这些是用于构建 Setting
或 Task
的键上的方法。入门指南涵盖了所有这些方法,请参见 .sbt 构建定义、任务图 和 追加值 以获得示例。
%
方法是什么? 它用于从字符串创建 ModuleID
,用于指定管理依赖项。请阅读入门指南中的 库依赖项 部分。
ThisBuild / scalaVersion
是什么意思? ThisBuild
充当一个特殊的子项目名称,你可以使用它来定义构建的默认值。当你定义一个或多个子项目时,如果子项目没有定义 scalaVersion
键,它将查找 ThisBuild / scalaVersion
。
参见 构建范围设置。
ModuleID
、Project
、… 是什么? 要弄清楚未知类型或方法,请查看 入门指南,如果你还没有看过。还可以尝试 索引,它包含常用的方法、值和类型,以及 API 文档。
工件中包含的文件默认情况下由 mappings
任务配置,该任务由相关包任务限定范围。mappings
任务返回一个映射序列 Seq[(File,String)]
,从要包含的文件映射到 jar 中的路径。有关创建这些映射的详细信息,请参见 映射文件。
例如,要将生成的源代码添加到打包的源代码工件中
Compile / packageSrc / mappings ++= {
import Path.{flat, relativeTo}
val base = (Compile / sourceManaged).value
val srcs = (Compile / managedSources).value
srcs pair (relativeTo(base) | flat)
}
这将从 managedSources
任务中获取源代码,并将它们相对于 managedSource
基目录进行相对化,回退到扁平化映射。如果源代码生成任务没有将源代码写入 managedSource
目录,则必须调整映射函数以尝试相对于其他目录进行相对化,或采用更适合生成器的方案。
参见 生成文件。
参见 缓存。
参见 如何定义自定义依赖项配置。
run
之外,如何创建自定义运行任务? 此答案摘自 邮件列表讨论。
阅读入门指南,直到 自定义设置 部分以了解背景知识。
基本运行任务通过以下方式创建
lazy val myRunTask = taskKey[Unit]("A custom run task.")
// this can go either in a `build.sbt` or the settings member
// of a Project in a full configuration
fullRunTask(myRunTask, Test, "foo.Foo", "arg1", "arg2")
如果你想要能够在命令行上提供参数,请将 TaskKey
替换为 InputKey
,将 fullRunTask
替换为 fullRunInputTask
。Test
部分可以替换为其他配置,例如 Compile
,以使用该配置的类路径。
此运行任务可以通过在范围内指定任务键来单独配置。例如
myRunTask / fork := true
myRunTask / javaOptions += "-Xmx6144m"
工具依赖项用于实现任务,而项目源代码不需要它们。这些依赖项可以在它们自己的配置和类路径中声明。以下是步骤
作为示例,考虑一个 proguard
任务。此任务需要 ProGuard jar 文件才能运行该工具。首先,定义并添加新的配置
lazy val ProguardConfig = config("proguard").hide
ivyConfigurations += ProguardConfig
然后,
// Add proguard as a dependency in the custom configuration.
// This keeps it separate from project dependencies.
libraryDependencies +=
"net.sf.proguard" % "proguard" % "4.4" % ProguardConfig.name
// Extract the dependencies from the UpdateReport.
ProguardConfig / managedClasspath := {
// these are the types of artifacts to include
val artifactTypes: Set[String] = (ProguardConfig / classpathTypes).value
Classpaths.managedJars(proguardConfig, artifactTypes, update.value)
}
// Use the dependencies in a task, typically by putting them
// in a ClassLoader and reflectively calling an appropriate
// method.
proguard := {
val cp: Seq[File] = (ProguardConfig / managedClasspath).value
// ... do something with , which includes proguard ...
}
定义中间类路径是可选的,但对于调试或如果多个任务需要使用它,它可能会有用。也可以在内联中指定工件类型。以下 proguard
任务替代方案将如下所示
proguard := {
val artifactTypes = Set("jar")
val cp =
Classpaths.managedJars(proguardConfig, artifactTypes, update.value)
// ... do something with , which includes proguard ...
}
可以注册一些额外的 jar 包,这些 jar 包将被放置在 sbt 的类路径上。通过 State,可以获取一个 xsbti.ComponentProvider,它管理应用程序组件。组件是 ~/.sbt/boot/
目录中的一组文件,在本例中,应用程序是 sbt。除了基本类路径之外,"extra" 组件中的组件也包含在 sbt 的类路径上。
(注意:应用程序类路径上的额外组件由启动器配置文件 boot.properties
的 [main]
部分中的 components
属性声明。)
由于这些组件被添加到 ~/.sbt/boot/
目录中,而 ~/.sbt/boot/
可能为只读,因此这可能会失败。在这种情况下,用户通常是故意这样设置 sbt 的,因此通常不需要错误恢复(只需一条简短的错误消息解释情况即可)。
以下代码可以在需要 State => State
的地方使用,例如在 onLoad
设置(如下所述)或在 命令 中。它将一些文件添加到 "extra" 组件中,如果这些文件尚未添加,则会重新加载 sbt。请注意,重新加载将删除用户的会话状态。
def augment(extra: Seq[File])(s: State): State = {
// Get the component provider
val cs: xsbti.ComponentProvider = s.configuration.provider.components()
// Adds the files in 'extra' to the "extra" component
// under an exclusive machine-wide lock.
// The returned value is 'true' if files were actually copied and 'false'
// if the target files already exists (based on name only).
val copied: Boolean = s.locked(cs.lockFile, cs.addToComponent("extra", extra.toArray))
// If files were copied, reload so that we use the new classpath.
if(copied) s.reload else s
}
请参阅 如何在启动时采取行动。
以下示例维护一个项目加载次数的计数,并打印该数字。
{
// the key for the current count
val key = AttributeKey[Int]("loadCount")
// the State transformer
val f = (s: State) => {
val previous = s get key getOrElse 0
println("Project load count: " + previous)
s.put(key, previous + 1)
}
Global / onLoad := {
val previous = (Global / onLoad).value
f compose previous
}
}
设置初始化器按顺序执行。如果设置的初始化依赖于尚未初始化的其他设置,sbt 将停止加载。
在本例中,我们尝试在 libraryDependencies
初始化为一个空序列之前将一个库追加到它。
libraryDependencies += "commons-io" % "commons-io" % "1.4" % "test"
disablePlugins(plugins.IvyPlugin)
要更正此问题,请包含 IvyPlugin 插件设置,其中包括 libraryDependencies := Seq()
。因此,我们只是删除了显式禁用。
libraryDependencies += "commons-io" % "commons-io" % "1.4" % "test"
当使用 作用域设置 时,会出现这种错误的更微妙的变化。
// error: Reference to uninitialized setting
settings = Seq(
libraryDependencies += "commons-io" % "commons-io" % "1.2" % "test",
fullClasspath := fullClasspath.value.filterNot(_.data.name.contains("commons-io"))
)
此设置在测试和编译作用域之间有所不同。解决方案是使用作用域设置,既作为初始化器的输入,又是我们更新的设置。
Compile / fullClasspath := (Compile / fullClasspath).value.filterNot(_.data.name.contains("commons-io"))
当已发布的校验和(例如 sha1 或 md5 哈希值)与为下载的工件(例如 jar 或 pom.xml)计算的校验和不同时,就会出现此错误。此类错误的一个示例是
[warn] problem while downloading module descriptor:
https://repo1.maven.org/maven2/commons-fileupload/commons-fileupload/1.2.2/commons-fileupload-1.2.2.pom:
invalid sha1: expected=ad3fda4adc95eb0d061341228cc94845ddb9a6fe computed=0ce5d4a03b07c8b00ab60252e5cacdc708a4e6d8 (1070ms)
通常应将无效校验和报告给存储库所有者(如针对上述错误所做的那样,已完成)。同时,您可以使用以下设置暂时禁用检查
checksums in update := Nil
有关详细信息,请参阅 库管理。
这个问题经常出现。插件只发布了用于 sbt 使用的 Scala 版本(目前为 2.12)。您仍然可以在交叉编译期间使用插件,因为 sbt 只会查找插件的 2.12 版本。
... 除非您在错误的位置指定插件!
一个常见的错误是将全局插件定义放在 ~/.sbt/plugins.sbt
中。这是错误的。 ~/.sbt
中的 .sbt
文件为每个构建加载,也就是说,为每个交叉编译加载。因此,如果您为 Scala 2.11.0 构建,sbt 将尝试查找为 2.11.0 编译的插件版本,而它通常不会找到。这是因为它不知道依赖项是一个插件。
要告诉 sbt 依赖项是一个 sbt 插件,请确保您在 ~/.sbt/plugins/
中的 .sbt
文件中定义全局插件。sbt 知道 ~/.sbt/plugins
中的文件仅供 sbt 本身使用,而不是作为一般构建定义的一部分。如果您在该目录下的文件中定义插件,它们不会影响您的交叉编译。任何以 .sbt
结尾的文件名都可以,但大多数人使用 ~/.sbt/plugins/build.sbt
或 ~/.sbt/plugins/plugins.sbt
。
请参阅 社区插件,获取当前可用插件的列表。