Gradle で Uber Jar(Fat Jar)を作る

blog1.mammb.com


Uber Jar タスクの作成

新規で Uber Jar 作成のタスクを登録。

tasks.register<Jar>("uberJar") {
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    archiveClassifier.set("uber")

    from(sourceSets.main.get().output)

    dependsOn(configurations.runtimeClasspath)
    from({
        configurations.runtimeClasspath.get()
          .filter { it.name.endsWith("jar") }
          .map { zipTree(it) }
    })
}

DuplicatesStrategy では、同じファイルが存在した場合の動作を定義する。通常は以下のいずれかを指定。

  • EXCLUDE : いわゆる先勝ち。同じパスで作成される後続のアイテムを無視して、重複を許さない。
  • INCLUDE : 同じパスで作成されるファイルが存在しても、構わず上書する(Zipファイルの場合、仕様上重複を禁止していないため、重複エントリーが作成され、解凍時に上書きされることになる)

DuplicatesStrategy を指定しない場合は、重複エントリが存在した場合にエラーとなる。 通常は、ファイル重複があった場合にはエラーとして報告を得て、以下のように対象を明示的に除外する対応の方が望ましい。

    from({
        configurations.runtimeClasspath.get()
          .filter { it.name.endsWith("jar") }
          .map { zipTree(it) }
    }) {
        exclude("module-info.class")
    }


以下のようにすれば Uber Jar が作成できる。

$ ./gradlew uberJar


ビルド時に Uber Jar を作成する

assemble の依存に uberJar タスクを追加する。

tasks.register<Jar>("uberJar") {
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    dependsOn(configurations.runtimeClasspath)
    from({
        configurations.runtimeClasspath.get()
          .filter { it.name.endsWith("jar") }
          .map { zipTree(it) }
    })
    mustRunAfter("jar")
}
tasks.assemble {
    dependsOn("uberJar")
}

sourceSets.main のソース・ファイルは既存で含まれるため指定する必要は無い。


Jar タスクで Uber Jar を作成する

既存の Jar タスクで Uber Jar を作成するには以下。

tasks.named<Jar>("jar") {
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    dependsOn(configurations.runtimeClasspath)
    from({
        configurations.runtimeClasspath.get()
            .filter { it.name.endsWith("jar") }
            .map { zipTree(it) }
    })
    from(sourceSets.main.get().output)
}


Uber Jar を実行可能 Jar にする

マニフェストで Main-Class を指定する。

tasks.named<Jar>("jar") {
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    dependsOn(configurations.runtimeClasspath)
    from({
        configurations.runtimeClasspath.get()
            .filter { it.name.endsWith("jar") }
            .map { zipTree(it) }
    })
    from(sourceSets.main.get().output)
    manifest {
        attributes("Main-Class" to "com.example.Main")
    }
}

自動モジュールにする場合は Automatic-Module-Name を指定する。

manifest {
    attributes("Automatic-Module-Name" to "com.example")
}