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 / actualRunCompile / run 複製品

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.scala 中 run 任務的實作複製貼上而來。

現在我們可以從 Shell 呼叫 run foo,它會使用傳入的引數評估 Compile / actualRun,然後評估 openbrowser 任務。