项目中的 build.sbt
中发现定义任务时有 task2 <<= task1 map {...}
这样任务依赖的写法, 这个 <<=
方法有点晦涩难懂, 读过 sbt in action
之后才知道这是 sbt 0.12 或之前的做法, sbt 0.13 之后不这么用了, 直接访问下 task1.value 就行, 因此前面可改写为 task2 := {task1.value; ...}
, 这也使得定义任务依赖时与普通任务一致风格了. 新的写法得益于 Scala 2.10 的宏特性, 后面还会讲到.
对于依赖于多个任务的情况, 在 sbt 0.13 前面分别是这样的, 假定有三个 Task(task1, task2, 和 task3)
val task1 = taskKey[Int]("task 1")
val task2 = taskKey[Int]("task 2")
val task3 = taskKey[Int]("task 3")
如果 task3 依赖于 task1 和 task 2
sbt 0.13 前后版本中的写法分别是
像任务的 .value
属性其实是一个宏定义, 源码
所谓的 Scala 的宏与 C/C++ 的宏效果很类似, 试想 sbt 在执行 task3 之前会有一个宏替换的操作, 在 task1.value
,task2.value
处执行 task1 和 task2 任务, 并把结果放在此处, 最后才把两结果相加. sbt 只对 task 或 setting 定义中的 task.value
进行宏处理, 如果在自定义的方法中也调用 task.value
你将会收到宏定义上一行一样的编译错误.
为说明宏的先于本身任务先执行, 可以看下面一个例子:
执行后是这样的
➜ test sbt
[info] Set current project to test (in build file:/Users/yanbin/test/)
> show task3
task1
task2
[info] 2
[success] Total time: 0 s, completed Apr 22, 2016 10:54:20 PM
> inspect tree task3
[info] *:task3 = Task[Int]
[info] +-*:task1 = Task[Int]
[info] +-*:task2 = Task[Int]
由上可见, 尽管 task1.value 写在了 if(false) 条件中, 但是它仍然被执行了, 这就是宏处理的功劳, 而其中的 `println("task3") 受控于 false 条件.
sbt 中被依赖的任务是并发执行的, 所以不因为谁写在前面就谁先执行, 看个例子
执行 task3
> task3
task2
task1
可见 task1 和 task2 是并发执行的, task2 并不需要等待 task1
那么如何让任务依赖有先后顺序呢? 可以使用 dependsOn()
方法了, 同样针对 <<=
和 :=
可以有两种写法
这样 task2 排在 task1 之后执行, dependsOn()
可以串取起来, 并且它的原型是 dependsOn(tasks: AnyInitTask*)
, 所以它可以同时接受多个参数(它们要并发执行了)
如何在内置任务之前做些事情呢? 把原任务呼出使其依赖别的任务, 比如在 run
之前执行某个任务, 同样列出新旧两种写法
sbt 官方文档 : Modifying an Existing Task.
如何让依赖间条件依赖? 像是动态依赖, 还是接回上面的例子, 想要在 task
执行结果为 100 时才执行 run in Runtime
, 想像中的代码如下:
这里有两个问题: 1) task1 和 (run in Runtime) 是并发执行的, 2) (run in Runtime) 在判断条件之前的宏处理阶段就已经先执行完了. 所以 (run in Runtime) 无论如何都会执行, 我行我素, 完全与 if 条件无关.
针对于此, 我们有一个不甚完美的解决, 还是用 dependsOn()
方法, task1 在某种情况下调用 sys.error("error occurs")
, 那么它后面的所有任务就不再执行了, 回到 sbt 控制后. 片断代码如下:
项目中有一个 Scala 文件
所以如果 run 正常执行了可以输出 Hello World.
, 现在来看看条件控制执行 run
parallelExecution
默认为 true, 所以第一次执行 task3 时 run 没被执行直接退到 sbt 控制台, 然后把 parallelExecution
设置为 false 后, task3 就能执行 run 任务了.
参见 sbt 官方文档: Handling Failure
sbt 0.13 中好像有一种更精确控制条件执行任务的办法: Dynamic Computations with Def.taskDyn, 试了一下还是可以的:
执行效果:
方向是对了, 只是对于 InputTask run in Runtime
来说在 Def.task
中无处安身, 写成
这样不会去执行内置的 run
任务, 在 Def.task
又不允许写成 (run in Runtime).evaluated
. 目前还尚不清楚如何把动态任务应用到修改内置的 run
任务.
本文链接 https://yanbin.blog/sbt-task-dependency/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。