sbt-pack和sbt-assembly之间的主要区别是什么?

时间:2014-03-21 10:41:37

标签: sbt sbt-assembly

我偶然发现了sbt-pack插件。开发流似乎steady。令我感到惊讶的是,我认为(引用sbt-pack标题)"创建可分发的Scala包的唯一插件。" 是{{ 3}}(在其他功能中)。

插件之间的主要区别是什么?我何时应该使用另一个?

2 个答案:

答案 0 :(得分:63)

(免责声明:我维持sbt-assembly)

SBT-组件

sbt-assembly创建一个胖JAR - 一个包含代码和库中所有类文件的JAR文件。通过演化,它还包含在多个JAR提供相同文件路径(如config或README文件)时解决冲突的方法。它涉及解压缩所有库JAR,因此它有点慢,但这些都是高度缓存的。

SBT-包

sbt-pack保持所有库JAR完整无缺,将它们移动到target/pack目录(而不是通常存在的常春藤缓存),并为您创建一个shell脚本来运行它们。

SBT-本机打包器

sbt-native-packager与sbt-pack类似,但它是由sbt提交者Josh Suereth启动的,现在由高能力Nepomuk Seiler(也称为muuki88)维护。该插件支持多种格式,如Windows msi文件和Debian deb文件。最近添加的内容是对Docker images的支持。

所有这些都是创建部署映像的可行方法。在某些情况下,例如将您的应用程序部署到Web框架等,如果您处理的是一个文件而不是十几个文件,则可能会使事情变得更容易。

荣誉奖:sbt-progardsbt-onejar

答案 1 :(得分:8)

尽管 Eugene Yokota 的解释是完整的,但我还是要在用法和如何产生不同结果方面,使用package命令解释提到的插件。

目录设置和build.sbt

lazy val commonSettings = Seq(
  organization := "stackOverFlow",
  scalaVersion := "2.11.12",
  version := "1.0",
)

lazy val app  = (project in file ("app")).
  enablePlugins(PackPlugin).
  settings(commonSettings)

build.sbt 文件上方声明名为app的项目,并将所有源文件包含在app目录中。要启用Pack插件,enablePlugins(PackPlugin)应该包含在sbt文件中。

此外,我将以下行放在 project / plugins.sbt 文件中,以在我们的项目中使用包插件

addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.9.3")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5")

默认情况下,该软件包已集成到sbt中,因此您不必使用addSbtPlugins显式指定插件。但是,默认情况下,sbt中不包含sbt-pack和sbt-assembly插件,因此您必须指定要使用它们。 addSbtPlugin是对您的sbt说“我想在我的项目中使用xxx,yyy插件”的一种方式。

此外,我在./app/src/main/scala中实现了两个人为的scala文件:

AppBar.scala

class AppBar {
  def printDescription() = println(AppBar.getDescription)
}

object AppBar {
  private val getDescription: String = "Hello World, I am AppBar"

  def main (args: Array[String]): Unit = {
    val appBar = new AppBar
    appBar.printDescription()
  }
}

AppFoo.scala

class AppFoo {
  def printDescription() = println(AppFoo.getDescription)
}

object AppFoo {
  private val getDescription: String = "Hello World, I am AppFoo"

  def main (args: Array[String]): Unit = {
    val appFoo = new AppFoo
    appFoo.printDescription()
  }
}

sbt软件包

这是sbt中包含的非常基本的sbt命令,可帮助您通过jar文件分发项目。 package命令生成的jar文件位于projectDirectoy / target / scala-2.11 / app_2.11-1.0.jar中(此处,build.sbt文件中包含的指定scalaVersion和版本设置键用于生成jar。文件名)。

当您查看jar时,可以看到sbt工具生成的类文件,这是在app / src / main / scala中编译源代码的结果。此外,它包括一个清单文件。

$vi  projectDirectoy/target/scala-2.11/app_2.11-1.0.jar

META-INF/MANIFEST.MF
AppBar$.class
AppBar.class
AppFoo.class
AppFoo$.class

请注意,它仅包含从位于app / src / main / scala目录中的scala文件生成的类文件。 package命令生成的jar文件不包含任何与Scala相关的库,例如scala库中的collection(例如collection.mutable.Map.class)。因此,执行该程序可能需要scala库,因为generate jar文件仅包含从我实现的scala源生成的最小类。这就是jar文件包含AppBar.class,伴随对象的AppBar $ .class等的原因。

