当SBT装配发现冲突时,为什么Maven装配工作

时间:2018-05-09 09:22:55

标签: maven sbt maven-3 maven-assembly-plugin sbt-assembly

标题也可以是:
Maven和SBT组装插件之间有什么区别。

我发现这是一个问题,同时将项目从Maven迁移到SBT。

为了描述问题,我创建了一个带有依赖关系的示例项目,我发现它的行为有所不同,具体取决于构建工具。

https://github.com/atais/mvn-sbt-assembly

唯一的依赖是(sbt style)

"com.netflix.astyanax" % "astyanax-cassandra" % "3.9.0",
"org.apache.cassandra" % "cassandra-all" % "3.4",

我不明白的是,为什么mvn package成功创建了胖罐,而sbt assembly给出了冲突:

[error] 39 errors were encountered during merge
[error] java.lang.RuntimeException: deduplicate: different file contents found in the following:
[error] /home/siatkowskim/.ivy2/cache/org.slf4j/jcl-over-slf4j/jars/jcl-over-slf4j-1.7.7.jar:org/apache/commons/logging/<some classes>
[error] /home/siatkowskim/.ivy2/cache/commons-logging/commons-logging/jars/commons-logging-1.1.1.jar:org/apache/commons/logging/<some classes>
...
[error] /home/siatkowskim/.ivy2/cache/com.github.stephenc.high-scale-lib/high-scale-lib/jars/high-scale-lib-1.1.2.jar:org/cliffc/high_scale_lib/<some classes>
[error] /home/siatkowskim/.ivy2/cache/com.boundary/high-scale-lib/jars/high-scale-lib-1.0.6.jar:org/cliffc/high_scale_lib/<some classes>
...

3 个答案:

答案 0 :(得分:4)

扩展为Alexey Romanov answer

我还更新了my project的详细说明,因此您可能需要查看它。

遵循建议

  

你可以通过解压缩Maven产生的jar和SBT错误消息中的依赖jar来验证它的情况,然后检查Maven使用的.class文件。

我将fat-jarsmaven生成的sbt

进行了比较
  • MergeStrategy.first,显示了一些额外的文件
  • MergeStrategy.last,显示二元差异&amp;额外文件

我已采取下一步措施并检查fat-jars针对发现冲突的依赖关系sbt,具体为:

结论

maven-assembly-plugin解决了jar级别的冲突。 当发现任何冲突时,它会选择第一个jar并忽略另一个中的所有内容。

sbt-assembly混合所有class个文件,在本地解析冲突,逐个文件。

我的理论会是,如果您使用fat-jar制作的maven-assembly-plugin有效,那么您可以 为MergeStrategy.first中的所有冲突指定sbt。 他们唯一的区别是,使用jar生成的sbt会更大,包含maven忽略的额外类。

答案 1 :(得分:2)

似乎maven-assembly-plugin通过选择其中一个来解决冲突等同于MergeStrategy.first(不确定它是否等同于等等)使用jar-with-dependencies时,以未指定的方式处理文件(自it only has one phase起):

  

If two or more elements (e.g., file, fileSet) select different sources for the same file for archiving, only one of the source files will be archived.

     

根据程序集插件的2.5.2版,将文件添加到存档的第一阶段&#34;胜出&#34;。过滤仅基于存档内的名称完成,因此可以在不同的输出名称下添加相同的源文件。阶段的顺序如下:1)FileItem 2)FileSets 3)ModuleSet 4)DepenedencySet和5)Repository元素。

     

相同类型的元素将按照它们在描述符中出现的顺序进行处理。如果你需要&#34;覆盖&#34;前一个集合包含的文件,唯一的方法是从早期的集合中排除该文件。

     

请注意,在早期版本的程序集插件中,此行为略有不同。

即使其中一个冲突的文件适用于所有依赖项(也不一定如此),Maven也不知道哪一个,所以你可以默默地得到错误的结果。我的意思是,在构建时默默无闻;在运行时你可以得到例如AbstractMethodError,或者只是错误的结果。

您可以通过编写自己的描述符来影响选择哪个文件,但是它非常冗长,并不等同于只写MergeStrategy.first/last(和concat / {{ 1}}是不允许的。)

SBT插件也可以这样做:当你没有指定一个策略时默认为策略,但是,你可以默默地得到错误的结果。

答案 2 :(得分:0)

build.sbt 我可以看到他们在你构建中没有合并策略。此外,在“org.apache.cassandra”%“cassandra-all”%“依赖关系之后的 libraryDependencies Key 中有一个Rogue ”,“在您上面分享的链接项目的 build.sbt 中。

需要合并策略来处理所有重复文件以及jar和版本。以下是如何在构建中安装一个的示例。

assemblyMergeStrategy in assembly := {
  case m if m.toLowerCase.endsWith("manifest.mf")       => MergeStrategy.discard
  case m if m.toLowerCase.matches("meta-inf.*\\.sf$")   => MergeStrategy.discard
  case "reference.conf"                                 => MergeStrategy.concat
  case x: String if x.contains("UnusedStubClass.class") => MergeStrategy.first
  case _                                                => MergeStrategy.first
}

如果您的项目中没有子项目,您可以尝试编写一个简单的构建文件。您可以尝试以下build.sbt。

name := "assembly-test",

version := "0.1",

scalaVersion := "2.12.4",

libraryDependencies ++= Seq(
      "com.netflix.astyanax" % "astyanax-cassandra" % "3.9.0",
      "org.apache.cassandra" % "cassandra-all" % "3.4"
)

mainClass in assembly := Some("com.atais.cassandra.MainClass")

assemblyMergeStrategy in assembly := {
      case m if m.toLowerCase.endsWith("manifest.mf")       => MergeStrategy.discard
      case m if m.toLowerCase.matches("meta-inf.*\\.sf$")   => MergeStrategy.discard
      case "reference.conf"                                 => MergeStrategy.concat
      case x: String if x.contains("UnusedStubClass.class") => MergeStrategy.first
      case _                                                => MergeStrategy.first
    }