我有一个关于 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
好的,经过一天的调试,我找到了解决这个问题的方法。
SBT 主要基于以下规则使编译的类无效:
也就是说,路径和修改日期必须完全相同
前 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)
(准备大量输出)
| 归档时间: |
|
| 查看次数: |
767 次 |
| 最近记录: |