説明
2. Gradle プロジェクト では、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 に追加します。
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) にしておくと便利ですね。