1. 使用 Def.inputTaskDyn 定义动态输入任务

使用 Def.inputTaskDyn 定义动态输入任务 

假设已经有了一个名为 openbrowser 的任务用于打开浏览器(由于插件的缘故)。以下是我们在输入任务后排序任务的方法。

build.sbt v1 

lazy val runopen = inputKey[Unit]("run and then open the browser")
lazy val openbrowser = taskKey[Unit]("open the browser")

lazy val root = (project in file("."))
 
.settings(
    runopen
:= (Def.inputTaskDyn {
     
import sbt.complete.Parsers.spaceDelimited
     
val args = spaceDelimited("<args>").parsed
     
Def.taskDyn {
       
(Compile / run).toTask(" " + args.mkString(" ")).value
        openbrowser
     
}
   
}).evaluated,
    openbrowser
:= {
      println
("open browser!")
   
}
 
)

build.sbt v2 

尝试重新连接 Compile / run 会很复杂。由于对内部 Compile / run 的引用已经在延续任务中,简单地将 runopen 重新连接到 Compile / run 会创建一个循环引用。为了打破循环,我们将引入 Compile / run 的克隆,称为 Compile / actualRun

lazy val actualRun = inputKey[Unit]("The actual run task")
lazy val openbrowser = taskKey[Unit]("open the browser")

lazy val root = (project in file("."))
 
.settings(
   
Compile / run := (Def.inputTaskDyn {
     
import sbt.complete.Parsers.spaceDelimited
     
val args = spaceDelimited("<args>").parsed
     
Def.taskDyn {
       
(Compile / actualRun).toTask(" " + args.mkString(" ")).value
        openbrowser
     
}
   
}).evaluated,
   
Comile / actualRun := Defaults.runTask(
     
Runtime / fullClasspath,
     
Compile / run / mainClass,
     
Compile / run / runner
   
).evaluated,
    openbrowser
:= {
      println
("open browser!")
   
}
 
)

* 请注意,某些任务(例如 testOnly)会在结尾处出现空格,因此可能需要对为 toTask 构建的字符串进行右修剪(.replaceAll("\s+$", ""))来处理空 args

Compile / actualRun 的实现是从 Defaults.scalarun 任务的实现复制粘贴的。

现在,我们可以从 shell 中调用 run foo,它会使用传递的参数评估 Compile / actualRun,然后评估 openbrowser 任务。