sbt-assembly で依存ライブラリを含んだ über-jar を生成する
scalasbt-assembly は依存ライブラリを含んだ über-jar (fat-jar) を生成するためのプラグイン。
$ cat peojcts/plugins.sbt
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.1")
$ sbt assembly
$ java -jar ./target/scala-2.13/sbt-assembly-test-0.1.0-SNAPSHOT.jar
Hello world!
依存ライブラリの依存先も含めて複数の JAR が同じパスのファイルを含み、その中身が異なっている場合、Deduplicate found different file contents エラーになってしまうので、 その際は assemblyMergeStrategy でそれをどのように解決するかを設定する。
$ cat build.sbt
...
libraryDependencies += "software.amazon.awssdk" % "s3" % "2.20.14"
assembly / assemblyMergeStrategy := {
case PathList("META-INF", xs @ _*) => MergeStrategy.discard
case x =>
val oldStrategy = (ThisBuild / assemblyMergeStrategy).value
oldStrategy(x)
}
数が多く解決が面倒な場合は assemblyExcludedJars で über-jar からは除きクラスパスに配置して実行時にロードさせることもできるが、結局互換性のためにエラーになったりする可能性がある。
assembly / assemblyExcludedJars := {
(assembly / fullClasspath).value.filter { _.data.getName.contains("testlib") }
}
val outputExcludedJar = taskKey[Seq[File]]("Output jars that is excluded from the uber jar")
outputExcludedJar := {
val targetDir = (assembly / Keys.target).value
val assemblyJAR = (assembly / assemblyOutputPath).value
val libraryJARs = (assembly / update).value matching configurationFilter(Runtime.name)
val exludedJARs = libraryJARs.filter { jar => (assembly / assemblyExcludedJars).value.map(_.data).contains(jar) }
IO.createDirectory(targetDir)
exludedJARs.foreach { jar =>
IO.copyFile(jar, targetDir / jar.getName)
}
Seq(assemblyJAR) ++ (targetDir ** "*.jar").get
}
assembly := assembly.dependsOn(outputExcludedJar).value
また、実行環境に一部のライブラリが存在している場合は、provided を付けることで JAR から除き、 さらに次のように書くことでローカルでの sbt run 時には含めることができる。
$ cat build.sbt
...
libraryDependencies += "software.amazon.awssdk" % "s3" % "2.20.14" % "provided"
Compile / run := Defaults.runTask(Compile / fullClasspath, Compile / run / mainClass, Compile / run / runner).evaluated
$ sbt run