使用SBT构建Scala应用

SBT 简介

SBT是Simple Build Tool的简称,如果读者使用过Maven,那么可以简单将 SBT  看做是Scala世界的Maven,虽然二者各有优劣,但完成的工作基本是类似的。虽然  Maven同样可以管理Scala项目的依赖并进行构建,但SBT的某些特性却让人如此着迷

相关信息参见: http://www.scala-sbt.org/release/tutorial/zh-cn/index.html  里面有详细的介绍, 本文是从网络搜集的一些介绍供参考。

比如:

使用Scala作为DSL来定义build文件(one language rules them all  )  ;

通过触发执行(trigger execution) 特性支持持续的编译与测试;

增量编译;^[SBT的增量编译支持因为如此优秀,已经剥离为Zinc,可被Eclipse,Maven,Gradle  等使用  ]
可以混合构建Java和Scala项目;

并行的任务执行;

可以重用Maven或者ivy的repository进行依赖管理;

等等这些,都是SBT得以在Scala的世界里广受欢迎的印记。

SBT的发展可以分为两个阶段,即SBT_0.7.x时代以及SBT_0.10.x以后的时代。

目前来讲,SBT_0.7.x已经很少使用,大部分公司和项目都已经迁移到0.10.x以后的版本上来,最新的是0.12版本。0.10.x之后的版本build定义采用了新的Settings系统,与最初0.7.x版本采用纯Scala代码来定义build文件大相径庭,虽然笔者在迁移之前很抵触(因为0.7.x中采用Scala定义build文件的做法可以体现很好的统一性),但还是升级并接纳了0.10.x以后的版本,并且也逐渐意识到,  虽然新的版本初看起来很复杂,  但一旦了解了其设计和实现的哲学跟思路,就会明白这种设计可以更便捷的定义build  文件。而且可选的build文件方式也同样运行采用Scala代码来定义,即并未放弃统一性的思想,以上是  SBT  的简单介绍.

sbt使用

build.sbt非常简单,它隐藏了sbt实际是如何工作的。sbt构建是用Scala代码定义的。这些代码自身需要建立。还有比sbt更好的方式吗?

project文件夹是你项目中的另一个子项目,它知道如何构建你的项目。子项目(理论上)可以可以做任何其他项目能做的事情。你的构建定义就是一个sbt项目。

如果你喜欢,你可以通过创建一个project/project/文件夹,来调整构建定义项目的构建定义。

这有一个例证。

hello/                  # your project's base directory

Hello.scala         # a source file in your project (could be in
#   src/main/scala too)

build.sbt           # build.sbt is part of the source code for the
#   build definition project inside project/

project/            # base directory of the build definition project

Build.scala     # a source file in the project/ project,
#   that is, a source file in the build definition

build.sbt       # this is part of a build definition for a project
#   in project/project ; build definition's build
#   definition

project/        # base directory of the build definition project
#   for the build definition

Build.scala # source file in the project/project/ project

别担心!多数时候你并不需要全部。但理解原理是很有帮助的。

顺便说一下:任何时候,以.scala或.sbt结尾的文件都会被用上,把他们命名为build.sbt和Build.scala仅仅是惯例而已。这也意味着多个文件也是允许的。

构建定义项目中的.scala源文件

.sbt文件会合并入它们的兄弟项目文件夹。回顾一下项目布局:

hello/                  # your project's base directory

build.sbt           # build.sbt is part of the source code for the
#   build definition project inside project/

project/            # base directory of the build definition project

Build.scala     # a source file in the project/ project,
#   that is, a source file in the build definition

build.sbt中的Scala表达式会编译并与Build.scala(或project/文件夹中的其他.scala文件)合并。

``.sbt`` files in the base directory for a project become part of the ``project`` build definition project also located in that base directory.(绕晕了)
项目根目录下的`.sbt`文件如果成为构建定义项目的一部分,也要放在相应的根目录下(是这个意思吗?)。

.sbt文件格式是将设置添加到构建定义项目的方便的速记法。

关联build.sbt到Build.scala

为了混合你的构建定义中的.sbt和.scala文件,你需要了解如何关联他们。

