SBT 中的排序和覆盖任务
Posted
技术标签:
【中文标题】SBT 中的排序和覆盖任务【英文标题】:Sequencing and overriding tasks in SBT 【发布时间】:2016-04-22 20:06:39 【问题描述】:快速总结:我试图在顶层项目中等待所有 SBT 子模块构建,然后删除它们的 target
目录。***应用程序聚合所有子模块,它们不会单独部署,而只是作为具有类路径依赖项的捆绑包,而子模块中的重复库会破坏整个包的大小,并且 slug 超出了 Heroku 的限制。
从技术上讲,我正在尝试 actually use this - 我正在尝试添加将在 stage
之后运行的“清理”任务。上面链接中的解决方案似乎对我不起作用(Play 2.4,SBT 0.13.5),错误说明它比我能做的更好:
build.sbt:50: error: reference to stage is ambiguous;
it is imported twice in the same scope by
import _root_.com.typesafe.sbt.packager.universal.UniversalPlugin.autoImport._
and import $52e59eb09172b3222f9e._
stage :=
假设我有我的清理任务:
val stageCleanupTask = taskKey[Unit]("Clean after stage task")
stageCleanupTask :=
val log = streams.value.log
if (sys.env.getOrElse("POST_STAGE_CLEAN", "false").equals("true"))
log.info("Cleaning submodules' target directories")
sbt.IO.delete(baseDirectory.value / "modules" / "a" / "target")
sbt.IO.delete(baseDirectory.value / "modules" / "b" / "target")
sbt.IO.delete(baseDirectory.value / "modules" / "c" / "target")
我正在尝试覆盖stage
:
stage :=
val f = (stage in Universal).value
stageCleanupTask.value
f
这似乎完全是错误的,因为这两个任务同时运行。 SBT 也不是很容易,官方文档里也没找到太多,所以就随便玩玩了:
stage.flatMap
需要一个返回 Task[U]
的函数,但 stageCleanupTask
是 TaskKey[T]
,而 .value
在非常特定的区域之外不起作用,因此通过类似于 @ 的东西进行组合987654337@似乎是不可能的。
dependsOn
只能作为stage <<= stage dependsOn stageCleanupTask
工作,这与我想要的依赖链完全相反。 stageCleanupTask
应该依赖于stage
,但类型不匹配(Task[Unit]
vs Task[File]
)
我试图在覆盖的stage
中尝试组合,如下所示:
stage :=
(stage in Universal).map(f => /*follow up*/ f).value
但那通常只会打我illegal dynamic dependency
或illegal dynamic reference
的耳光
排序 SBT 任务的首选方式是什么?
【问题讨论】:
我不知道您的问题的答案,但就减少 Heroku 应用程序的 slug 大小而言,您可以分叉 buildpack 并修改此部分以包含您的target
sub-目录:github.com/heroku/heroku-buildpack-scala/blob/master/bin/…
我也会接受 PR 在这里添加 SBT_POST_TASKS:github.com/heroku/heroku-buildpack-scala/blob/master/bin/…
@codefinger 谢谢,我一定会调查 SBT_POST_TASKS - 虽然没有承诺,bash(尤其是健壮的脚本)不是我的强项 :)
我做出了改变。 github.com/heroku/heroku-buildpack-scala/commit/… 如果你可以在help.heroku.com 开票,我可以告诉你如何使用 buildpack 的这个分支并提供更多信息。我宁愿从这个帖子中讨论出来。
@codefinger SBT 唯一的解决方案应该是完全可能的;)它被称为Simple Build Tool
是有原因的
【参考方案1】:
请参阅http://www.scala-sbt.org/0.13/docs/Howto-Sequencing.html 了解如何对任务进行排序。
比如:
stage in Universal := Def.sequential(
stage in Universal,
stageCleanupTask
)
【讨论】:
我对此投了赞成票,但在查看了实际代码后,我有了第二个想法。这不符合 OP 的要求。这是该方法的实际签名:def sequential[A0, B](task0: Initialize[Task[A0]], last: Initialize[Task[B]]): Initialize[Task[B]]
,这意味着它仍然返回您传递给它的第二个任务的类型,在本例中为 Unit
。这是不正确的,因为 stage
需要 sbt.File
。【参考方案2】:
首先让我说baseDirectory.value / "modules" / "a" / "target"
不是您想要使用的那种路径定义,因为使用SBT 为您提供的设置要好得多。我推荐使用(target in moduleName).value
。
至于你的主要问题,我建议你这样做:
val stageCleanupTask = taskKey[sbt.File]("Clean after stage task")
lazy val root = project.in(file("."))
...
.settings(
stageCleanupTask :=
val a = (stage in Universal).value
val log = streams.value.log
if (sys.env.getOrElse("POST_STAGE_CLEAN", "false").equals("true"))
log.info("Cleaning submodules' target directories")
sbt.IO.delete((target in a).value)
sbt.IO.delete((target in b).value)
sbt.IO.delete((target in c).value)
a
,
stage <<= stageCleanupTask
我刚刚在我自己的一个项目中进行了测试,结果完美无缺。
编辑 1
我的电池快没电了,所以我无法进一步查看this,但它可能是您正在寻找的东西。
【讨论】:
这个讨论也蛮有趣的:github.com/sbt/sbt/issues/1001 哦,对了,感谢您指出(target in module).value
。至于线程清理只是在最后返回它 - 我认为可能有一个专用的,也许更优雅的解决方案,但我会接受它。谢谢!最后,这意味着如果我更改stageCleanupTask
,那么<<=
或Def.sequential
应该可以工作。
我收回了,Def.sequential
仍然不匹配所有类型。【参考方案3】:
替换以下代码
stage :=
val f = (stage in Universal).value
stageCleanupTask.value
f
这个
import com.typesafe.sbt.packager.universal.UniversalPlugin.autoImport.Universal
stage := Def.taskDyn
val x = (stage in Universal).value
Def.task
stageCleanupTask.value
x
.value
这将导致任务排序并按预期工作。
【讨论】:
在这种情况下如何导入Universal
?以上是关于SBT 中的排序和覆盖任务的主要内容,如果未能解决你的问题,请参考以下文章