如何在 IntelliJ IDEA 中使用 SBT 构建 Uber JAR(Fat JAR)?

How to build an Uber JAR (Fat JAR) using SBT within IntelliJ IDEA?

我正在使用 SBT(在 IntelliJ IDEA 中)构建一个简单的 Scala 项目。

我想知道构建 Uber JAR 文件(又名 Fat JAR、Super JAR)的最简单方法 是什么。

我目前正在使用 SBT,但是当我将我的 JAR 文件提交到 Apache Spark 时,我收到以下错误:

Exception in thread "main" java.lang.SecurityException: Invalid signature file digest for Manifest main attributes

或者编译时出现这个错误:

java.lang.RuntimeException: deduplicate: different file contents found in the following:
PATH\DEPENDENCY.jar:META-INF/DEPENDENCIES
PATH\DEPENDENCY.jar:META-INF/MANIFEST.MF

looks like 这是因为我的一些依赖项包括需要在最终 Uber JAR 文件中删除的签名文件 (META-INF)。

我试过这样使用 sbt-assembly 插件:

/project/assembly.sbt

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.12.0")

/project/plugins.sbt

logLevel := Level.Warn

/build.sbt

lazy val commonSettings = Seq(
  name := "Spark-Test"
  version := "1.0"
  scalaVersion := "2.11.4"
)

lazy val app = (project in file("app")).
  settings(commonSettings: _*).
  settings(
    libraryDependencies ++= Seq(
      "org.apache.spark" %% "spark-core" % "1.2.0",
      "org.apache.spark" %% "spark-streaming" % "1.2.0",
      "org.apache.spark" % "spark-streaming-twitter_2.10" % "1.2.0"
    )
  )

当我在 IntelliJ IDEA 中单击“Build Artifact...”时,我得到了一个 JAR 文件。但是我最终遇到了同样的错误...

我是 SBT 的新手,对 IntelliJ 的体验还不够 IDE。

谢谢。

最后我完全跳过使用 IntelliJ IDEA 以避免在我的全局理解中产生噪音:)

我开始阅读 official SBT tutorial

我使用以下文件结构创建了我的项目:

my-project/project/assembly.sbt
my-project/src/main/scala/myPackage/MyMainObject.scala
my-project/build.sbt

在我的 assembly.sbt 文件中添加了 sbt-assembly plugin。允许我构建一个胖 JAR :

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.12.0")

我的最小 build.sbt 看起来像:

lazy val root = (project in file(".")).
  settings(
    name := "my-project",
    version := "1.0",
    scalaVersion := "2.11.4",
    mainClass in Compile := Some("myPackage.MyMainObject")        
  )

val sparkVersion = "1.2.0"

libraryDependencies ++= Seq(
  "org.apache.spark" %% "spark-core" % sparkVersion % "provided",
  "org.apache.spark" %% "spark-streaming" % sparkVersion % "provided",
  "org.apache.spark" %% "spark-streaming-twitter" % sparkVersion
)

// META-INF discarding
mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) =>
   {
    case PathList("META-INF", xs @ _*) => MergeStrategy.discard
    case x => MergeStrategy.first
   }
}

注意% "provided" 表示不在最终的 fat JAR 中包含依赖项(那些库已经包含在我的 workers 中)

注意:META-INF 丢弃 inspired by this answser

注意% and %%

的含义

现在我可以通过 运行 在我的 /my-project 根文件夹中使用 SBT (how to install it) 构建我的胖 JAR:

sbt assembly

我的胖 JAR 现在位于新生成的 /target 文件夹中:

/my-project/target/scala-2.11/my-project-assembly-1.0.jar

希望对其他人有所帮助。


对于那些想要在 IntelliJ 中嵌入 SBT 的人 IDE:How to run sbt-assembly tasks from within IntelliJ IDEA?

将以下行添加到您的 project/plugins.sbt

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.12.0")

将以下内容添加到您的 build.sbt

mainClass in assembly := some("package.MainClass")
assemblyJarName := "desired_jar_name_after_assembly.jar"

val meta = """META.INF(.)*""".r
assemblyMergeStrategy in assembly := {
  case PathList("javax", "servlet", xs @ _*) => MergeStrategy.first
  case PathList(ps @ _*) if ps.last endsWith ".html" => MergeStrategy.first
  case n if n.startsWith("reference.conf") => MergeStrategy.concat
  case n if n.endsWith(".conf") => MergeStrategy.concat
  case meta(_) => MergeStrategy.discard
  case x => MergeStrategy.first
}

Assembly合并策略用于解决创建fat jar时发生的冲突。

在 IntelliJ Idea 中构建 Uber JAR/Fat JAR 的 3 步过程:

Uber JAR/Fat JAR : JAR 文件中包含所有外部库依赖项。

  1. 在 IntelliJ Idea 中添加 SBT Assembly 插件

    转到 ProjectName/project/target/plugins.sbt 文件并添加此行 addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.12.0")

  2. 在build.sbt

    中添加合并、丢弃和不添加策略

    转到 ProjectName/build.sbt 文件并添加 Uber JAR 打包策略

    合并策略:如果两个包中的库版本存在冲突,那么在 Uber JAR 中打包哪个包。
    丢弃策略: 从库中删除一些您不想打包到 Uber JAR 中的文件。
    不要添加策略:不要将某些包添加到 Uber JAR。
    例如:spark-core 已经存在于您的 Spark Cluster.So 我们不应将其打包在 Uber JAR

    合并策略和丢弃策略基本代码:

    assemblyMergeStrategy in assembly := { case PathList("META-INF", xs @ _*) => MergeStrategy.discard case x => MergeStrategy.first }

    因此,您要求使用此命令 MergeStrategy.discard 丢弃 META-INF 文件,对于其余文件,您将使用 首次出现 的库文件(如果有)使用此命令是否有任何冲突 MergeStrategy.first.

    不添加策略基本代码:

    libraryDependencies += "org.apache.spark" %% "spark-core" % "1.4.1" %"provided"

    如果我们不想将 spark-core 添加到我们的 Uber JAR 文件中,因为它已经在我们的 clutser 上,所以我们在它的库依赖项末尾添加 % "provided"

  3. 构建 Uber JAR 及其所有依赖项

    在终端中键入 sbt assembly 用于构建程序包


瞧!构建了 Uber JAR。 JAR 将在 ProjectName/target/scala-XX