sbt编译入门

非托管依赖 为放在 lib 目录下的 jar 文件
托管依赖 配置在构建定义中,而且会自动从仓库(repository)中下载
非托管依赖java

大多数人会用托管依赖而非非托管依赖。可是非托管依赖在起步阶段会简单不少。git

非托管依赖像这样工做:将 jar 文件放在 lib 文件夹下,而后它们将会被添加到项目的 classpath 中。没有更多的事情了!github

你也能够将测试依赖的 jar 文件放在 lib 目录下,好比 ScalaCheck,Specs2,ScalaTest。apache

lib 目录下的全部依赖都会在 classpaths(为了 compile, test, run 和 console)。若是你想对其中的一个改变 classpath, 你须要作适当调整,例如 dependencyClasspath in Compile 或者dependencyClasspath in Runtime。缓存

若是用非托管依赖的话,不用往 build.sbt 文件中添加任何内容,不过你能够改变unmanagedBase key,若是你想用一个不一样的目录而非 lib。安全

用 custom_lib 替代 lib:服务器

unmanagedBase := baseDirectory.value / "custom_lib"
baseDirectory 是项目的根目录,因此在这里你依据 baseDirectory 的值改变了 unmanagedBase,经过在 更多关于设置 中介绍的一个特殊的 value 方法。maven

同时也有一个列举 unmanagedBase 目录下全部 jar 文件的 task 叫 unmanagedJars。若是你想用多个目录或者作一些更加复杂的事情,你可能须要用一个能够作其它事情的 task 替换整个unmanagedJars task, 例如清空 Compile configuration 的列表,不考虑 lib 目录下的文件:ide

unmanagedJars in Compile := Seq.empty[sbt.Attributed[java.io.File]]
托管依赖测试

sbt 使用 Apache Ivy 来实现托管依赖,因此若是你对 Ivy 或者 Maven 比较熟悉的话,你不会有太多的麻烦。

libraryDependencies Key

大多数时候,你能够很简单的在 libraryDependencies 设置项中列出你的依赖。也能够经过 Maven POM 文件或者 Ivy 配置文件来配置依赖,并且能够经过 sbt 来调用这些外部的配置文件。 你能够从这里获取更详细的内容。

像这样定义一个依赖,groupId, artifactId 和 revision 都是字符串:

libraryDependencies += groupID % artifactID % revision
或者像这样, 用字符串或者 Configuration val 当作 configuration:

libraryDependencies += groupID % artifactID % revision % configuration
libraryDependencies 在 Keys 中像这样声明:

val libraryDependencies = settingKeySeq[ModuleID]
方法 % 从字符串建立 ModuleID 对象,而后将 ModuleID 添加到 libraryDependencies 中。

固然,要让 sbt(经过 Ivy)知道从哪里下载模块。若是你的模块和 sbt 来自相同的某个默认的仓库,这样就会工做。例如,Apache Derby 在标准的 Maven2 仓库中:

libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3"
若是你在 build.sbt 中输入上面这些内容,而后执行 update,sbt 会将 Derby 下载到~/.ivy2/cache/org.apache.derby/。(顺便提一下, compile 依赖于 update,因此 大多数时候不须要手动的执行 update。)

固然,你也能够经过 ++= 一次将全部依赖做为一个列表添加:

libraryDependencies ++= Seq(
groupID % artifactID % revision,
groupID % otherID % otherRevision
)
在不多状况下,你也会须要在 libraryDependencies 上用 := 方法。

经过 %% 方法获取正确的 Scala 版本

若是你用是 groupID %% artifactID % revision 而不是 groupID % artifactID % revision(区别在于 groupID 后面是 %%),sbt 会在 工件名称中加上项目的 Scala 版本号。 这只是一种快捷方法。你能够这样写不用 %%:

libraryDependencies += "org.scala-tools" % "scala-stm_2.11.1" % "0.3"
假设这个构建的 scalaVersion 是 2.11.1,下面这种方式是等效的(注意 "org.scala-tools" 后面是 %%):

libraryDependencies += "org.scala-tools" %% "scala-stm" % "0.3"
这个想法是不少依赖都会被编译以后给多个 Scala 版本,而后你想确保和项目匹配的某一个是二进制兼容的。

