Javaから実行可能なJARや実行ファイルをGradleで作る


Gradleでは`application`プラグインを使うことで実行用のスクリプトを生成できるが，この方法では複数のファイルに別れてしまい，使い勝手が悪い．そこで，単一ファイルにしたりインストーラーで簡単に導入できるようにしたファイルの作り方を紹介する．

方法その1 依存するクラスをすべて内包したJARを作る
========

[gradleをつかって依存ライブラリを含む単一で実行可能なjarを生成する](http://qiita.com/mychaelstyle/items/9338f558903ac0c14bc3)などで紹介されているが，このまま使うと，このプロジェクトに依存したプロジェクトを作るとクラスの重複が起こってしまって問題になるので，別のタスクとして定義し，別の名前のファイルになるようにする．

```groovy
apply plugin: 'java'
apply plugin: 'application'

mainClassName = 'info.informationsea.java.excel2csv.Excel2CSV'
def executableBaseName = "excel2csv"

task executableJar(type: Jar, dependsOn: jar)  {
    archiveName = "${executableBaseName}.jar"
    manifest {
        attributes 'Main-Class' : mainClassName
    }

    from configurations.compile.collect {it.isDirectory() ? it : zipTree(it)}
    from "$buildDir/classes/main"
    from "$buildDir/resources/main"
}
```

こうしておくと`${executableBaseName}.jar`が`build/libs`にでき上がる．


方法その1-2 `java -jar`なしで実行可能にする
--------

`java -jar`で指定するJARには先頭に余計なデータがついていても途中のJARの部分から読んでくれるので，ファイルの先頭に

```bash
#!/bin/sh
exec java -jar $0 "$@"
```
と書いて，後ろにJARをくっつけておくとそのまま実行できる（Linux, OS Xの場合）．そのようなファイルを自動生成するタスクは以下の通り．上で説明した`executableJar`タスクが存在していることが前提となっている．

```groovy
task createExecutable(dependsOn: executableJar) << {
    def executableFile = file("$buildDir/deploy/${executableBaseName}")
    executableFile.parentFile.mkdirs()

    def output = new FileOutputStream(executableFile)
    output.write(("#!/bin/sh\n" +
            "\n" +
            "exec java -jar \$0 \"\$@\"\n").bytes)
    output.write file("$libsDir/${executableBaseName}.jar").bytes
    output.close()
    executableFile.executable = true
}
```

方法その1-3 `javapackager`でJREを入りのインストーラーを作る
--------

`javapackager`コマンドを利用するとJREを含んだインストーラーやアプリケーションを作ることができる．これによりGUIのJavaのプログラムを配るときに，JREをインストールさせなくて良いので楽になる．ただし，コマンドラインで使うアプリケーションには使えない．Antタスクでもできるようだが，いまいちやり方が分からないのでコマンドを呼んでいて，そのせいで汎用性に乏しいコードになってしまっている．以下のコードは OS X 専用．

```groovy
task nativePackage(dependsOn: executableJar) << {
    file("$buildDir/deploy").mkdirs()

    exec {
        executable System.getProperty("java.home")+"/../bin/javapackager"
        args "-deploy", "-title", "${project.name}", "-name", project.name, "-appclass", mainClassName,
                "-native", "installer", "-outdir", "$buildDir/deploy", "-outfile", project.name,
                "-srcdir", "$libsDir", "-srcfiles", "${executableBaseName}.jar"
    }
}
```

その他のOSでも同様にコマンドを呼んであげれば良い． OS X 専用になってしまっているのは`javapackager`を探すところだけなので，他のOSでもコマンドの呼び出し方は同じ．`-srcfiles`に指定するJARは必要なファイルがすべて含まれているものにする．

`javapackager`はお仕着せ感の強いアプリケーションなので，インストーラーに利用規約を追加したり，App Store提出用バイナリにPermission追加したりするのは面倒くさそう．

方法その2 Capsuleを使う
========

一部の Class Path をスキャンするようなプログラムでは上で説明したように一つのJARにまとめてしまうと上手く動かない（たとえばSpring）．そこで単一JARの中に複数のJARをまとめて入れて，実行時に展開させることができる．そのためには [Capsule](https://github.com/puniverse/capsule) を使う．

[Gradle Capsule Plugin](https://github.com/danthegoodman/gradle-capsule-plugin)を使うことで，Gradleから簡単に使うことができる．

```groovy
plugins {
    id "us.kirchmeier.capsule" version "0.10.0"
}

apply plugin: 'java'
apply plugin: 'application'

mainClassName = 'info.informationsea.java.excel2csv.Excel2CSV'

task fatCapsule(type: FatCapsule) {
    applicationClass mainClassName
}

task executableCapsule(type: FatCapsule) {
    applicationClass mainClassName
    reallyExecutable
    archiveName = "excel2csv"
}
```

ここでは二つのタスクを定義している．最初の`fatCapsule`は実行可能なJARを作成している．このプログラムは実行時に内部のJARを`~/.capsule/apps`以下に展開して実行する．このときJVMを新たに立ち上げるのでメモリを多く使ったり，場合によっては面倒なことになるかもしれない．

次の`executableCapsule`は上の **方法その1-2 `java -jar`なしで実行可能にする** で説明したスクリプトと同じようなことをするためのもの．`archiveName`を設定することで自由にファイル名を変えられる．