Как можно исключить дублирующий класс из сборки sbt?

Мы имеем ситуацию, когда две зависимости имеют ровно один и тот же класс (потому что одна из зависимостей скопировала его и включила в свой собственный источник).

Это приводит к тому, что sbt assembly завершит проверку дедупликации.

Как я могу исключить класс из конкретной банки?

Ответ 1

Вам нужна mergeStrategy, которая возьмет один из файлов.

mergeStrategy in assembly := {
    case PathList("path", "to", "your", "DuplicatedClass.class") => MergeStrategy.first
    case x => (mergeStrategy in assembly).value(x)
}

Обновить

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

Я бы изменил ваше состояние, хотя. Я думаю, что вопрос должен быть "Как я могу включить класс из конкретного JAR?". Причина в том, что может быть более двух JAR, имеющих один и тот же класс, и вы можете включить только один в конце.

Вы можете определить, откуда этот файл, используя AssemblyUtils.sourceOfFileForMerge.

project/IncludeFromJar.scala

import sbtassembly._
import java.io.File
import sbtassembly.Plugin.MergeStrategy

class IncludeFromJar(val jarName: String) extends MergeStrategy {

  val name = "includeFromJar"

  def apply(args: (File, String, Seq[File])): Either[String, Seq[(File, String)]] = {
    val (tmp, path, files) = args
    val includedFiles = files.flatMap { f =>
      val (source, _, _, isFromJar) = sbtassembly.AssemblyUtils.sourceOfFileForMerge(tmp, f)
      if(isFromJar && source.getName == jarName) Some(f -> path) else None
    }
    Right(includedFiles)
  }

}

build.sbt

mergeStrategy in assembly := {
    case PathList("path", "to", "your", "DuplicatedClass.class") => new IncludeFromJar("jarname.jar")
    case x => (mergeStrategy in assembly).value(x)
}

Ответ 2

Какую версию sbtassembly вы используете? Я считаю, что я использую другую версию (0.14.2), потому что я получаю ошибку, используя use project/IncludeFromJar.scala.

При компиляции я получаю следующую ошибку:

method apply cannot override final member
def apply(args: (File, String, Seq[File])): Either[String, Seq[(File, String)]] = {
^  

После дальнейшего изучения я обнаружил, что apply метода sbtassembly.MergeStrategy является окончательным. Следовательно, метод IncludeFromJar apply не может переопределить sbtassembly.MergeStrategy даже с указанием override def apply в IncludeFromJar

Спасибо :)

Ответ 3

Возможно, немного поздно для обсуждения, но чтобы помочь другим, которые находят это:

когда вы расширяете MergeStrategy, вы хотите переопределить метод apply с помощью подписи:

 def apply(tempDir: File, path: String, files: Seq[File]): Either[String, Seq[(File, String)]]

Метод apply, который является окончательным с аргументом tuple, вызывает метод apply с параметрами, выделенными как указано выше

https://github.com/sbt/sbt-assembly/blob/edd35cfbaf05c3465371b63d38fda8ac579d766c/src/main/scala/sbtassembly/MergeStrategy.scala#L19

Таким образом, пример становится:

def includeFromJar(val jarName: String): sbtassembly.MergeStrategy = new sbtassembly.MergeStrategy {

  val name = "includeFromJar"

  def apply(tmp: File, path: String, files: Seq[File]): Either[String, Seq[(File, String)]] = {
    val includedFiles = files.flatMap { f =>
      val (source, _, _, isFromJar) = sbtassembly.AssemblyUtils.sourceOfFileForMerge(tmp, f)
      if(isFromJar && source.getName == jarName) Some(f -> path) else None
    }
    Right(includedFiles)
  }
}

mergeStrategy in assembly := {
    case PathList("path", "to", "your", "DuplicatedClass.class") => includeFromJar("jarname.jar")
    case x => (mergeStrategy in assembly).value(x)
}