实践中的复杂度在于一般一个依赖会和稍微不一样的 Scala 版本一块儿工做;可是 %% 就没有那么智能了。因此若是一个依赖要求版本为 2.10.1,可是你使用的 scalaVersion := "2.10.4", 你不可能使用 %% 方法即便 2.10.1 的版本颇有可能工做。若是 %% 中止工做了,只须要去检查那个依赖是基于哪一个 Scala 版本构建的,而后硬编码你认为能够工做的版本号(假设已经有一个)。

参见 交叉构建 获取更多信息。

Ivy 修正

groupID % artifactID % revision 中的 revision 不须要是一个固定的版本号。Ivy 可以根据你指定的约束选择一个模块的最新版本。你指定 "latest.integration","2.9.+" 或者 "[1.0,)",而不是 一个固定的版本号,像 "1.6.1"。参看Ivy 修订文档获取详细内容。

解析器

不是全部的依赖包都放在同一台服务器上,sbt 默认使用标准的 Maven2 仓库。若是你的依赖不在默认的仓库中,你须要添加 resolver 来帮助 Ivy 找到它。

经过如下形式添加额外的仓库:

resolvers += name at location
在两个字符串中间有一个特殊的 at。

例如:

resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"
resolvers key 在 Keys 中像这样定义:

val resolvers = settingKeySeq[Resolver]
at 方法经过两个字符串建立了一个 Resolver 对象。

sbt 会搜索你的本地 Maven 仓库若是你将它添加为一个仓库:

resolvers += "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
或者,为了方便起见:

resolvers += Resolver.mavenLocal
参看法析器获取更多关于定义其余类型的仓库的内容。

覆写默认的解析器

resolvers 不包含默认的解析器,仅仅经过构建定义添加额外的解析器。

sbt 将 resolvers 和一些默认的仓库组合起来构成 externalResolvers。

然而,为了改变或者移除默认的解析器,你须要覆写externalResolvers 而不是 resolvers。

Per-configuration dependencies

一般一个依赖只被测试代码使用(在 src/test/scala 中,经过 Test configuration 编译)。

若是你想要一个依赖只在 Test configuration 的 classpath 中出现而不是 Compile configuration,像这样添加 % "test":

libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3" % "test"
也可能也会像这样使用类型安全的 Test configuration:

libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3" % Test
如今,若是你在 sbt 的命令提示行里输入 show compile:dependencyClasspath,你不该该看到 derby jar。可是若是你输入 show test:dependencyClasspath, 你应该在列表中看到 derby jar。

一般,测试相关的依赖,如 ScalaCheck, Specs2和 ScalaTest 将会被定义为 % "test"。

库依赖更详细的内容和技巧在这里。

经常使用命令

actions – 显示对当前工程可用的命令
update – 下载依赖
compile – 编译代码
test – 运行测试代码
package – 建立一个可发布的jar包
publish-local – 把构建出来的jar包安装到本地的ivy缓存
publish – 把jar包发布到远程仓库(若是配置了的话)
更多命令

test-failed – 运行失败的spec
test-quick – 运行全部失败的以及/或者是由依赖更新的spec
clean-cache – 清除全部的sbt缓存。相似于sbt的clean命令
clean-lib – 删除lib_managed下的全部内容

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

使用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之旅,那么让咱们先从SBT的安装和配置开始吧!

SBT安装和配置

SBT的安装和配置能够采用两种方式,一种是全部平台都通用的安装配置方式,另外一种是跟平台相关的安装和配置方式,下面咱们分别对两种方式进行详细介绍。

全部平台通用的安装配置方式

全部平台通用的安装和配置方式只须要两步:

下载sbt boot launcher
本书采用最新的sbt0.12,其下载地址为http://typesafe.artifactoryonline.com/typesafe/ivy-releases/org.scala-sbt/sbt-launch/0.12.0/sbt-launch.jar
建立sbt启动脚本(启动脚本是平台相关的)
若是是Linux/Unit系统,建立名称为sbt的脚本,并赋予其执行权限,并将其加到PATH路径中; sbt脚本内容相似于 java -Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=384M -jar dirname $0/sbt-launch.jar "$@", 能够根据状况调整合适的java进程启动参数;
若是是Windows系统,则建立sbt.bat命令行脚本,一样将其添加到PATH路径中。 脚本内容相似于set SCRIPT_DIR=%~dp0 \n java -Xmx512M -jar "%SCRIPT_DIR%sbt-launch.jar" %*
以上两步便可完成sbt的安装和配置。

平台相关的安装配置方式

笔者使用的是Mac系统,安装sbt只须要执行brew install sbt便可(由于我已经安装有homebrew这个包管理器),使用macport一样能够很简单的安装sbt - sudo port install sbt;

若是读者使用的是Linux系统,那么这些系统一般都会有相应的包管理器可用,好比yum或者apt,安装和配置sbt也一样轻松,只要简单的运行yum install sbt 或者 apt-get install sbt命令就能搞定(固然,一般须要先将有sbt的repository添加到包管理器的列表中);

Windows的用户也能够偷懒,只要下载MSI文件直接安装,MSI文件下载地址为http://scalasbt.artifactoryonline.com/scalasbt/sbt-native-packages/org/scala-sbt/sbt/0.12.0/sbt.msi

以上方式基本上囊括三大主流操做系统特定的安装和配置方式,其它特殊状况读者能够酌情处理 ^_^

SBT基础篇

既然咱们已经安装和配置好了SBT,那就让咱们先尝试构建一个简单的Scala项目吧!

Hello, SBT

在SBT的眼里, 一个最简单的Scala项目能够极简到项目目录下只有一个.scala文件,好比HelloWorld.scala:

object HelloWorld{
def main(args: Array[String]) {
println("Hello, SBT")
}
}
假设咱们HelloWorld.scala放到hello目录下,那么能够尝试在该目录下执行:

$ sbt

run
[info] Running HelloWorld
Hello, SBT
[success] Total time: 2 s, completed Sep 2, 2012 7:54:58 PM
怎么样,是否是很简单那? (画外音: 这岂止是简单,简直就是个玩具嘛,有啥用嘛?! 来点儿实在的行不?)

好吧, 笔者也认可这过小儿科了,因此,咱们仍是来点儿"干货"吧!

NOTE: 以上实例简单归简单,但可不要小看它哦,你可知道笔者开始就由于忽略了如此简单的小细节而"光阴虚度"? 该实例的项目目录下,没有定义任何的build文件,却依然能够正确的执行sbt命令, 实际上, 即便在一个空目录下执行sbt命令也是能够成功进入sbt的console的。 因此,只要了解了sbt构建的这个最低条件,那么,当你无心间在非项目的根目录下执行了相应sbt命令而出错的时候,除了检查build文件的定义,另外要注意的就是,你是否在预想的项目根目录下面执行的sbt命令!
SBT项目工程结构详解

通常意义上讲,SBT工程项目的目录结构跟Maven的很像, 若是读者接触过Maven,那么能够很容易的理解以下内容。

一个典型的SBT项目工程结构以下图所示:

src目录详解

Maven用户对src目录的结构应该不会感到陌生,下面简单介绍各个子目录的做用。

src/main/java目录存放Java源代码文件
src/main/resources目录存放相应的资源文件
src/main/scala目录存放Scala源代码文件
src/test/java目录存放Java语言书写的测试代码文件
src/test/resources目录存放测试起见使用到的资源文件
src/test/scala目录存放scala语言书写的测试代码文件
build.sbt详解

读者能够简单的将build.sbt文件理解为Maven项目的pom.xml文件,它是build定义文件。 SBT运行使用两种形式的build定义文件,一种就是放在项目的根目录下,即build.sbt, 是一种简化形式的build定义; 另外一种放在project目录下,采用纯Scala语言编写,形式更加复杂,固然,也更完备,更有表现力。

咱们暂时先介绍build.sbt的定义格式,基于scala的build定义格式咱们稍后再细说。

一个简单的build.sbt文件内容以下:

name := "hello" // 项目名称

organization := "xxx.xxx.xxx" // 组织名称

version := "0.0.1-SNAPSHOT" // 版本号

scalaVersion := "2.9.2" // 使用的Scala版本号

// 其它build定义
其中, name和version的定义是必须的,由于若是想生成jar包的话,这两个属性的值将做为jar包名称的一部分。

build.sbt的内容其实很好理解,能够简单理解为一行表明一个键值对(Key-Value Pair),各行之间以空行相分割。

固然,实际状况要比这复杂,须要理解SBT的Settings引擎才能够彻底领会, 以上原则只是为了便于读者理解build.sbt的内容。

除了定义以上项目相关信息,咱们还能够在build.sbt中添加项目依赖:

// 添加源代码编译或者运行期间使用的依赖
libraryDependencies += "ch.qos.logback" % "logback-core" % "1.0.0"

libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.0.0"

// 或者

