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 任务。