以下面两个文件举例。首先,如果你的项目在hello文件夹中,如下方式创建hello/project/Build.scala:
(译注:0.12.x略有不同,见http://www.scala-sbt.org/0.12.3/docs/Getting-Started/Full-Def.html#relating-build-sbt-to-build-scala)

import sbt._ import Keys._ object HelloBuild extends Build { val sampleKeyA = settingKey[String]("demo key A") val sampleKeyB = settingKey[String]("demo key B") val sampleKeyC = settingKey[String]("demo key C") val sampleKeyD = settingKey[String]("demo key D") override lazy val settings = super.settings ++ Seq(sampleKeyA := "A: in Build.settings in Build.scala", resolvers := Seq()) lazy val root = Project(id = "hello", base = file("."), settings = Project.defaultSettings ++ Seq(sampleKeyB := "B: in the root project settings in Build.scala")) }

现在,如下方式创建hello/build.sbt:

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

启动sbt交互命令窗口。输入inspect sampleKeyA,然后你讲看到(除了别的以外):

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

然后输入inspect sampleKeyC,你将看到:

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

需要注意“Provided by”显示了这两个值的相同范围。也就是,.sbt文件中的sampleKeyC in ThisBuild等价于将setting放在.scala文件的Build.settings列表中。sbt从这两个地方获取构建范围的设置来创建构建 定义。

现在,输入inspect sampleKeyB:

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

要注意sampleKeyB是项目范围的({file:/home/hp/checkout/hello/}hello)而不是整个构建({file:/home/hp/checkout/hello/})。

正如你可能已经猜到的,inspect sampleKeyB和sampleKeyD匹配。

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

sbt将.sbt文件的设置追加到Build.settings和Project.settings中的设置,这意味着.sbt中的设置有优先权。 尝试修改Build.scala中的sampleC或sampleD,它们依然是build.sbt中的值。build.sbt中的设置将胜过 Build.scala中的。

另一个你需要注意的:sampleKeyC和sampleKeyD在build.sbt内部有效。这是因为sbt将你的Build对象的内容导入到.sbt文件。在这个例子中build.sbt文件的import HelloBuild._是隐式完成的。

概括起来就是:

在.scala文件中,你可以将设置添加到Build.settings,sbt可以找到它,而且它们自动是构建范围的。
在.scala文件中,你可以将设置添加到Project.settings,sbt可以找到它,而且它们自动是项目范围的。
.scala文件中任何你写的Build对象,它们的内容将导入到.sbt文件并对它有效。
.sbt文件中的设置会被追加到.scala文件的设置。
.sbt文件中的任何设置都是项目范围的,除非你明确指定了另一个范围。

什么时候该使用.scala文件

在.scala文件中,你可以写任何Scala代码,包括值,对象和方法定义。

一种推荐的方式是,在.sbt文件中定义设置,只在实际需要一个值,对象或方法定义的时候使用.scala文件。

有一个构建定义,它是你主项目的嵌套项目。.sbt和.scala文件将一起被编译来创建单一的定义。

在单一构建中定义多个项目时.scala文件也是必需的。更多内容在Multi-Project Builds中讨论。

(在多项目构建中使用.sbt文件的一个缺点是,它们会在散布不同的文件夹中。因此,如果有子项目,很多人更喜欢把设置放在他们的.scala文件中。在你看完多项目构建是如何工作的之后将更加清晰。)

交互模式中的构建定义项目

你可以切换sbt交互命令行,使包含构建定义项目的project/作为当前项目。只需要这么做,输入reload plugins。

> reload plugins
[info] Set current project to default-a0e8e4 (in build file:/home/hp/checkout/hello/project/)
> show sources
[info] ArrayBuffer(/home/hp/checkout/hello/project/Build.scala)
> reload return
[info] Loading project definition from /home/hp/checkout/hello/project
[info] Set current project to hello (in build file:/home/hp/checkout/hello/)
> show sources
[info] ArrayBuffer(/home/hp/checkout/hello/hw.scala)
>

如上所示,您可以使用reload return离开构建定义项目并返回你的常规项目。
提示:所有都是不可变的

如果你认为build.sbt中的设置是被添加到Build和Project对象的settings域,那就错了。相反,Build和 Project,以及build.sbt中的设置,会被串联入另一个不可变的列表,然后作用于sbt。Build和Project对象只是构成完整构建定 义的一部分。

事实上,还有其他来源的设置。它们按这个顺序来追加:

.scala文件中来自Build.settings和Project.settings的设置。
你的用户全局设置;例如,在~/.sbt/build.sbt中你可以定义设置来影响所有的项目。
通过插件注入的设置,见后续使用插件章节。
来自项目.sbt文件的设置。
构建定义项目(也就是项目中的项目)拥有全局插件(~/.sbt/plugins)中添加的设置。使用插件章节有更详细的说明。

后面的设置会覆盖前面的。设置的完整列表构成了构建定义。

发表评论