SBT不必要的重新编译

时间:2016-05-24 09:47:43

标签: 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'

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

2 个答案:

答案 0 :(得分:3)

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

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

  • Canonical path
  • 上次修改日期

也就是说,

的路径和修改日期必须完全相同
  • 源代码文件
  • Ivy依赖项罐子
  • JRE的罐子

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

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

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

关注时间戳的地方是JRE的东西。我使用JRE烘焙(使用sbt-docker插件)构建了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) }

(准备大量输出)

答案 1 :(得分:0)

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

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