为了加快我们的开发工作流程,我们拆分了测试,并在多个代理上并行运行每个部分。但是,编译测试源似乎大部分时间都在测试步骤中。
为避免这种情况,我们使用sbt test:compile
预编译测试,并使用已编译的目标构建docker映像。
此后,每个代理程序都使用此映像来运行测试。但是,即使已编译的类存在,它似乎仍会重新编译测试和应用程序源。
有没有办法使sbt使用现有的已编译目标?
更新:提供更多背景信息
该问题严格涉及scala和sbt(因此带有sbt标签)。
我们的CI流程分为多个阶段。它大致是这样的。
阶段1:使用sbt compile
使用SBT将Scala项目编译为Java bitecode,我们使用sbt test:compile
在同一测试中编译测试源。将目标捆绑在docker映像中并推送到远程存储库
阶段2:我们使用多个代理来拆分和并行运行测试。
测试是从构建的docker映像运行的,因此环境是
相同。但是,运行sbt test
会导致项目重新编译,甚至
通过编译的位码存在。
为清楚起见,我基本上想在一台计算机上进行编译,而无需重新编译就在另一台计算机上运行已编译的测试源
更新
我不认为https://stackoverflow.com/a/37440714/8261是同一个问题,因为与它不同的是,我不装载卷也不在主机上构建。一切都在docker中进行编译和运行,但分为两个构建阶段。因此,文件的修改时间和路径保持不变。
调试输出具有类似的内容
Initial source changes:
removed:Set()
added: Set()
modified: Set()
Invalidated products: Set(/app/target/scala-2.12/classes/Class1.class, /app/target/scala-2.12/classes/graph/Class2.class, ...)
External API changes: API Changes: Set()
Modified binary dependencies: Set()
Initial directly invalidated classes: Set()
Sources indirectly invalidated by:
product: Set(/app/Class4.scala, /app/Class5.scala, ...)
binary dep: Set()
external source: Set()
All initially invalidated classes: Set()
All initially invalidated sources:Set(/app/Class4.scala, /app/Class5.scala, ...)
Recompiling all 304 sources: invalidated sources (266) exceeded 50.0% of all sources
Compiling 302 Scala sources and 2 Java sources to /app/target/scala-2.12/classes ...
它没有初始源更改,但是产品无效。
更新:可复制的最小项目
我创建了一个最小的sbt项目来重现该问题。 https://github.com/pulasthibandara/sbt-docker-recomplile
如您所见,在构建阶段之间没有任何变化,除了在新步骤(新容器)的第二阶段中运行。
答案 0 :(得分:2)
尽管https://stackoverflow.com/a/37440714/8261指出了正确的方向,但根本的问题和解决方案却有所不同。
问题
当SBT在docker构建的不同阶段上运行时,似乎可以重新编译所有内容。这是因为docker压缩在每个阶段创建的图像,从而从源中剥离lastModifiedDate的毫秒部分。
在确定源代码是否已更改时,SBT取决于lastModifiedDate,并且由于其不同(毫秒部分),因此生成会触发完全重新编译。
解决方案
Java 8:
根据{{3}}中的建议,在运行SBT时设置-Dsbt.io.jdktimestamps=true
可以解决此问题。
较新: 遵循https://github.com/sbt/sbt/issues/4168#issuecomment-417655678
我通过在docker文件中设置SBT_OPTS
env变量来解决了这个问题,例如
ENV SBT_OPTS="${SBT_OPTS} -Dsbt.io.jdktimestamps=true"
https://github.com/sbt/sbt/issues/4168#issuecomment-417658294已使用此替代方法进行了更新。
答案 1 :(得分:1)
我认为这里已经有了答案:https://stackoverflow.com/a/37440714/8261
要完全正确看起来很棘手。祝你好运!
如果上述方法太困难(即让sbt test
认为您的测试类不需要重新编译),则可以避免使用sbt
,而可以使用{ {1}}。
如果您可以获取java
来记录运行测试套件所使用的sbt
命令(例如,使用调试日志记录),则可以直接在测试运行程序代理上运行该命令,这将完全排除java
重新编译内容。
(如果类路径太长而无法在外壳程序中作为命令行参数传递,则可能需要将sbt
命令写入脚本文件。我以前不得不在大型项目中这样做)
与上面的方法相比,这将是一种更加骇人听闻的方法,但可能更快地开始工作。
答案 2 :(得分:0)
一个可能的解决方案可能是定义自己的sbt任务而没有依赖项,或者尝试更改测试任务。例如,如果这是您的测试框架,则可以创建一个任务来运行JUnit运行器。要定义任务,请参阅《实现任务》上的this。
您甚至可以编译发送代码并从所需的任何scala代码的同一任务运行遥控器。从sbt参考手册
您可能正在定义自己的任务,或者计划重新定义现有任务。两种方式看起来都一样。使用:=将一些代码与任务键关联起来