【问题标题】:Why does test-scoped setting not hold correct value?为什么测试范围的设置不保持正确的值?
【发布时间】: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 运行时都会自动调用此命令。

但这对我来说似乎太脏了,所以如果有更优雅的方式来实现类似的语义,我很好奇。

【问题讨论】:

    标签: scala sbt


    【解决方案1】:

    “不正确” 值的原因是targetExample 依赖于sourceExample 在Compile 范围内,如:

    targetExample := "extended" +: sourceExample.value
    

    如果它使用来自Test 范围的sourceExample 值,请使用in Test 明确表达您的愿望,如下所示:

    targetExample := "extended" +: (sourceExample in Test).value
    

    使用inspect了解依赖链。

    顺便说一句,我强烈建议使用build.sbt 文件来进行如此简单的构建定义。

    【讨论】:

      【解决方案2】:

      您可以使用默认设置,并在 Plugins Best Practices - Playing nice with configurations 中描述的不同配置中重复使用它。我相信,这应该会给你一个类似于你正在寻找的语义。

      您可以定义基本设置并在不同的配置中重复使用。

      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 ++ 
          inConfig(Compile)(basePluginSettings) ++
          inConfig(Test)(basePluginSettings ++ Seq(
            sourceExample += "testing" // we are already "in Test" here
          ))
      
        lazy val basePluginSettings: Seq[Setting[_]] = Seq (
          sourceExample := Seq("base"),
          targetExample := "extended" +: sourceExample.value
        )
      
      } 
      

      PS。由于您正在谈论编写插件,您可能还想看看编写 sbt 插件的新方法,即AutoPlugin,因为旧机制现在是deprecated。

      【讨论】:

      • 如果设置多了怎么办? libraryDependencies 可以用作示例。它没有为每个任务定义,因为它太麻烦并且实现自定义推理机制。 % test 依赖定义的一部分。
      • @ayvango 我不确定我是否理解正确,但您可以添加不受范围限制的设置,例如作为另一个lazy val,并将其添加到settings 而不通过@987654329 进行范围限制@ 方法。但是,如果您在 basePluginSettings 中定义它,我想它也应该可以工作,您只是不要在配置中覆盖它
      猜你喜欢
      • 1970-01-01
      • 2016-08-24
      • 2015-10-11
      • 2012-11-21
      • 2018-12-21
      • 2018-05-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多