libraryDependencies ++= Seq(
"ch.qos.logback" % "logback-core" % "1.0.0",
"ch.qos.logback" % "logback-classic" % "1.0.0",
...
)

// 添加测试代码编译或者运行期间使用的依赖
libraryDependencies ++= Seq("org.scalatest" %% "scalatest" % "1.8" % "test")
甚至于直接使用ivy的xml定义格式:

ivyXML :=











在这里,咱们排除了某些没必要要的依赖,而且声明了某个定制过的依赖声明。

固然, build.sbt文件中还能够定义不少东西,好比添加插件,声明额外的repository,声明各类编译参数等等,咱们这里就不在一一赘述了。

project目录即相关文件介绍

project目录下的几个文件实际上都是非必须存在的,能够根据状况添加。

build.properties文件声明使用的要使用哪一个版本的SBT来编译当前项目, 最新的sbt boot launcher能够可以兼容编译全部0.10.x版本的SBT构建项目,好比若是我使用的是0.12版本的sbt,但却想用0.11.3版本的sbt来编译当前项目,则能够在build.properties文件中添加sbt.version=0.11.3来指定。 默认状况下,当前项目的构建采用使用的sbt boot launcher对应的版本。

plugins.sbt文件用来声明当前项目但愿使用哪些插件来加强当前项目使用的sbt的功能,好比像assembly功能,清理ivy local cache功能,都有相应的sbt插件供使用, 要使用这些插件只须要在plugins.sbt中声明便可,不用本身去再造轮子:

resolvers += Resolver.url("git://github.com/jrudolph/sbt-dependency-graph.git")

resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"

addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.1.0")

addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.6.0")
在笔者的项目中, 使用sbt-idea来生成IDEA IDE对应的meta目录和文件,以便可以使用IDEA来编写项目代码; 使用sbt-dependency-graph来发现项目使用的各个依赖之间的关系;

为了可以成功加载这些sbt插件,咱们将他们的查找位置添加到resolovers当中。有关resolvers的内容,咱们后面将会详细介绍,这里注意一个比较有趣的地方就是,sbt支持直接将相应的github项目做为依赖或者插件依赖,而不用非得先将相应的依赖或者插件发布到maven或者ivy的repository当中才可使用。

其它

以上目录和文件一般是在建立项目的时候须要咱们建立的,实际上, SBT还会在编译或者运行期间自动生成某些相应的目录和文件,好比SBT会在项目的根目录下和project目录下自动生成相应的target目录,并将编译结果或者某些缓存的信息置于其中, 通常状况下,咱们不但愿将这些目录和文件记录到版本控制系统中,因此,一般会将这些目录和文件排除在版本管理以外。

好比, 若是咱们使用git来作版本控制,那么就能够在.gitignore中添加一行"target/"来排除项目根目录下和project目录下的target目录及其相关文件。

TIPS

在sbt0.7.x时代, 咱们只要建立项目目录,而后在项目目录下敲入sbt,则应该建立哪些须要的目录和文件就会由sbt自动为咱们生成, 而sbt0.10以后,这项福利就没有了。 因此,刚开始,咱们可能会认为要很苦逼的执行一长串命令来生成相应的目录和文件:

$ touch build.sbt
$ mkdir src
$ mkdir src/main
$ mkdir src/main/java
$ mkdir src/main/resources
$ mkdir src/main/scala
$ mkdir src/test
$ mkdir src/test/java
$ mkdir src/test/resources
$ mkdir src/test/scala
$ mkdir project
$ ...

SBT的使用

SBT支持两种使用方式:

批处理模式(batch mode)
可交互模式(interactive mode)
批处理模式是指咱们能够在命令行模式下直接依次执行多个SBT命令, 好比:

$ sbt compile test package

而可交互模式则直接运行sbt,后面不跟任何SBT命令,在这种状况下, 咱们将直接进入sbt控制台(console), 在sbt控制台中,咱们能够输入任何合法的sbt命令并得到相应的反馈:

$ sbt

compile
[success] Total time: 1 s, completed Sep 3, 2012 9:34:58 PM
test
[info] No tests to run for test:test
[success] Total time: 0 s, completed Sep 3, 2012 9:35:04 PM
package
[info] Packaging XXX_XXX_2.9.2-0.1-SNAPSHOT.jar ...
[info] Done packaging.
[success] Total time: 0 s, completed Sep 3, 2012 9:35:08 PM

TIPS

