SBT: pre-applying input to inputKeys(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
}
)
但我得到 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
}
这根本不提供输入.我怀疑这是执行顺序的问题(在我想要的时候没有设置属性,因为 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 提到的,parsed
和 evaluated
是宏,定义在 InputWrapper
.
As Daniel C. Sobral mentioned, parsed
and evaluated
are macros, defined in InputWrapper
.
由于它们是在编译时执行的,并且参数是在运行时检索的,所以它们不能很好地混合.特别是 args 的值只是在运行时才真正定义,不能被 evaluate
宏检索到.
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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:SBT:将输入预应用于 inputKeys
基础教程推荐
- 如何对 HashSet 进行排序? 2022-01-01
- 如何强制对超级方法进行多态调用? 2022-01-01
- Spring Boot Freemarker从2.2.0升级失败 2022-01-01
- 如何使用 Stream 在集合中拆分奇数和偶数以及两者的总和 2022-01-01
- 首次使用 Hadoop,MapReduce Job 不运行 Reduce Phase 2022-01-01
- 如何使用 Eclipse 检查调试符号状态? 2022-01-01
- 在螺旋中写一个字符串 2022-01-01
- Java 中保存最后 N 个元素的大小受限队列 2022-01-01
- 如何在不安装整个 WTP 包的情况下将 Tomcat 8 添加到 Eclipse Kepler 2022-01-01
- 由于对所需库 rt.jar 的限制,对类的访问限制? 2022-01-01