sbt笔记四 .sbt构建定义

.sbt vs .scala

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

这里只讨论.sbt。

所谓构建定义 

** 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是值得类型。

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文件是可选的。

键在Keys对象中定义

内置的键都是Keys对象的字段。build.sbt隐式引入了sbt.Keys._。所以sbt.Keys.name可以写成name。 

自定义键可以在.scala文件或插件中定义。

改变settings的另一个方式

:=是最简单的转换。还有其他方式。例如可以用+=向一个列表值添加内容。

其他的转换需要scopes的知识,所以后面再介绍。

任务键

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

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。

转载于:https://my.oschina.net/u/580483/blog/104853

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值