【问题标题】:Unnecessary recompilations by SBTSBT 不必要的重新编译
【发布时间】:2016-05-24 09:47:43
【问题描述】:

我有一个关于 SBT 看似不必要的重新编译的问题。我有以下场景:我在 docker 容器中运行 SBT,通过将带有我的应用程序源代码的卷附加到容器,并以 sbt 作为入口点启动容器。如果我在该容器中持续运行 SBT,它不会重新编译整个应用程序,这很好。

但是,如果我在 OS X 上本地启动 SBT,它会进行完整的重新编译。如果之后我在 docker 中再次启动它,它会再次进行完整的重新编译。这需要很长时间,而且真的很烦人。这种行为的原因可能是什么?

这是我在容器中启动 SBT 的方式:

docker run --name=bla -it --net=host -v /Users/me/.ivy2:/tmp/.ivy2 \
-v /Users/me/.aws/config:/root/.aws/config \
-v /Users/me/.sbt:/root/.sbt \
-v /Users/me/projects/myapp:/src 01ac0b888527 \
/bin/sh -c 'sbt -Dsbt.ivy.home=/tmp/.ivy2 -Divy.home=/tmp/.ivy2 -jvm-debug 5005 -mem 3072'

我的 Java、Scala 和 SBT 版本在主机和容器中是相同的。具体来说:Scala 2.11.8、Java 1.8.0_77、SBT 0.13.11

【问题讨论】:

  • 你有类似的 sbt/ivy 配置吗?两种情况下的常春藤存储库文件夹是否相同?
  • 是的,是一样的。事实上,我也将~/.ivy2~/.sbt 目录映射到容器。否则它每次都会获取所有依赖项,这需要 40 分钟 :(

标签: scala sbt


【解决方案1】:

好的,经过一天的调试,我找到了解决这个问题的方法。

SBT主要根据以下规则使编译后的类失效:

  • 规范路径
  • 最后修改日期

也就是说,路径和修改日期必须完全相同

  • 源代码文件
  • Ivy 依赖项 jars
  • JRE 的罐子

前 2 点很容易实现,因为它只是 docker 卷的映射。关键是映射到与主机上完全相同的路径。例如,如果您像我一样在 OS X 上工作,您的项目源路径可能如下所示:/Users/<username>/projects/bla,因此在您的 docker run 命令中,您必须执行以下操作:

docker run ... -v /Users/<username>/projects/bla:/Users/<username>/projects/bla ...

您不必关心源和 ivy jar 的时间戳,因为它们将完全相同(它们是相同的文件)。

关心时间戳的地方是 JRE 的东西。我使用 JRE 构建了 docker 映像(使用 sbt-docker 插件),所以我最终读取了本地 JRE 库的修改日期并在映像中设置了相同的日期:

new mutable.Dockerfile {
  ...
  val hostJreTimestamp = new Date(new File(javaHome + "/jre/lib/rt.jar").lastModified()).toString
  val hostJceTimestamp = new Date(new File(javaHome + "/jre/lib/jce.jar").lastModified()).toString
  runRaw(s"""touch -d "$hostJreTimestamp" $javaHome/jre/lib/rt.jar""")
  runRaw(s"""touch -d "$hostJceTimestamp" $javaHome/jre/lib/jce.jar""")
  ...
}

当然,JRE 也应该安装到与主机上完全相同相同的路径,例如,如果您过去从 RPM 安装 Java,这可能会出现问题。我最终下载了服务器 JRE(分发为 .tar.gz)并手动将其解压缩到正确的路径。

所以,长话短说,它最终奏效了。无需重新编译,无需长时间等待。我能够从 2 个主要来源找到相关信息:SBT 源代码,特别是这个函数:https://github.com/sbt/sbt/blob/0.13/compile/inc/src/main/scala/sbt/inc/IncrementalCommon.scala#L271,并在 build.sbt 中启用 SBT 调试输出:

logLevel := Level.Debug
incOptions ~= { _.copy(apiDebug = true, relationsDebug = true) }

(准备大量输出)

【讨论】:

    【解决方案2】:

    这只是一个猜测。正如 rumoku 所说,这可能是存储库的问题,但我认为它与 SBT 本身有关。从 SBT 的角度来看,您正在运行两台不同的机器,当它认为文件已更改时,它必须全部编译。

    我不知道 SBT 或编译器如何识别 Scala 和 Java 的版本,但即使您在两种环境中拥有完全相同的 Java 和 Scala 版本,SBT 也可能认为它们是不同的。这是有道理的,因为它们是不同的操作系统。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-26
      • 2013-06-05
      • 1970-01-01
      • 1970-01-01
      • 2013-12-29
      • 2014-01-28
      • 2017-10-09
      相关资源
      最近更新 更多