sbt笔记四 .sbt构建定义

.sbt vs .scala

sbt构建定义包括项目根目录下的sbt文件和project子目录下的scala文件。 
能够只用一种,也能够两种都用。一种比较好的方式是:大部分在.sbt中定义,若是.sbt处理不了,才用.scala,例如: 
    定制sbt(添加新的设置和任务) 
    定义嵌套项目(即子模块) 

这里只讨论.sbt。 web

所谓构建定义 

** PLEASE READ THIS SECTION ** 
在检查项目和处理构建定义文件以后,sbt最终以一个不可变映射(键值对的集合)来描述构建。 
好比,项目名的键名为name,映射到一个字符串的值。 

构建定义文件并不直接(仍是当即?)影响sbt的映射。 
(Build definition files do not affect sbt's map directly.) 

相反,构建定义建立一个巨大的Setting[T]对象列表(T是map中值的类型)。Setting描述了一个到map的转换,好比添加一个新的键值对或追加一个已存在的值。(本着函数式编程的精神,一个转换返回一个新映射,而不是更新一个旧的映射) 
在build.sbt中,用以下的方式建立一个Setting[String]来表示项目名称: 
name := "hello"
这个Setting[String]将映射中的name键与"hello"关联(添加或替换)。将转换后的映射做为新的sbt映射。 
为了建立映射,sbt首先将settings列表排序以便将对同一键名所作的修改集中在一块儿,而且若是值依赖于其余的键,那么会在依赖键以后处理。而后sbt利用排好序的Setting列表,依次应用到映射中。 

总结:构建定义定义了一个Setting[T]列表,Setting[T]是会影响sbt映射键值对的转换,T是值得类型。 apache

build.sbt如何定义settings 

build.sbt定义了一个Seq[Setting[_]];它是一个Scala表达式列表,以空行间隔,每一行是序列的一个元素。若是在.sbt文件的开头写入"Seq("并在结尾写入")"(不包含引号),并把空行换成逗号,就能够看到等价的.scala代码。 
例如: 
name := "hello"

version := "1.0"

scalaVersion := "2.9.2"


build.sbt中的表达式都是相互独立的,并且他们是表达式而不是完整的scala语句。在build.sbt中不能定义顶级的val,object,class和方法。  编程

上例中,:=左边的是键,键必须是SettingKey[T],TaskKey[T]或InputKey[T]类型的实例,T表示预期值的类型。 

Keys有一个:=方法,它返回一个Setting[T],能够用Java语法风格来调用这个方法: 
name.:=("hello")

在Scala中用name := "hello"代替(Java风格好丑...) 

name这个键的:=方法返回一个Setting[String],而name键自身的类型为SettingKey[String]。在本例中,返回值Setting[String]是一个转换,它把sbt映射中name键的值设为"hello"(添加或替换)。 

若是值的类型错误,构建定义将没法编译: 
scala name := 42  // will not compile ### Settings are separated by blank lines

也不能这么写: 
// 没有空行,没法工做
name := "hello"
version := "1.0"
scalaVersion := "2.9.2"

sbt须要某种定界符来代表表达式在哪结束,下一个在哪开始。 函数式编程

.sbt文件包含一个Scala表达式列表,它不是完整的Scala程序,这些表达式必须先切分,而后逐个传给编译器。 函数

若是想要一个完整的Scala程序,用.scala文件;.sbt文件是可选的。 ui

键在Keys对象中定义

内置的键都是Keys对象的字段。build.sbt隐式引入了sbt.Keys._。因此sbt.Keys.name能够写成name。 

自定义键能够在.scala文件或插件中定义。 spa

改变settings的另外一个方式

:=是最简单的转换。还有其余方式。例如能够用+=向一个列表值添加内容。 插件

其余的转换须要scopes的知识,因此后面再介绍。 命令行

任务键

有三种风味的键:(红烧,清蒸,还有油炸的) scala

SettingKey[T]:有一个值,并且只计算一次(只在项目加载的时候计算一次,并一直存在)。 
TaskKey[T]:有一个值,而且每次都要从新计算,可能产生反作用。 

InputKey[T]:是一个有命令行参数做为输入的任务键。入门指南中未涵盖InputKey。

TaskKey[T]定义一个任务,像compile或package这样的操做就是任务。任务能够返回Unit(就是Scala中的void),或返回一个和任务相关联的值,如package是一个TaskKey[File],它的值就是他建立的jar文件。

每次开始执行一个任务(好比在交互式sbt提示符后输入compile),sbt都会从新运行一次涉及到的任务。

虽然sbt的映射描述一个项目时,能够为诸如name的setting保持一个固定的字符串,可是它必须为诸如compile这样的任务保持可执行的代码——即便可执行代码最终返回一个字符串,它每次都会从新运行。

一个给定的键必须引用一个任务或普通的setting。也就是说,"taskiness"(是否每次从新运行)是这个键的属性,而不是值。

用:=能够建立一个任务,任务的代码每次调用时都会执行:

hello := {println("Hello!")}

从类型系统的观点来看,task key建立的Setting与setting key建立的Setting稍有不一样,taskKey := 42返回一个Setting[Task[T]],而settingKey := 42返回一个Setting[T]。在大多数状况下这没多大差异,当任务执行的时候task key依然建立一个类型为T的值。(也就是说,当T是String,那么不管是调用task key,仍是setting key,返回的值都是String类型的)

T和Task[T]隐含的区别是:一个setting key不能依赖一个task key,由于setting key的值只在项目加载的时候评估一次,并且不能重复执行。

sbt交互模式下的Keys

在sbt交互模式下,能够输入任务的名称来执行这个任务。所以输入compile就会运行compile任务,compile是一个task key。

若是输入一个setting key的名称,就会显示setting key的值。输入task key的名称会执行任务但不会显示结果值;要看任务的结果,能够用show <task name>。

在构建定义文件中,keys以驼峰式命名(沿袭Scala惯例),但在sbt命令行中用 连字符-分隔-单词 代替。这些在sbt中使用的 连字符-分隔 字符串是在Keys中定义的。例如,在Keys.scala,定义了一个键:

val scalacOptions = TaskKey[Seq[String]]("scalac-options", "Options for the Scala compiler.")

在sbt中要输入scalac-options,但在构建定义中得用scalacOptions。

要了解更多的键,在sbt命令提示符后输入inspect <keyname>。inspect显示的有些信息没什么意义,可是在顶端展现了setting的值类型,以及它的简短描述。

在build.sbt中引入

你能够把引入语句放在build.sbt的头部;而且不须要用空行分隔。
下面这些是隐式默认导入的:
import sbt._
import Process._
import Keys._

(此外,若有你有.scala文件,它们的内容(Build或Plugin对象)将会被导入。)

添加类库依赖

有两种方式来添加第三方类库依赖。第一种是把jar包放到lib/(非托管依赖),第二种是添加托管依赖,放在build.sbt里,像这样:

libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3"
libraryDependencies涉及两个复杂状况:+=操做符,以及%方法。+=向原来的值中追加,而非替换它。%方法用来从字符串构造ivy模块ID。
相关文章
相关标签/搜索