ことりんと一緒 - 3. 実行可能 JAR

## 説明
[2. Gradle プロジェクト](https://qiita.com/shinyay/items/d2506d3c63a2a750fc67) では、Kotlin を Gradle でビルドするプロジェクトを作成し、実際にビルド＆実行してみました。

ところで、実行の仕方は **gradle run** でした。つまり、Kotlinの標準ライブラリへの参照をもっている Gradle プロジェクトから実行を行っていました。

では、生成された JAR ファイルを直接実行するとどうなっていたでしょうか。試してみます。

```
$ java -jar build/libs/sample-1.0-SNAPSHOT.jar
build/libs/sample-1.0-SNAPSHOT.jarにメイン・マニフェスト属性がありません
```

このようにマニフェスト属性がないというエラーが出力されます。
つまり、JAR ファイルのマニフェストにどのクラスが **MainClass** かという情報が欠落しているのです。

このエラーを解消して **実行可能JAR** を Gradle で作成してみます。

## 前提
次の環境で作業しています。

|項目|内容|
|---|---|
|OS|MacOS|
||Homebrew が使えること|
|JDK|1.8.0_152|
|Gradle|4.3.1|

## 説明
### 1. マニフェストに Main-Class 属性の追加

Gradle の JAR タスクで マニフェストファイルの編集を行います。MainClass がどれか指定したいので **Main-Class** 属性を マニフェストファイルに追加されるようにします。


以下のタスク定義を build.gradle に追加します。

``` build.gradle
jar {
    manifest {
        attributes 'Main-Class': "HelloKotlinKt"
    }
}
```

上記の定義でマニフェストに MainClass が HelloKotlinKt として登録されます。

### 2. マニフェストの確認

ビルド実施して生成された JAR に含まれているマニフェストを確認してみます。

```
$ cat META-INF/MANIFEST.MF
Manifest-Version: 1.0
Main-Class: HelloKotlinKt
```

### 3. Main-Class 属性追加後の実行
実行してみます。

```
$ java -jar build/libs/sample-1.0-SNAPSHOT.jar
Exception in thread "main" java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics
        at HelloKotlinKt.main(HelloKotlin.kt)
Caused by: java.lang.ClassNotFoundException: kotlin.jvm.internal.Intrinsics
        at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        ... 1 more
```

*Caused by: java.lang.ClassNotFoundException: kotlin.jvm.internal.Intrinsics* と、Kotlin のライブラリが見つからなくてエラーが発生しています。

JAR ファイルを中身を確認してみます。

```
$ jar tvf build/libs/sample-1.0-SNAPSHOT.jar
     0 Tue Dec 12 14:02:26 JST 2017 META-INF/
    52 Tue Dec 12 14:02:26 JST 2017 META-INF/MANIFEST.MF
    35 Tue Dec 12 14:02:26 JST 2017 META-INF/sample.kotlin_module
  1005 Tue Dec 12 14:02:26 JST 2017 HelloKotlinKt.class
```

JAR の中に Kotlin のライブラリが含まれていない事が分かります。

### 4. 依存ライブラリの追加
build.gradle の中で Kotlin のライブラリは次のように定義しています。

```
dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}
```

つまり、依存関係でコンパイル・スコープとして定義しています。
このコンパイル時に利用していたライブラリを同一JARに含め、FarJAR　として作成を行うように定義を行います。

JAR タスクに以下を追加します。

```
from {
    configurations.compile.collect {
        it.isDirectory() ? it : zipTree(it)
    }
}
```

### 5. 依存ライブラリ追加後のJARのビルドと中身の確認
JAR ファイルの中身を閲覧してみると、Kotlin のライブラリが含められている事が確認できます。

```
$ jar -tvf build/libs/sample-1.0-SNAPSHOT.jar
     0 Tue Dec 12 14:21:12 JST 2017 META-INF/
    52 Tue Dec 12 14:21:12 JST 2017 META-INF/MANIFEST.MF
    35 Tue Dec 12 14:21:10 JST 2017 META-INF/sample.kotlin_module
  1005 Tue Dec 12 14:21:10 JST 2017 HelloKotlinKt.class
   197 Mon Nov 27 19:00:42 JST 2017 META-INF/kotlin-stdlib-jdk8.kotlin_module
     0 Mon Nov 27 19:00:42 JST 2017 kotlin/
     0 Mon Nov 27 19:00:42 JST 2017 kotlin/collections/
     0 Mon Nov 27 19:00:42 JST 2017 kotlin/collections/jdk8/
  2132 Mon Nov 27 19:00:42 JST 2017 kotlin/collections/jdk8/CollectionsJDK8Kt.class
     0 Mon Nov 27 19:00:42 JST 2017 kotlin/internal/
     0 Mon Nov 27 19:00:42 JST 2017 kotlin/internal/jdk8/
  2218 Mon Nov 27 19:00:42 JST 2017 kotlin/internal/jdk8/JDK8PlatformImplementations.class
     0 Mon Nov 27 19:00:42 JST 2017 kotlin/streams/
     0 Mon Nov 27 19:00:42 JST 2017 kotlin/streams/jdk8/
  1745 Mon Nov 27 19:00:42 JST 2017 kotlin/streams/jdk8/StreamsKt$asSequence$$inlined$Sequence$1.class
  1856 Mon Nov 27 19:00:42 JST 2017 kotlin/streams/jdk8/StreamsKt$asSequence$$inlined$Sequence$2.class
  1855 Mon Nov 27 19:00:42 JST 2017 kotlin/streams/jdk8/StreamsKt$asSequence$$inlined$Sequence$3.class
  1869 Mon Nov 27 19:00:42 JST 2017 kotlin/streams/jdk8/StreamsKt$asSequence$$inlined$Sequence$4.class
  1417 Mon Nov 27 19:00:42 JST 2017 kotlin/streams/jdk8/StreamsKt$asStream$1.class
  5018 Mon Nov 27 19:00:42 JST 2017 kotlin/streams/jdk8/StreamsKt.class
     0 Mon Nov 27 19:00:42 JST 2017 kotlin/text/
     0 Mon Nov 27 19:00:42 JST 2017 kotlin/text/jdk8/
  1580 Mon Nov 27 19:00:42 JST 2017 kotlin/text/jdk8/RegexExtensionsJDK8Kt.class
   246 Mon Nov 27 18:49:30 JST 2017 META-INF/kotlin-runtime.kotlin_module
  2190 Mon Nov 27 18:50:20 JST 2017 META-INF/kotlin-stdlib.kotlin_module
   743 Mon Nov 27 18:49:30 JST 2017 kotlin/ArrayIntrinsicsKt.class
  1322 Mon Nov 27 18:49:30 JST 2017 kotlin/Deprecated.class
  1258 Mon Nov 27 18:49:30 JST 2017 kotlin/DeprecationLevel.class
   965 Mon Nov 27 18:49:30 JST 2017 kotlin/DslMarker.class
   343 Mon Nov 27 18:50:08 JST 2017 kotlin/ExceptionsKt.class
  2595 Mon Nov 27 18:50:08 JST 2017 kotlin/ExceptionsKt__ExceptionsKt.class
   739 Mon Nov 27 18:49:30 JST 2017 kotlin/ExtensionFunctionType.class
   414 Mon Nov 27 18:49:30 JST 2017 kotlin/Function.class
  1437 Mon Nov 27 18:50:20 JST 2017 kotlin/InitializedLazyImpl.class
   476 Mon Nov 27 18:49:32 JST 2017 kotlin/KotlinNullPointerException.class
   912 Mon Nov 27 18:50:20 JST 2017 kotlin/KotlinVersion$Companion.class
  4039 Mon Nov 27 18:50:20 JST 2017 kotlin/KotlinVersion.class
 :
 :
```

### 6. 依存ライブラリ追加後の実行
最後に、生成された JAR ファイルを実行してみます。

```
$ java -jar build/libs/sample-1.0-SNAPSHOT.jar
Hello, Kotlin!
```

エラーなく出力することができました。


## まとめ
Gradle で生成する JAR ファイルをどこでも実行できるように実行可能形式のFatJAR (UberJAR) にしておくと便利ですね。