Kotlinの複数のmainからそれぞれJarを作るGradle


ちょっとしたツールもKotlinで書きたい

自分で書いて自分で使う分にはIDEからでもちょちょいと動かせばそれでいいんだけど、提供する時ちょっと面倒臭い。

ほんで小さいツールでいちいちプロジェクト作らないし、もうなんかそういうごった煮みたいなプロジェクトにエントリーポイントだけ増やしてやっちゃうよね。

そのプロジェクトって大抵はある意味超便利な魔境化しちゃうんだけどね。いろんな依存関係が既に書いてあるから、その辺のDBにすぐSQL流し込めるようになってたりする。いろんな魔法が使えてデンジャラス且つコンビニエンス。

という事で、このfun main()をJarにしたろとかいう事をやってみた。

まずはソースファイル

beatles/George.kt
package beatles

fun main() {
    println("I'm George Harrison.")
}
beatles/John.kt
package beatles

fun main() {
    println("I'm John Lennon.")
}
beatles/Paul.kt
package beatles

fun main() {
    println("I'm Paul McCartney.")
}
beatles/Ringo.kt
package beatles

fun main() {
    println("I'm Ringo Starr.")
}

この4人をそれぞれJarにしてみるざんす。

ではGradle

build.gradle
plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.4.10'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib"
}

task George(type: Jar) {
    def name = 'George'
    archiveFileName = "${name}.jar"
    from sourceSets.main.output
    manifest {
        attributes 'Main-Class': "beatles.${name}Kt"
        attributes 'Class-Path': configurations.runtimeClasspath.collect { 'libs/' + it.name }.join(' ')
    }
}

task John(type: Jar) {
    def name = 'John'
    archiveFileName = "${name}.jar"
    from sourceSets.main.output
    manifest {
        attributes 'Main-Class': "beatles.${name}Kt"
        attributes 'Class-Path': configurations.runtimeClasspath.collect { 'libs/' + it.name }.join(' ')
    }
}

task Paul(type: Jar) {
    def name = 'Paul'
    archiveFileName = "${name}.jar"
    from sourceSets.main.output
    manifest {
        attributes 'Main-Class': "beatles.${name}Kt"
        attributes 'Class-Path': configurations.runtimeClasspath.collect { 'libs/' + it.name }.join(' ')
    }
}

task Ringo(type: Jar) {
    def name = 'Ringo'
    archiveFileName = "${name}.jar"
    from sourceSets.main.output
    manifest {
        attributes 'Main-Class': "beatles.${name}Kt"
        attributes 'Class-Path': configurations.runtimeClasspath.collect { 'libs/' + it.name }.join(' ')
    }
}

task beatles(dependsOn: ['George', 'John', 'Paul', 'Ringo']) doLast {
}

task copyDependencies(type:Copy) {
    def libs = 'build/libs/libs'
    new File(libs).mkdirs()
    copy {
        from configurations.runtimeClasspath
        into libs
    }
    FileCollection copyFiles = files { file(libs).listFiles() }
    copyFiles.each {File file -> println file.name}
}

4人分のタスクを用意しましたよ、と。
実際はfrom sourceSets.main.outputあたりでパッケージとか絞ったほうがいいでしょうね。関係ある奴だけ入れる感じで。

マニフェストにクラスパスだけ追加。

で、copyDependenciesってタスクだけど、要するに./build/libs/libsに依存関係のJar全部突っ込んどいて、さすがにここだけは別配布するのね。Jarに依存関係突っ込んだらサイズ爆発するんでね。

動かしましょ

./gradlew beatles
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2020/10/07     13:49                libs
-a----       2020/10/07     14:13           2888 George.jar
-a----       2020/10/07     14:12           2886 John.jar
-a----       2020/10/07     14:13           2886 Paul.jar
-a----       2020/10/07     14:13           2887 Ringo.jar

はい、こういう3KB弱の軽いJarがちゃんとできましたと。

PS> java -jar George.jar
I'm George Harrison.
PS> java -jar John.jar
I'm John Lennon.
PS> java -jar Paul.jar
I'm Paul McCartney.
PS> java -jar Ringo.jar
I'm Ringo Starr.

これで配布もなんかオートマチックにしたりとか、工夫していったら便利になりそう。