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] 的函数,但 stageCleanupTaskTaskKey[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 dependencyillegal 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,那么&lt;&lt;=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 中的排序和覆盖任务的主要内容,如果未能解决你的问题,请参考以下文章

SBT:覆盖任务“test”的行为以仅从默认 testSource 文件夹运行测试,有多个测试源文件夹

覆盖 SBT 插件中的映射

覆盖 sbt 中的传递依赖版本 [重复]

如何在具有多个子项目的 SBT 项目中覆盖子项目中的设置

Sbt autoplugin 覆盖项目设置

Playframework:无法覆盖 sbt 依赖项