Почему сборка Maven работает, когда сборка SBT обнаруживает конфликты - PullRequest
0 голосов
/ 09 мая 2018

Название также может быть:
В чем различия между подключаемыми модулями Maven и SBT.

Я обнаружил, что это проблема при переносе проекта из Mavenв SBT.

Чтобы описать проблему, я создал пример проекта с зависимостями, которые, по моему мнению, ведут себя по-разному, в зависимости от инструмента сборки.

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


Единственные зависимости (стиль sbt)

"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 голосов
/ 22 мая 2018

Кажется, maven-assembly-plugin разрешает конфликты эквивалентно MergeStrategy.first (не уверен, что он полностью эквивалент) , просто выбирая один из файлов неуказанным способом, когда jar-with-dependencies используется (поскольку имеет только одну фазу ):

Если два или более элемента (например, file, fileSet) выбирают разные источники для одного и того же файла для архивирования, только один из исходных файлов будет заархивирован.

Согласно версии 2.5.2 модуля сборки, первый этап добавления файла в архив «выигрывает». Фильтрация выполняется исключительно по имени внутри архива, поэтому один и тот же исходный файл может быть добавлен под разными выходными именами. Порядок фаз следующий: 1) FileItem 2) FileSets 3) ModuleSet 4) DepenedencySet и 5) Элементы репозитория.

Элементы одного типа будут обрабатываться в порядке их появления в дескрипторах. Если вам нужно «перезаписать» файл, включенный в предыдущий набор, единственный способ сделать это - исключить этот файл из предыдущего набора.

Обратите внимание, что это поведение немного отличалось в более ранних версиях сборочного плагина.

Даже если один из конфликтующих файлов будет работать для всех ваших зависимостей (что не обязательно так), Maven не знает, какая именно, поэтому вы можете просто молча получить неверный результат. Я имею в виду, молча во время сборки; во время выполнения вы можете получить, например, AbstractMethodError, или опять просто неверный результат.

Вы можете повлиять на то, какой файл выбирается, написав свой собственный дескриптор, но он ужасно многословен, нет эквивалента простому написанию MergeStrategy.first/lastconcat / discard не допускаются).

Плагин SBT может делать то же самое: по умолчанию используется стратегия, когда вы не указываете ее, но тогда вы можете молча получить неверный результат.

0 голосов
/ 23 мая 2018

Расширение до Алексей Романов ответ .

Я также обновил мой проект с подробным объяснением, так что вы можете проверить его.

Следуя совету

Вы можете проверить это для этого случая, распаковав jar-файл Maven и jar-файлы зависимостей в сообщении об ошибке SBT, а затем проверив, какой файл .class использовал Maven.

Я сравнил fat-jars, произведенный maven и sbt с

  • MergeStrategy.first, который показал некоторые дополнительные файлы
  • MergeStrategy.last, который показал двоичные различия и дополнительные файлы

Я сделал следующий шаг и проверил fat-jars по отношению к зависимостям sbt, обнаружил конфликты, в частности:

Заключение

maven-assembly-plugin разрешает конфликты на уровне jar. Когда он обнаруживает какой-либо конфликт, он выбирает первый jar и просто игнорирует весь контент другого.

Принимая во внимание, что sbt-assembly смешивает все файлы class, разрешая конфликты локально, файл за файлом.

Моя теория заключается в том, что если ваш fat-jar с maven-assembly-plugin работает, вы можете укажите MergeStrategy.first для всех конфликтов в sbt. Единственное их отличие состоит в том, что jar, созданный с sbt, будет еще больше, с дополнительными классами, которые были проигнорированы maven.

0 голосов
/ 22 мая 2018

Из build.sbt я вижу, что в вашей сборке нет стратегии слияния. Кроме того, в вашем ключе libraryDependencies есть Rogue "," , помещенный после зависимости "org.apache.cassandra"% "cassandra-all"% "3.4" в вашем build.sbt в проекте, на который вы поделились ссылкой выше.

Стратегия слияния требуется для обработки всех дубликатов файлов и в банке, а также версий. Следующий пример является примером того, как его получить в вашей сборке.

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
    }
...