sbt-assembly

正如Eugene Yokota所说,sbt-assembly还可以通过生成jar文件来帮助您分发项目。但是,生成的jar文件不仅包括源代码生成的类文件,还包括执行程序所需的所有库。例如,要执行AppFoo对象中定义的主要功能,您可能需要scala库。另外,当您在项目中添加外部库时,可以通过将依赖项添加到libraryDependencies键来将其包括在内。

libraryDependencies ++= Seq("org.json4s" %% "json4s-jackson" % "3.5.3")

例如,您可以在项目中包括json4s库,并且与项目中支持json4s相关的jar文件也将添加到sbt-assembly生成的最终jar文件中。换句话说,当您在sbt中调用程序集时,它将生成一个jar文件,其中包含执行程序的所有要求,因此您无需其他依赖项即可执行程序。

在sbt shell中提示汇编命令时,它将在目标目录中生成一个jar文件。在这种情况下,您可以在app / target / scala-2.11目录中找到app-assembly-1.0.jar。当您查看jar文件时,您会发现它包含很多类。

$vi  projectDirectoy/target/scala-2.11/app_2.11-1.0.jar
ETA-INF/MANIFEST.MF
scala/
scala/annotation/
scala/annotation/meta/
scala/annotation/unchecked/
scala/beans/
scala/collection/
scala/collection/concurrent/
scala/collection/convert/
scala/collection/generic/
scala/collection/immutable/
scala/collection/mutable/
scala/collection/parallel/
scala/collection/parallel/immutable/
scala/collection/parallel/mutable/
scala/collection/script/
scala/compat/
scala/concurrent/
scala/concurrent/duration/
scala/concurrent/forkjoin/
scala/concurrent/impl/
scala/concurrent/util/
scala/io/
scala/math/
scala/ref/
scala/reflect/
scala/reflect/macros/
scala/reflect/macros/internal/
scala/runtime/
scala/sys/
scala/sys/process/
scala/text/
scala/util/
scala/util/control/
scala/util/hashing/
scala/util/matching/
AppBar$.class
AppBar.class
AppFoo$.class
AppFoo.class
......

如前所述,由于程序集生成的jar文件包含所有依赖关系(例如scala和外部库)以在jar中执行程序,因此您可能会认为可以调用AppFoo对象和AppBar中定义的主要函数宾语。

jaehyuk@ubuntu:~/work/sbt/app/target/scala-2.11$ java -cp './*' AppFoo
Hello World, I am AppFoo
jaehyuk@ubuntu:~/work/sbt/app/target/scala-2.11$ java -cp './*' AppBar
Hello World, I am AppBar

是的,您可以使用生成的jar文件执行main函数。

sbt-pack

sbt-pack与sbt-assembly几乎相同;它将项目依赖的所有库保存为执行程序所需的jar文件。但是,sbt-pack不会将所有依赖项集成到一个jar文件中,而是会生成多个jar文件,这些jar文件对应于一个库依赖项和您的类(例如AppFoo.class)。

此外,有趣的是,它会自动生成脚本,以调用scala源文件和Makefile中定义的所有主要功能来安装程序。让我们看一下在sbt shell上提示pack命令后创建的pack目录。

jaehyuk@ubuntu:~/work/sbt/app/target/pack$ ls
bin  lib  Makefile  VERSION
jaehyuk@ubuntu:~/work/sbt/app/target/pack$ ls bin/
app-bar  app-bar.bat  app-foo  app-foo.bat
jaehyuk@ubuntu:~/work/sbt/app/target/pack$ ls lib/
app_2.11-1.0.jar  sbt_2.12-0.1.0-SNAPSHOT.jar  scala-library-2.11.12.jar
jaehyuk@ubuntu:~/work/sbt/app/target/pack$ 

如上所示,创建了两个目录和两个文件。 bin包含执行源代码中定义的功能的所有脚本文件(每个文件都是一个脚本,可帮助您执行scala文件中定义的主要方法); lib包含执行程序所需的所有jar文件;最后,Makefile可用于在系统中安装程序和相关库。

有关详细信息,请参阅每个插件的github页面。