Scope axes(范围轴)
一个返回轴是一种类型,每一个类型实例可以定义它自己的范围
(这就是每个实例可以拥有它自己唯一的key对于的值)
这里有三个范围轴:
1. 项目
2. 设置
3. 任务
Scopingby project axis(项目轴)
如果你把多个项目放到一个单一的构造里,每一个项目需要有自己设置。
这样,key依据项目被赋值
项目轴 可以 设置到整个构造。所以一个设置应用到一个整个构造,而不是单一个项目。构造级别 设置常常使用一个返回当一个项目没有正确定义
Scopingby configurationaxis(配置轴)
一个设置定义一个喜欢构造,可能与它拥有的类路径,源文件,生成包等等。设置内容来自ivy,这个是sbt用于管理依赖库和从maven的范围
一些设置你可以在sbt看到
1.编译默认路径 (src/main/scala).
2.测试哪里生成测试 (src/test/scala).
3.运行定义的类路径执行任务
默认情况下,所有于编译,打包和运行相关联关键字是配置范围并且 这样可以在不同配置工作。大多数情况发生例子是任务keys,compile,package 和 run。但是所有的key 提供这些key一般都是在配置范围
Scopingby task axis(任务轴)
设置可以影响一个任务怎样执行。譬如,packageSrc 任务由 packageOptions影响
为了支持这一观点,一个任务key(packageSrc)可以是在另一个key(packageoptions)的返回里
构建一个包的各种任务可以共享key 联系到packageing,譬如artifactNameand packageOptions
这样关键字可以在每个打包任务中有不同的值
Globalscope(全局)
每一个范围轴可以被一个轴的类型实例填充,或者一个轴可以被一个全局值填充
全局意味你所期待的。设置值都应用到那一个轴上的实例。譬如如果任务轴是全局,这样设置都会应用到所有的任务
Delegation(委托)
一个范围key可能没有定义,如果他没有值依赖它的范围。对于每个范围来说,sbt有一个倒退搜索路径由其他范围设置。代表性来说,如果一个key没有依赖在具体范围的值,sbt可以查是获取一个值从一个更加普通的范围,例如全局范围或者是整个构建范围
这个特性允许你去设置一个值一次在一个跟普通的范围,语序多个指定的范围去继承这个值
你可以看到倒退搜索路径或者 delegates” 使用 inspect 命令 查看描述
Referring to scoped keys when running sbt
当运行sbt时候关联到范围key
在命令行和在交互模式下,sbt显示范围可以如下
{<build-uri>}<project-id>/config:intask::key
• {<build-uri>}/<project-id>确定项目id. The
<project-id> part will be missing if theproject axis has “entire
build” scope.
• config identifiesthe configuration axis. 配置轴
• intask identifiesthe task axis. 任务轴
• key identifiesthe key being scoped. Key识别key的范围
* can appear for each axis, referring to the Global scope.
If you omit part of the scoped key, it will be inferredas follows:
• the current project will be used if you omit the project.
• a key-dependent configuration will be auto-detected if you omit theconfiguration
or task.
更多细节,请查看配置系统的交互
Examples of scoped key notation
范围key符号例子
• fullClasspath指定一个key,所以默认范围使用:当前项目一个key-dependent设置和全局范围
• test:fullClasspath指定一个配置,所以在测试设置中
• *:fullClasspath指定全局为了设置而不是默认配置
• doc::fullClasspath作为doc 任务范围,默认是项目和设置轴
• {file:/home/hp/checkout/hello/}default-aea33a/test:fullClasspath
指定一个项目, {file:/home/hp/checkout/hello/}default-aea33a,
项目指定在 {file:/home/hp/checkout/hello/}
并且项目id是 default-aea33a. 也指定了任务test, 而不是默认的任务轴
• {file:/home/hp/checkout/hello/}/test:fullClasspath
{file:/home/hp/checkout/hello/}.
• {.}/test:fullClasspath
• {file:/home/hp/checkout/hello/}/compile:doc::fullClasspath
setsall three scope axes.
Inspecting scopes(检查范围)
在sbt交互模式下,你可以使用检查命令去理解key和他们的范围
$ sbt
> inspect test:fullClasspath
[info] Task: scala.collection.Seq[sbt.Attributed[java.io.File]]--在第一行你看到这个是任务的。并且返回值是一个sbt.Attributed[java.io.File]
[info] Description:
[info] The exported classpath, consisting of buildproducts and unmanaged and managed, internal [info] Provided by:
[info]{file:/home/hp/checkout/hello/}default-aea33a/test:fullClasspath---指出你这个范围key定义的值。
[info] Dependencies: -------没有意义?下一节你就知道
[info] test:exportedProducts
[info] test:dependencyClasspath
[info] Reverse dependencies:
[info] test:runMain
[info] test:run
[info] test:testLoader
[info] test:console
[info] Delegates:
[info] test:fullClasspath
[info] runtime:fullClasspath
[info] compile:fullClasspath
[info] *:fullClasspath
[info] {.}/test:fullClasspath
[info] {.}/runtime:fullClasspath
[info] {.}/compile:fullClasspath
[info] {.}/*:fullClasspath
[info] */test:fullClasspath
[info] */runtime:fullClasspath
[info] */compile:fullClasspath
[info] */*:fullClasspath
[info] Related:
[info] compile:fullClasspath
[info] compile:fullClasspath(for doc)
[info] test:fullClasspath(for doc)
[info]runtime:fullClasspath
尝试执行inspectfullClasspath 获取一些不同,因为设置是省略的。它可以自动的编译成compile。inspect compile:fullClasspath 和inspect fullClasspath是一样的信息
尝试inspect *:fullClasspath ,由于fullClasspath没有定义在全局所以里面没有设置
更多细节参考配置系统
Referring to scopes in a build definition(在构建定义指定范围)
如果你创建一个设置在build.sbt单纯使用key,它的范围就是当前项目,配置全局和任务全局
lazy val root = (projectin file(".")).
settings(
name := "hello"
)
运行sbt 并且 检查 名称可以看到它由{file:/home/hp/checkout/hello/}default-aea33a/*提供,那他项目就是{file:/home/hp/checkout/hello/}default-aea33a,设置×,并且任务没有展示。
关键字有一个覆盖的方法叫做in 使用设置范围。参数就是任何范围轴的实例。所以举个例子,虽然没有正式的理由做这个,你可以设置name的返回到compile配置里:
name in Compile := "hello"
或者你可以设置名字的范围到packageBin task(这个是没有意义,只是例子)
name in packageBin := "hello"
或者直接添加多个范围进去
name in (Compile, packageBin) := "hello"
或者直接在全局添加
name in Global := "hello"
如果你不是使用scala,请记住:这是很重要的理解 in 和 := 仅仅是一个方法而不是魔术。Scala使你用一种好看的方式写它们 但是在java格式中你需要
name.in(Compile).:=("hello")
虽然没有理由使用这种丑陋的语法,但是他说明了它们实际上是方法
When to specify a scope(当指定了一个范围)
如果key在正常的范围里面有疑问,你就需要指定一个范围。例如,在compile任务中,默认是compile和test配置,并且不存在之外的范围
为了改变于compile关键字相关的值,你需要写compile in compile 或者compile in test。使用纯粹拍内衣一个新的任务在当前的项目上,而不是覆盖基础在配置范围订好的编译任务
如果你获得一个“Referenceto undefined setting”,错误,通常是你没有定义一个指定的范围,或者你定义了一个错误的范围。这个关键字你需要使用可能定义在其他范围里面。Sbt可以尝试建议你指明的错误,Did you meancompile:compile?
一种方式想象它作为key的一部分。在实际中,所有key由name,scop组成。
More kinds of setting(更多类型设置)
这一节通过其他途径解析创建一个setting,超于基本的 := 方法。这假设你已经阅读了.sbt构造定义和范围
Refresher: Settings(复习:Settings)
回想一下,一个构造定义创建了一个Setting列表,用于转换成sbt 构造描述(就是一个map啦)。一个Setting 就是作为sbt早期map转换的输入和 作为一个新的map的输出。新的map成为sbt的新的状态
不同的Settings 使用不同方式转换Map。早先,你阅读了关于:=方法
Setting 使用:= 创建 需要输入一个固定的,不变的值作为新值,然后转换map。例如如果你转换一个map使用setting 的name:=“hello”。这个新的map就有一个字符串“hello”作为key =name的值
Appending to previous values: +=and ++=
对上一个值进行附加 += 和 ++=
通过:=进行赋值是最简单的转换,但是keys也有其他方法不只这一个。如果 Settingkey[T]里面的T是一个列表,譬如,key的值类型就是队列,你就可以追加到队列而不是替换它。
• += 将会添加一个单一的元素到队列
• ++= 将会把另一个队列到添加到队列
例如,关键字SourceDirectories in Compile 拥有一个 seq【File】作为它的值。默认这个key值包含src/main/scala.,如果你想编译一个叫source的目录的源代码,你可以添加路径进去
sourceDirectories in Compile += new File("source")
或者,为了方便可以直接使用sbt包里面的file()方法
sourceDirectories in Compile += new File("source")
你也可以使用 ++= 同时添加多于一个目录
sourceDirectories in Compile ++= Seq(file("sources1"), file("sources2"))
这里Seq(a,b,c)使用的是scala的基本语法构建一个队列
如果代替原来的默认元路径,你当然可以使用 :=
sourceDirectories in Compile := Seq(file("sources1"), file("sources2"))
Computing a value based on other keys’ values
基于其他key值计算一个值
引用其他任务的值或者设置通过调用一个task或者setting的key的对应的值。这个值方法是等价的并且可以被这些:=,+=,++= 作为参数
作为第一个例子,考虑定义项目的组织和项目的名称一样
organization := name.value
或者设置name的值为项目路径的值
name := baseDirectory.value.getName
这个转换的值就是基本目录的值基于java.io.File类型的getName的方法
使用多个输入,例如
name := "project " + name.value + " from " + organization.value + " version " + version.value
这个设置名称使用了一组值和之前设置组织和版本设置值一样
Settings with dependencies(依赖设置)
在设置 name:=baseDirectory.value.getName, name将有一个对baseDirectory依赖。如果你在build.sbt替换和执行sbt的交互模式,并且输入 inspect name 你可以看到
[info] Dependencies:
[info]*:baseDirectory
这个就是怎样使sbt知道那个一个settings依赖其他settings。回想一下一些setting描述tasks,所以这种做法在任务之间也创建依赖
例如,如果你 inspect compile 你可以看到他的依赖于其他key compileInputs,并且如果你 inspect compileInputs 它也是依赖其他keys。一直保持追踪依赖链这样魔术发生了。当你输入compile sbt自动执行一个update,例如 它可以工作是由于需要作为输入到compile 计算需要sbt首先去做updae计算
这种方式下,所有在sbt的构造依赖都是自动的而不是显示定义。如果你使用了一个key的值在另一个计算,这个计算就会依赖这个key值。它可行的
When settings are undefined(当设置是未定义)
不管一个setting使用:=, +=, or ++= 去创建一个依赖基于它自己或者其他key值,这个值的依赖必须是存在的。如果它不存在,sbt就会抱怨~~。它就会说“Referenceto undefined setting”,
例如,当这个发生,确认你在范围里面使用的key值是有定义的
可能在创建周期的时候,这样是一个错误。Sbt会告诉你怎样做的
Tasks based on other keys’ values(任务基于其他key值)
你可以通过一些人任务或者设置计算一个值去定义或者应用到另外一个任务里。通过使用Def.task和taskValue搞定。作为参数使用到 :=, += or ++=
作为一个开始例子,考虑添加一个source通过是使用项目的基本目录和类路径
sourceGenerators in Compile += Def.task {
myGenerator(baseDirectory.value, (managedClasspath in Compile).value)
}.taskValue
Tasks with dependencies(任务依赖)
在.sbt构造定义,任务关键字创建一个Setting[Task[T]]而不是Setting[T],当你构造一个设置使用:=,等等。任务可以使用settings作为输入,但是settings不能使用task作为输入
任务两个keys:
val scalacOptions =taskKey[Seq[String]]("Options for the Scala compiler.")
val checksums = settingKey[Seq[String]]("The list of checksums to generate andto verify for
scalacOptions和checksums并没有相互影响,他们只是定义了两key相同值类型,其中一个是任务
有可能去编译一个build.sbt使得scalacOptions是checksums,的一个别名。没有其他路径。例如,下面是合法的
scalacOptions := checksums.value
这里没有其他方法去定义。就这样,一个setting key不能依赖于一个task key。这是因为一个setting key 只在项目加载中计算一次,所以任务不可能每次重复执行,并且任务是系统每次重新执行一次
checksums := scalacOptions.value ---这样不行的
Appending with dependencies: +=and ++=(在依赖使用+=/++=)
其他key值可以使用当应用到一个存在的setting或者task,就像他们使用:=作为参数
例如,你有一个覆盖报表以项目命名,并且你想在clean的时候把这个文件移除
cleanFiles += file("coverage-report-" + name.value + ".txt")
Library dependencies(库依赖)
这个节假设你以及阅读早先的入门指南,特别是.sbt构造定义,范围和多种类型设置
库依赖可以通过两种方式添加:
• unmanaged 把依赖jar放入lib文件夹里面
• managed 通过在构造定义里配置依赖并且自动下载到本地仓库
Unmanaged dependencies(没有管理的依赖库)
大多数人使用管理的依赖代替没有管理的依赖。但是没有管理的依赖可能在开始的时候会比较简单
没有管理的依赖工作如下:添加jar到lib目录,然后它们就会放入到项目的类路径。只有这么多!
你可以放入 测试jar例如 scalacheck,specs2 和 scalatest放入lib
在lib依赖去所有的类路径(包括:compile,test,run和console)。如果你想改变类路径对于它们其中一个你可以调整譬如dependencyClasspathin Compile or dependencyClasspathin
Runtime
这里没有东西需要添加到build.sbt去使用没有管理依赖,虽然你可以改变unmanagedBase 如果你想用其他目录而不是lib
使用custom_lib代替lib
unmanagedBase := baseDirectory.value / "custom_lib"
基本目录是项目的根路径,这里你可以改变unmanagedBase 依据 baseDirectory 使用特定的value值这个已经在前面有说
这里也有一个unmanagedJars 任务使jar从unmanagedBase加载。如果你想使用多个目录 或者做一些事情综合起来,你可能需要替换整个unmanagedJars 任务,而用一个其他做事情代替。例如一个空的compile 设置列表不管lib目录的文件。
unmanagedJars in Compile := Seq.empty[sbt.Attributed[java.io.File]]
Managed Dependencies(管理库)
Sbt使用Apache Ivy 去实现管理依赖,所以如果你曾经使用过ivy或者maven,你不会有太多的麻烦
The libraryDependencies key(库依赖关键字)
大多数时间,你可能简单列出你的依赖在设置libraryDependencies.。也可以写一个maven的pom文件或者ivy的配置文件去额外管理你的依赖,并且sbt有使用这些额外的配置文件,你可以学习更多的关于这些
定义一个依赖看起来想这样,groupId, artifactId, and revision都是字符串
libraryDependencies += groupID % artifactID % revision
或者像下面,configuration可以是字符串或者Configuration定义的
libraryDependencies += groupID % artifactID % revision %configuration
libraryDependencies定义关键字类型如下:
val libraryDependencies= settingKey[Seq[ModuleID]]("Declares managed dependencies.")
使用% 方法从字符串创建 ModelID 对象,然后你添加这些ModelID 到libraryDependencies
当然,sbt(通过lvy)知道去哪里下载这些模型。如果你的模型是在一个默认sbt自带的资源库其中一个,这是可行的。譬如,Apache Derby 是在基础的Maven2的资源库里:
libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3"
如果在build.sbt你输入这个然后更新,sbt就会下载Derby 到~/.ivy2/cache/org.apache.derby/.目录下(顺带一下,更新是依赖compile,所以你大多数情况下不用手工输入update)
当然,你可以使用 ++=加入依赖列表一次:
libraryDependencies ++= Seq(
groupID % artifactID % revision,
groupID % otherID % otherRevision
)
在极少数的情况下你可能找到理由去对使用libraryDependencies := 也是没问题的
Getting the right Scala version with %%(获取正确的scala版本使用%%)
如果你使用groupID%% artifactID % revision 而不是groupID %
artifactID % revision,sbt会添加你项目的scala版本信息到artifact名称里,这只是一个快捷方式。你也可以不用%%
libraryDependencies += "org.scala-tools" % "scala-stm_2.11.1" % "0.3"
假设你的构造的scala版本是2.11.1,下面的也是一致的
libraryDependencies += "org.scala-tools" %% "scala-stm" % "0.3"
这个想法是很多依赖编译成不同的scala版本,并且你想获取一个匹配你项目确保兼容
在实践复杂性常常是依赖使用一个稍微不同的scala版本才能工作。但是%%不是很聪明。所以如果依赖是对于2.10,1有用,但是你版本是scalaVersion:= "2.10.4",你就不能使用%% ,虽然2.101依赖看起来是可行的。如果%%停止工作了,你需要查看那一个版本的依赖可行,并且硬编码那个你希望的可以工作。
Ivy revisions(lvy 修正版)
在groupID% artifactID % revision 中的revision可以不是一个固定的版本。Lvy 可以查询改modele最新版本根据你指定的约束。代替一个固定版本如“1.6.1”,你可以指明
"latest.integration", "2.9.+", or "[1.0,)". 更详细的内容请查看lvy的文档
Resolvers(解析器)
并不是所有的包都在相同的服务上执行。Sbt 默认使用基础maven2 资源。如果你的依赖并不在默认的资源里面,你就不得不添加额外的资源帮助lvy找到它
添加额外的资源使用:
resolvers += name at location
通过特别在at 之间的两个字符串
例如
resolvers += "SonatypeOSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"
这个resolvers的关键字设置如下:
val resolvers =settingKey[Seq[Resolver]]("The user-defined additional resolversfor automatically
在at方法从两个字符串创建一个解析器对象
Sbt 可以查询的本地的maven库如果你添加它作为一个资源
resolvers += "LocalMaven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
或者下面等价的
resolvers += Resolver.mavenLocal
Overriding default resolvers(覆盖默认的解析器)
解析器并没有包含一个默认的解析器。只是额外通过你添加到构造定义里面。
Sbt 组合resolvers和默认的资源从externalResolvers.。这样改变和移除默认解析器,你可以需要覆盖externalResolvers代替 resolvers.
Per-configuration dependencies(配置依赖性)
常常在你的测试代码中使用一个依赖,而不是在你主要代码中。
如果你想一个依赖只为testconfig出现在类路径。而不是在compile配置,可以像这样 %test添加
libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3" % "test"
你也可以使用安全版本Test配置如下:
libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3" % Test
现在,如果你输入show compile:dependencyClasspath在sbt交互模式下,你不会看到derby jiar。但是如果你输入show test:dependencyClasspath,你就看到derby在列表中。
代表性的,一些测试的引用如ScalaCheck, Specs2, and ScalaTest 都应该添加 %"test".