【发布时间】:2014-06-06 13:36:31
【问题描述】:
范围在 sbt 中很重要。我完全同意。但也有委托规则允许您构建设置的层次结构。我想用它来为更具体的规则带来额外的设置。
import sbt._
import Keys._
object TestBuild extends Build {
val sourceExample = settingKey[Seq[String]]("example source for setting dependency")
val targetExample = settingKey[Seq[String]]("example of a dependent setting")
override lazy val settings = super.settings ++ Seq (
sourceExample := Seq("base"),
targetExample := "extended" +: sourceExample.value,
sourceExample in Test += "testing"
)
}
这个例子给了我意想不到的输出:
> show compile:sourceExample
[info] List(base)
> show test:sourceExample
[info] List(base, testing)
> show compile:targetExample
[info] List(extended, base)
> show test:targetExample
[info] List(extended, base)
我希望 test:targetExample 是 List(extended, base, testing) 而不是 List(extended, base)。一旦我得到结果,我会立即弄清楚为什么它的工作原理如图所示。 test:targetExample 代表来自 *:targetExample 的计算值,但不是在嵌套范围内计算它的规则。
这种行为给我编写自己的插件带来了两个困难。作为插件开发人员,我有额外的工作来在每个范围内定义相同的规则。而且我必须记住内部任务的范围定义才能正确地作为用户使用。
我该如何克服这种不便?我想引入 call-by-name 语义中的设置,而不是 call-by-value。有什么技巧可以奏效?
附: libraryDependencies in Test 看起来比使用 % test 更简洁。
我应该明确表示,我完全理解 sbt 派生的值就像文档中描述的那样。它按照创建者的预期工作。
但是我为什么要遵守规则呢?我认为它们完全违反直觉。 Sbt 引入了继承语义,其实际工作方式与过去定义的继承方式不同。当你写
trait A { lazy val x : Int = 5 }
trait B extends A { lazy val y : Int = x * 2}
trait C extends A { override lazy val x : Int = 3 }
您希望(new B with C).y 是 6,而不是 10。知道它实际上是 10 可以让您正确使用这种继承,但会让您渴望找到更传统的实现继承的方法。您甚至可以根据name->value 字典编写自己的实现。你可以根据编程的第十条规则继续前进。
所以我正在寻找一种可以带来与常见语义一致的继承语义的技巧。作为一个起点,我可能会建议写command 来扫描所有设置并将它们从父母明确推送给孩子。并且每次 sbt 运行时都会自动调用此命令。
但这对我来说似乎太脏了,所以如果有更优雅的方式来实现类似的语义,我很好奇。
【问题讨论】: