Run custom task automatically before/after standar

2020-01-27 04:15发布

问题:

I often want to do some customization before one of the standard tasks are run. I realize I can make new tasks that executes existing tasks in the order I want, but I find that cumbersome and the chance that a developer misses that he is supposed to run my-compile instead of compile is big and leads to hard to fix errors.

So I want to define a custom task (say prepare-app) and inject it into the dependency tree of the existing tasks (say package-bin) so that every time someone invokes package-bin my custom tasks is run right before it.

I tried doing this

  def mySettings = {
    inConfig(Compile)(Seq(prepareAppTask <<= packageBin in Compile map { (pkg: File) =>
      // fiddle with the /target folder before package-bin makes it into a jar
    })) ++
    Seq(name := "my project", version := "1.0")
  }

  lazy val prepareAppTask = TaskKey[Unit]("prepare-app")

but it's not executed automatically by package-bin right before it packages the compile output into a jar. So how do I alter the above code to be run at the right time ?

More generally where do I find info about hooking into other tasks like compile and is there a general way to ensure that your own tasks are run before and after a standard tasks are invoked ?.

回答1:

Extending an existing task is documented the SBT documentation for Tasks (look at the section Modifying an Existing Task).

Something like this:

compile in Compile <<= (compile in Compile) map { _ => 
  // what you want to happen after compile goes here 
}

Actually, there is another way - define your task to depend on compile

prepareAppTask := (whatever you want to do) dependsOn compile

and then modify packageBin to depend on that:

packageBin <<= packageBin dependsOn prepareAppTask

(all of the above non-tested, but the general thrust should work, I hope).



回答2:

As an update for the previous answer by @Paul Butcher, this could be done in a bit different way in SBT 1.x versions since <<== is no longer supported. I took an example of a sample task to run before the compilation that I use in one of my projects:

lazy val wsdlImport = TaskKey[Unit]("wsdlImport", "Generates Java classes from WSDL")

wsdlImport := {
  import sys.process._
  "./wsdl/bin/wsdl_import.sh" !
  // or do whatever stuff you need
}

(compile in Compile) := ((compile in Compile) dependsOn wsdlImport).value

This is very similar to how it was done before 1.x.

Also, there is another way suggested by official SBT docs, which is basically a composition of tasks (instead of dependencies hierarchy). Taking the same example as above:

(compile in Compile) := {
  val w = wsdlImport.value
  val c = (compile in Compile).value
  // you can add more tasks to composition or perform some actions with them
  c
}

It feels like giving more flexibility in some cases, though the first example looks a bit neater, as for me.

Tested on SBT 1.2.3 but should work with other 1.x as well.



标签: scala sbt