SBT 不必要的重新编译

Has*_*tor 1 scala sbt

我有一个关于 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'
Run Code Online (Sandbox Code Playgroud)

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

Has*_*tor 5

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

SBT 主要基于以下规则使编译的类无效:

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

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

  • 源代码文件
  • 常春藤依赖 jars
  • JRE 的罐子

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

docker run ... -v /Users/<username>/projects/bla:/Users/<username>/projects/bla ...
Run Code Online (Sandbox Code Playgroud)

您不关心源和常春藤罐的时间戳,因为它们将完全相同(它们是相同的文件)。

必须关心时间戳的地方是 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""")
  ...
}
Run Code Online (Sandbox Code Playgroud)

而且,当然,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) }
Run Code Online (Sandbox Code Playgroud)

(准备大量输出)