SBT:将输入预先应用于inputKeys

在SBT中:我想定义一个inputKey,它读入命令行参数,稍微改变它们并将结果用作其他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("").parsed val one = (run in Compile).fullInput(args(0) + "foo").evaluated } ) 

但我收到error: Illegal dynamic reference: args

我也尝试过:

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

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

所以,在我的绝望中,我甚至尝试过残暴的:

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

强迫订单。 这只会抛出一个NullPointerException。

正如Daniel C. Sobral所提到的, parsedevaluated是在InputWrapper定义的宏。

由于它们是在编译时执行的,并且在运行时检索参数,因此它们不能很好地混合。 特别是,args的值仅在运行时实际定义,并且不能由evaluated宏检索。

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

更新的答案

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

 val filtersParser = { import complete.DefaultParsers._ (token(Space) ~> token(StringBasic, "")) ~ (token(Space) ~> token(StringBasic, "")) } lazy val testFiltered = inputKey[Unit]("runs test methods matching ** within classes matching **") 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)]

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

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

 lazy val testFiltered = inputKey[Unit]("runs test methods matching ** within classes matching **") testFiltered.in(Test) := Def.inputTaskDyn { val (classFilter, methodFilter) = filtersParser.parsed runTestsFiltered(classFilter, methodFilter) }.evaluated 

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

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

以前的答案

但是,您应该能够通过在两个任务中拆分定义和用法来绕过它:

 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, "")).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 [...]