在可交互模式的sbt控制台下,能够输入help获取进一步的使用信息。
在以上实例中,咱们依次执行了compile, test和package命令, 实际上, 这些命令之间是有依赖关系的,若是仅仅是为了package,那么,只须要执行package命令便可, package命令依赖的compile和test命令将先于package命令执行,以保证它们之间的依赖关系得以知足。

除了compile,test和package命令, 下面列出了更多可用的sbt命令供读者参考:

compile
test-compile
run
test
package
这些命令在某些状况下也能够结合SBT的触发执行(Trigger Execution)机制一块儿使用, 惟一须要作的就只是在相应的命令前追加~符号好比:

$ sbt ~compile

原则上, ~和相应命令之间应该用空格分隔,不过对于通常的命令来说,直接前缀~也是能够的,就跟咱们使用~compile的方式同样。
SBT的依赖管理

在SBT中, 类库的依赖管理能够分为两类:

unmanaged dependencies
managed dependencies
大部分状况下,咱们会采用managed dependencies方式来管理依赖关系,但也不排除为了快速构建项目环境等特殊状况下,直接使用unmanaged dependencies来管理依赖关系。

Unmanaged Dependencies简介

要使用unmanaged dependencies的方式来管理依赖其实很简单,只须要将想要放入当前项目classpath的jar包放到lib目录下便可。

若是对默认的lib目录看着不爽, 咱们也能够经过配置来更改这个默认位置,好比使用3rdlibs:

unmanagedBase <<= baseDirectory { base => base / "3rdlibs" }
这里可能须要解释一下以上配置。 首先unmanagedBase这个Key用来表示unmanaged dependencies存放第三方jar包的路径, 具体的值默认是lib, 咱们为了改变这个Key的值, 采用<<=操做符, 根据baseDirectory的值转换并计算出一个新值赋值给unmanagedBase这个Key, 其中, baseDirectory指的是当前项目目录,而<<=操做符(实际上是Key的方法)则负责从已知的某些Key的值计算出新的值并赋值给指定的Key。

关于Unmanaged dependencies,通常状况下,须要知道的基本上就这些。

Managed Dependencies详解

sbt的managed dependencies采用Apache Ivy的依赖管理方式, 能够支持从Maven或者Ivy的Repository中自动下载相应的依赖。

简单来讲,在SBT中, 使用managed dependencies基本上就意味着往libraryDependencies这个Key中添加所须要的依赖, 添加的通常格式以下:

libraryDependencies += groupID % artifactID % revision
好比:

libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3"
这种格式实际上是简化的常见形式,实际上,咱们还能够作更多微调, 好比:

(1) libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3" % "test"
(2) libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3" exclude("org", "artifact")
(3) libraryDependencies += "org.apache.derby" %% "derby" % "10.4.1.3"
(1)的形式容许咱们限定依赖的范围只限于测试期间; (2)的形势容许咱们排除递归依赖中某些咱们须要排除的依赖; (3)的形式则会在依赖查找的时候,将当前项目使用的scala版本号追加到artifactId以后做为完整的artifactId来查找依赖,好比若是咱们的项目使用scala2.9.2,那么(3)的依赖声明实际上等同于"org.apache.derby" %% "derby_2.9.2" % "10.4.1.3",这种方式更可能是为了简化同一依赖类库存在有多个Scala版本对应的发布的状况。

若是有一堆依赖要添加,一行一行的添加是一种方式,其实也能够一次添加多个依赖:

libraryDependencies ++= Seq("org.apache.derby" %% "derby" % "10.4.1.3",
"org.scala-tools" %% "scala-stm" % "0.3",
...)
Resovers简介

对于managed dependencies来讲,虽然咱们指定了依赖哪些类库,但有没有想过,SBT是如何知道到哪里去抓取这些类库和相关资料那?!

实际上,默认状况下, SBT回去默认的Maven2的Repository中抓取依赖,但若是默认的Repository中找不到咱们的依赖,那咱们能够经过resolver机制,追加更多的repository让SBT去查找并抓取, 好比:

resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"
at^[at其实是String类型进行了隐式类型转换(Implicit conversion)后目标类型的方法名]以前是要追加的repository的标志名称(任意取),at后面则是要追加的repository的路径。

除了可远程访问的Maven Repo,咱们也能够将本地的Maven Repo追加到resolver的搜索范围:

resolvers += "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"

相关文章
相关标签/搜索