我有以下示例build.sbt使用sbt-assembly.(我的assembly.sbt和project/assembly.sbt的设置如自述文件中所述.)
import AssemblyKeys._
organization := "com.example"
name := "hello-sbt"
version := "1.0"
scalaVersion := "2.10.3"
val hello = taskKey[Unit]("Prints hello")
hello := println(s"hello, ${assembly.value.getName}")
val hey = taskKey[Unit]("Prints hey")
hey <<= assembly map { (asm) => println(s"hey, ${asm.getName}") }
//val hi = taskKey[Unit]("Prints hi")
//hi <<= assembly { (asm) => println(s"hi, $asm") }
Run Code Online (Sandbox Code Playgroud)
无论hello和hey相同的功能,当我从SBT运行任何任务,他们跑assembly第一,具有相同文件名打印的消息.两者之间是否存在有意义的差异?(似乎定义hello是"有些神奇",因为对汇编的依赖只是隐含在那里,而不是明确的.)
最后,我试图理解为什么hey需要这个map电话.显然它会导致不同的对象被传入asm,但我不太确定如何在定义中修复此类型错误hi:
sbt-hello/build.sbt:21: error: type mismatch;
found : Unit
required: sbt.Task[Unit]
hi <<= assembly { (asm) => println(s"hi, $asm") }
^
[error] Type error in expression
Run Code Online (Sandbox Code Playgroud)
它看起来像是assembly一个,[sbt.TaskKey[java.io.File]][2]但我没有看到map那里定义的方法,所以我不能完全弄清楚hey上面的类型发生了什么.
Eug*_*ota 19
两者之间是否存在有意义的差异?
通过有意义的差异,如果您将语义差异视为编译代码行为的可观察差异,则它们是相同的.
如果你的意思是代码中的任何预期差异,那就是sbt 0.12语法sbt 0.13语法之间的风格差异.从概念上讲,我认为sbt 0.13语法使您更容易学习和编码,因为您处理T而不是Initialize[T]直接.使用宏,sbt 0.13扩展x.value为sbt 0.12当量.
我想知道为什么他们需要地图电话.
这实际上是差异宏之一,现在能够自动处理.要了解mapsbt 0.12样式中需要的原因,您需要了解sbt DSL表达式的类型,即Setting[_].正如入门指南所说:
相反,构建定义创建了一个庞大的与类型对象的列表
Setting[T],其中T是在地图中值的类型.ASetting描述了对地图的转换,例如添加新的键值对或附加到现有值.
对于任务,DSL表达式的类型是Setting[Task[T]].要将设置键转换为Setting[T]或将任务键转换为Setting[Task[T]],您可以使用<<=在相应键上定义的方法.这是在Structure.scala中实现的(sbt 0.12代码库具有更简单的实现,<<=因此我将使用它作为参考.):
sealed trait SettingKey[T] extends ScopedTaskable[T] with KeyedInitialize[T] with Scoped.ScopingSetting[SettingKey[T]] with Scoped.DefinableSetting[T] with Scoped.ListSetting[T, Id] { ... }
sealed trait TaskKey[T] extends ScopedTaskable[T] with KeyedInitialize[Task[T]] with Scoped.ScopingSetting[TaskKey[T]] with Scoped.ListSetting[T, Task] with Scoped.DefinableTask[T] { ... }
object Scoped {
sealed trait DefinableSetting[T] {
final def <<= (app: Initialize[T]): Setting[T] = setting(scopedKey, app)
...
}
sealed trait DefinableTask[T] { self: TaskKey[T] =>
def <<= (app: Initialize[Task[T]]): Setting[Task[T]] = Project.setting(scopedKey, app)
...
}
}
Run Code Online (Sandbox Code Playgroud)
注意app参数的类型.设置密钥<<=需要,Initialize[T]而任务密钥<<=需要Initialize[Task[T]].换句话说,根据<<=表达式的lhs 类型,rhs的类型会发生变化.这要求sbt 0.12用户了解密钥中的设置/任务差异.
假设您有一个类似于descriptionlhs 的设置键,并假设您想依赖于name设置和创建描述.要创建设置依赖关系表达式,请使用apply:
description <<= name { n => n + " is good." }
Run Code Online (Sandbox Code Playgroud)
apply对于单个键,在Settings.scala中实现:
sealed trait Keyed[S, T] extends Initialize[T]
{
def transform: S => T
final def apply[Z](g: T => Z): Initialize[Z] = new GetValue(scopedKey, g compose transform)
}
trait KeyedInitialize[T] extends Keyed[T, T] {
final val transform = idFun[T]
}
Run Code Online (Sandbox Code Playgroud)
接下来,description假设您要为其创建设置,而不是jarName in assembly.这是一个任务键,所以<<=需要rhs Initialize[Task[T]],所以apply并不好.这是map进来的地方:
jarName in assembly <<= name map { n => n + ".jar" }
Run Code Online (Sandbox Code Playgroud)
这也在Structure.scala中实现:
final class RichInitialize[S](init: Initialize[S]) {
def map[T](f: S => T): Initialize[Task[T]] = init(s => mktask(f(s)) )
}
Run Code Online (Sandbox Code Playgroud)
因为设置键延伸KeyedInitialize[T],这是Initialize[T],因为有一个隐式转换从Initialize[T]到RichInitialize[T]上述可供name.这是一种奇怪的定义方式,map因为地图通常会保留结构.
如果您看到类似的任务键浓缩类,则可能更有意义:
final class RichInitializeTask[S](i: Initialize[Task[S]]) extends RichInitTaskBase[S, Task] {...}
sealed abstract class RichInitTaskBase[S, R[_]] {
def map[T](f: S => T): Initialize[R[T]] = mapR(f compose successM)
}
Run Code Online (Sandbox Code Playgroud)
因此,对于任务,map从类型的任务S到的地图T.对于设置,我们可以将其视为:map未在设置上定义,因此它隐式地将自身转换为任务并映射它.在任何情况下,这让0.12用户认为:apply用于设置,map用于任务.请注意,apply随着任务键的扩展,它们就会消失Keyed[Task[T], Task[T]].这应该解释:
sbt-hello/build.sbt:21: error: type mismatch;
found : Unit
required: sbt.Task[Unit]
Run Code Online (Sandbox Code Playgroud)
那就是元组问题.到目前为止,我已经讨论了对单个设置的依赖性.如果要依赖于更多的,SBT隐式添加apply和map以Tuple2..N处理它.现在它已扩展到15,但它过去只是为了Tuple9.来自新用户的角度来看,调用的想法,看见map上Tuple9的设置,以便它生成一个任务一样Initialize[Task[T]]会出现外星人.在不改变底层机制的情况下,sbt 0.13提供了更加清洁的表面来开始.