SBT:将输入预先应用于inputKeys

hyp*_*rio 10 java scala sbt

在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("<arg>").parsed
    val one = (run in Compile).fullInput(args(0) + "foo").evaluated
  }
)
Run Code Online (Sandbox Code Playgroud)

但我得到了error: Illegal dynamic reference: 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
}
Run Code Online (Sandbox Code Playgroud)

这根本不提供输入.我怀疑这是执行顺序的问题(当我需要时,属性不会被设置,因为JVM可以随意移动线路).

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

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
  }
}
Run Code Online (Sandbox Code Playgroud)

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

Tho*_*mas 8

正如Daniel C. Sobral提到的那样,parsed并且evaluated是宏,定义于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, "<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*")
}
Run Code Online (Sandbox Code Playgroud)

更详细

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

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

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

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
Run Code Online (Sandbox Code Playgroud)

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

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

以前的答案

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

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"))
}
Run Code Online (Sandbox Code Playgroud)

其中可以使用如下

> loadArgTask orange
[success] Total time: 0 s, completed [...]
> runStuff
orangefoo
[success] Total time: 0 s, completed [...]
Run Code Online (Sandbox Code Playgroud)