.sbt构造定义
这个页面描述sbt构造定义,包含一些“理论”和build.sbt的语法。假设你已经知道怎样使用sbt,并且已经阅读了上一节内容
三种构造定义风格
这里有三个构造定义风格
1. 多项目 .sbt 构造定义
2. 空 .sbt 构造定义
3. .scala 构造定义
这一页只讨论最新的多项目 .sbt构造定义,他组合其他两个旧定义的强度,并且适用于所有的场景。你可以来自其他旧风格处理构建构造。看空的 .sbt定义和.scala 构造定义(之后的入门指南)还有其他更多的风格
此外,一个构造定义可以包含文件如 .scala,处在project/ 基本目录下的子目录定义常用的函数和值
什么是一个构造定义
在检查一组目录和处理构造定义文件之后,sbt就会结束项目的定义(Afterexamining a set of directories and processing build definition files, sbt ends upwith Project definitions.)
在build.sbt你可以创建一个项目定义,项目位于当前路径下如下
lazy val root = (projectin file("."))
每一个项目都关联着一个不可变的map,描述一个项目。
例如,一个key 叫做name 他对应一个字符串值,你的项目名称
Builddefinition files do not affect sbt’s map directly
构造定义文件并不立即影响sbt的map
取而代之,一个构造定义创建一个巨大对象列表,类型是Setting[T],这里的T是map值的类型.一个设置描述了一个转换到map,譬如增加了一个新的key-value对 或者 附加到一个已经存在的值。(在函数式编程的精神中使用不可变数据结构和值一个转换返回一个新的map-它不会更新替换原来map里面的值)
这里你怎样关联到Setting[String]为了处于当前路径项目的名字
lazy val root = (projectin file(".")).
settings(
name := "hello"
)
这个Setting[String]通过添加了一个新的namekey改变map,给定它的值是“hello”。这个转变map变成sbt 的一个新的map
为了创建map,sbt首先排序设置list,把所有相同key改变放到一起,然后 值取决于其他key在他们依赖的key处理。这样 sbt 走完 这个顺序settings列表后 应用每一个到map,这个map返回
总结:一个构建定义就是定义项目的一个Setting[String]列表,这个列表会转换到sbt一个map,这里T就是每一个值的类型
怎样在build.sbt定义设置
Build.sbt 定义了一个项目,它保持一个名叫settings的scala表达式的列表
这里有一个例子
lazy val commonSettings =Seq(
organization := "com.example",
version := "0.1.0",
scalaVersion := "2.11.8"
)
lazy val root = (projectin file(".")).
settings(commonSettings:_*).
settings(
name := "hello"
)
每一个Setting 定义一个scala的表达式。这个表达式在settings 是和相互独立的,并且他们是表达式并不是完整的scala指令
Build.sbt 可能被 vals, lazy vals, and defs 点缀。顶层的对象和类定义在build.sbt是不允许.这些应该放到project/目录下作为一个完整的scala源文件
在左边,name, version, and scalaVersion都是key值,一个key是一个SettingKey[T], TaskKey[T], or InputKey[T]的实例,这里【T】是解析值的类型。Key的种类在下面解析
Keys有一个方法叫 := 返回一个Setting[T],你可以使用一个 类似java语法去调用方法:
lazy val root = (projectin file(".")).
settings(
name.:=("hello")
)
但是scala 允许用 name:=“hellow”代替
这个:=方法在key name 返回一个Setting,特别一个Setting[String]。String 也应用到name 自身的一个类型,也是SettingKey[String].在这个例子中,返回Setting[String]被转换一个增加了或者修改了sbt的map,给它的值是“hello”
如果你错误的使用值类型,构造定义不会成功编译:
lazy val root = (projectin file(".")).
settings(
name := 42 // will not compile
)
Keys
Types
有三个类型的key
1. SettingKey[T]:一个key对应一个value 只计算一次(value计算当加载project)
2. TaskKey[T],一个keyi对应一个value,调用一个任务,就会每次重新计算,有副作用(函数式)
3. InputKey[T]:一个key对一个任务需要输入的参数,更对查看Tasks
Built-in Keys
内置key就是keys的一个对象。一个build.sbt隐含加载了sbt.key._所以sbt.Keys.name,可以关联到name
Custom Keys
自定义key
自定义key 可能被定义通过他们各自创建方法:settingKey,taskKey,和inputKey。每个方法指明一个值的类型通过相关的key以及描述。Key的名字被被配置val值。譬如,定义一个新的任务key名字叫做hello
lazy val hello =taskKey[Unit]("An example task")
这里我们有使用事实就是在一个 .sbt文件里面可以包含vals 和defs 除了设置。所有这样定义在不管定义在文件哪里都会被评估。Vals 和defs 必须被分离开settings通过空行
注意:通常情况下,lazy vals 一般都是代替 vals 使用从而避免初始顺序造成的问题
Task vs Setting keys
一个TaskKey[T]是对于一个任务来说的。任务的操作例如compile或者package。他们会返回Unit(就是sacla说的没有返回void),或者他们可能返回一个值关联到任务。譬如package 是一个TaskKey[File]并且这个值就是jar文件创建
每一次你开始一个任务执行,例如通过输入compile在sbt的控制台里面,sbt会再一次重新执行任务任务
Sbt 的map 描述了项目可以保持一个固定的字符串值对于一个setting例如name。但是它不得不保持一些可以执行任务代码例如compile-甚至如果最终可执行代码返回一个字符串,它每次都返回
A given key always refers to either a task or a plainsetting.
一个给定的key总是关联到每一次任务或者一次计划设置
Defining tasks and settings
定义任务和设置
使用 := ,你可以分配一个值到一个setting 和 一个 计算给一个任务。对于设置,值只被计算一次在项目加载的时候。对应任务,这个计算可以被每次任务执行重复执行
例如,实现一个上一节定义的hello 任务
lazy val hello =taskKey[Unit]("An example task")
lazy val root = (projectin file(".")).
settings(
hello := { println("Hello!") }
)
Types for tasks and settings
从一个类型系统角度来说,从任务可以创建的Setting略有不同于从设置创建的setting。taskKey:=42 结果返回一个Setting[Task[T]],settingKey:=42 返回Setting[T]。为了大多数目的这个没有不同。任务key仍然创建一个T类型的值当任务执行的时候
T vs. Task[T] 不同是他们实现:一个 setting不能依赖于任务,因为一个setting 只会在项目加载的时候只执行一次 并且它不能被重算。更多类型的setting,即将到来
Keys in sbt interactive mode(在交互模式下的key使用)
在sbt的交互模式下,你可以输入任何任务的名称去执行那个任务。这就是为什么输入 compile执行 compile任务。Compile就是一个任务key。
如果你输入一个settingkey名称而不是一个任务名称,这个settingkey的值就会被显示。输入一个任务key名称执行任务,但是不会显示结果值。想看任务结果,需要使用 show<taskname> 而不是普通 <task name> . 这个key 名字的约定是使用 骆驼拼写法,所以这样的话命令行的名称和scala标识号是一样的。
为了学习跟多关于任务key,type,在sbt 交互模式下输入inspect <keyname>。一些信息 inspect 显示出来并不有意义,但是在顶端显示你 设置的值的类型和一个对于设置的有用的描述
Imports in build.sbt(在build.sbt导入)
你可以在build.sbt的顶部位置添加import指令。他们不需要空行隔开
他们的一些隐含的默认导入如下:
import sbt._
import Process._
import Keys._
Adding library dependencies(加入依赖lib)
为了依赖第三方lib,这里有两个选项:第一个是直接把需要jars放入到lib目录下。另一个是加入到依赖管理,如下定义到build.sbt:
val derby = "org.apache.derby" % "derby" % "10.4.1.3"
lazy val commonSettings =Seq(
organization := "com.example",
version := "0.1.0",
scalaVersion := "2.11.8"
)
lazy val root = (projectin file(".")).
settings(commonSettings:_*).
settings(
name := "hello",
libraryDependencies+= derby
)
这里就是怎样加入一个管理的依赖基于Apache Derby library, version
10.4.1.3.
这个libraryDependencieskey 涉及两个复杂: += 比 := 更好用 和 %函数。 += 应用在追加值而不是代替原来的值,这就是解析大多数类型的setting。 %函数使用ivy模式 字符串结构,描述依赖的
我们暂时跳过依赖的一些细节在入门指南里面。这里有一整个章节描述它