SBT:将输入预先应用于inputKeys [英] SBT: pre-applying input to inputKeys

查看:122
本文介绍了SBT:将输入预先应用于inputKeys的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在SBT中:我想定义一个inputKey,它读入命令行参数,稍微改变它们,使用结果作为其他inputKeys的输入。

In SBT: I would like to define an inputKey that reads in command line arguments, changes them slightly and uses the result as input to other inputKeys.

我试过:

lazy val demo = inputKey[Unit]("A demo input task.")
lazy val root = (project in file(".")).settings(
  libraryDependencies += jUnitInterface
).settings(
  demo := {
    val args: Seq[String] = spaceDelimited("<arg>").parsed
    val one = (run in Compile).fullInput(args(0) + "foo").evaluated
  }
)

但我得到错误:非法动态参考:args

我也试过:

demo := {
  val args: Seq[String] = spaceDelimited("<arg>").parsed
  System.setProperty("args0", args(0))
  val one = (run in Compile).fullInput(System.getProperty(args0) + "foo").evaluated
}

这根本不是提供输入。我怀疑这是执行顺序的问题(属性在我想要的时候没有设置,因为JVM可以自由移动线路)。

This simply does not provide the input. I suspect it is a matter of execution order (property does not get set when I want it, since JVM is free to move lines around).

所以,在我的绝望中,我甚至尝试了恶劣的:

So, in my desperation, I even tried the atrocious:

demo := {
  val args: Seq[String] = spaceDelimited("<arg>").parsed
  try {
    System.setProperty("args0", args(0))
  } finally {
    val one = (run in Compile).fullInput(System.getProperty(args0) + "foo").evaluated
  }
}

强制执行订单。这只会引发一个NullPointerException。

to force the order. This just throws a NullPointerException.

推荐答案

正如Daniel C. Sobral所说,已解析已计算是宏,在 InputWrapper 中定义。

As Daniel C. Sobral mentioned, parsed and evaluated are macros, defined in InputWrapper.

因为它们是在编译时执行的,并且在运行时检索参数,所以它们不能很好地混合。特别是,args的值仅在运行时真正定义,并且无法通过已计算的宏检索。

Since they are executed at compile time, and the arguments are retrieved at runtime, they do not mix well. In particular, the value of args is only really defined at runtime and cannot be retrieved by the evaluated macro.

编辑:在与OP聊天后,我确定他的目标是写作的快捷方式 myTask Foo bar 而不是 testOnly * Foo * - --tests = * bar * ,我已相应地更新了我的答案。

如上所述,因为你基本上想要一个宏来写 myTask Foo bar 而不是 testOnly * Foo * - --tests = * bar * ,这是我的解决方案:

As discussed, since you basically want a "macro" for writing myTask Foo bar instead of testOnly *Foo* -- --tests=*bar*, here's my solution:

val filtersParser = {
    import complete.DefaultParsers._
    (token(Space) ~> token(StringBasic, "<classFilter>")) ~
        (token(Space) ~> token(StringBasic, "<methodFilter>"))
}

lazy val testFiltered = inputKey[Unit]("runs test methods matching *<methodFilter>* within classes matching *<classFilter>*")

testFiltered.in(Test) := Def.inputTaskDyn {
    val (classFilter, methodFilter) = filtersParser.parsed
    runTestsFiltered(classFilter, methodFilter)
}.evaluated

def runTestsFiltered(classFilter: String, methodFilter: String) = Def.taskDyn {
    (testOnly in Test).toTask(s" *$classFilter* -- --tests *$methodFilter*")
}



更多细节



您需要一个自定义解析器来检索您期望的两个参数。这是通过以下代码实现的,它基本上定义了两个组,chomping两个空格而不记住它们,以及两个 StringBasic 参数,它们是解析器的结果( filtersParser 的类型为 Parser [(String,String)]

In more detail

You need a custom parser to retrieve the two arguments you're expecting. This is achieved with the following code, which basically defines two groups, "chomping" both spaces without remembering them, and two StringBasic arguments, which are the result of the parser (filtersParser is of type Parser[(String, String)])

val filtersParser = {
    import complete.DefaultParsers._
    (token(Space) ~> token(StringBasic, "<classFilter>")) ~
        (token(Space) ~> token(StringBasic, "<methodFilter>"))
}

然后,您需要一个输入任务来使用解析器的结果并将它们转发到测试框架。
这是在下一个片段中完成的(如果有人比我更了解我想要使用 inputTaskDyn 的微妙之处,我很乐意开悟: ))。请注意任务范围的定义 .in(Test),它授予对测试依赖项的访问权限。

Then you need an input task to use the results of the parser and forward them to the test framework. This is done in the next snippet (if someone more knowledgeable than me wishes to chime in on the subtleties of using an inputTaskDyn, I'll gladly be enlightened :) ). Do note the definition of the scope of the task .in(Test) which grants access to the test dependencies.

lazy val testFiltered = inputKey[Unit]("runs test methods matching *<methodFilter>* within classes matching *<classFilter>*")

testFiltered.in(Test) := Def.inputTaskDyn {
    val (classFilter, methodFilter) = filtersParser.parsed
    runTestsFiltered(classFilter, methodFilter)
}.evaluated

最后一段代码只是将参数转发给预先存在的 testOnly 任务:

And the last bit of code simply forwards the arguments to the pre-existing testOnly task:

def runTestsFiltered(classFilter: String, methodFilter: String) = Def.taskDyn {
    (testOnly in Test).toTask(s" *$classFilter* -- --tests *$methodFilter*")
}



上一个答案



但是,您应该可以通过在两个任务中拆分定义和用法来解决这个问题:

Previous answer

However, you should be able to go around it by splitting definition and usage in two tasks:

import sbt._
import complete.DefaultParsers._

lazy val loadArgTask = inputKey[Unit]("loads and transforms argument")

lazy val runStuff = taskKey[Unit]("Runs some stuff")

lazy val loadArgIntoPropertyTask: Def.Initialize[InputTask[Unit]] = Def.inputTask {
    val myArg = (token(Space) ~> token(StringBasic, "<myArg>")).parsed
    System.setProperty("myArg", myArg + "foo")
}

loadArgTask <<= loadArgIntoPropertyTask

runStuff := {
    println(System.getProperty("myArg"))
}

可以使用如下

> loadArgTask orange
[success] Total time: 0 s, completed [...]
> runStuff
orangefoo
[success] Total time: 0 s, completed [...]

这篇关于SBT:将输入预先应用于inputKeys的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