The Badass Runtime Plugin+Gradle+JPackageで自己完結型Javaアプリケーションインストーラを作る&パスも通す

# はじめに
どーもnaotikiKtです。とあるコンソールアプリにJavaを同梱して配布したくなったのでその時のハマりポイントとかを備忘録的な感じで書いておきます。

**本記事はKotlinユーザーの方向けです。（Javaの方でもほとんど同じです。）**
# JPackageて何？
JDK 14から含まれるようになったJavaを同梱した様々なプラットフォームのネイティブインストーラを作成できるツールです。
もとはjavafxpackager(JDK 7),javapackager(JDK 11)て名前だったらしいです。

https://docs.oracle.com/javase/jp/14/jpackage/index.html

# 環境
Windows,Linux両方のインストーラーを作りたいので両方の環境を用意する必要があります。

```text:Windowsの環境
OS: Windows 11
JDK: Amazon Corretto Version 17.0.3 (JDK 14以上が必要)
IDE: InteliJ IDEA 2022.1.3
```

```text:Ubuntu(WSL)の環境
WSL: WSL2 Ubuntu
JDK: OpenJDK 17(≧JDK 14)
```
~~macOS？ﾅﾆｿﾚｵｲｼｲﾉ？~~
# The Badass Runtime Pluginを導入
Docs

https://badass-runtime-plugin.beryx.org/releases/latest/

Github

https://github.com/beryx/badass-runtime-plugin

build.gradle.ktsにこんな感じで追記してください
```diff_kotlin:build.gradle.kts
plugins {
+    id("org.beryx.runtime") version "1.12.7"
}
+ runtime {
+    jpackage {
+        //この中に色々書きます        
+    }
+ }
```
次はjpackageブロックの書き方を説明します
# jpackageブロック
jpackageブロック以下で使えるものの中からよく使いそうなものを説明します。
## `mainClass`
メインクラスを指定します。
Kotlinで
```kotlin
fun main(args: Array<String>) {
    //処理
}
```
このように書いていたらクラス`MainKt`が生成されるので、`mainClass="MainKt"`と書きましょう。
## `imageName`/`installerName`
その名の通り、プログラムのファイル名を指定できます。
## `outputDir`
出力先を変更できます。
## `skipInstaller`
インストーラーなど要らん！わしはプログラムとJavaが同梱されたフォルダだけほしいんじゃ！って方向けのオプションです。
## `installerOptions`
jpackageにわたす引数を設定できます
## `installerType`
インストーラの形式(deb,rpm,exe,msiとか)を指定します。
## `resourceDir`
インストーラを作成するときの設定ファイルなどをオーバーライドできます。
詳しくは後ほど
# プラットフォームごとに処理を分ける
ビルド時のOSは`org.gradle.internal.os.OperatingSystem.current()`で取得できます。
使用例↓
```kotlin
jpackage {
    val currentOs = org.gradle.internal.os.OperatingSystem.current()
    when{
        currentOs.isWindows->{
            resourceDir=file("$rootDir/res/windows")
            outputDir="jpackage/windows"
        }
        currentOs.isLinux->{
            resourceDir=file("$rootDir/res/linux")
            outputDir="jpackage/linux"
        }
    }
}
```
次はそれぞれのOSごとの詳しい設定をしてみましょー
## Windows編
### コンソールアプリを作っている方へ
脳死でもなんでもいいので
```kotlin
imageOptions.add( "--win-console")
```
をjpackageブロックに追記しましょう。
これがないとなんかエラー吐いてきます。

---
## Linux(主にUbuntu)編
`installerType`が自動でdebにならないので
```kotlin
installerType="deb"
```
しましょう
# ビルド！
`./gradlew jpackage`
成功したら成功です。~~(某構文)~~
# インストール時にパスを通す
Pathとか環境変数設定したくなりますよね！
私はなりました。設定しましょう。
## 共通部分
`resourceDir`でファイルをオーバーライドする必要があるのでもとのファイルを取得しましょう。
```diff_kotlin
runtime {
    jpackage {
+        installerOptions.add("--verbose")
+        installerOptions.add("--temp")
+        installerOptions.add("HogeHoge")
    }
}
```
`--verbose`で詳細のログ取得
`--temp HogeHoge`でHogeHogeディレクトリ内に設定ファイルを出力します。
オプションを追記したら`./gradlew jpackage`しましょう。
`HogeHoge/`になんか色々出力されましたね。
Windowsの場合はconfigディレクトリ,Linux(Ubuntu)はimages/DEBIAN/にオーバーライドできるファイルが入っています。
[jpackageリソースのオーバーライド JPackage Docs](https://docs.oracle.com/javase/jp/14/jpackage/override-jpackage-resources.html#GUID-1B718F8B-B68D-4D46-B1DB-003D7729AAB6)
今回使うファイルは、
Windowsなら`main.wxs`
Linuxなら`postinst`,`postrm`です
適当なディレクトリ(`res`とします)を作ってそれらのファイルを入れましょう。
res内のファイルを変更していきます。
私の場合はプラットフォームごとに分けて入れています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357553/a75cb3fb-3830-a9f8-6b2d-acc95ec1ce6f.png)
build.gradle.ktsにも追記します。
```diff_kotlin:build.gradle.kts
jpackage {
    when {
        currentOs.isWindows -> {
+           resourceDir = file("$rootDir/res/windows")
            imageOptions.add("--win-console")
            outputDir = "jpackage/windows"
        }
        currentOs.isLinux -> {
+           resourceDir = file("$rootDir/res/linux")
            installerType = "deb"
            outputDir = "jpackage/linux"
        }
    }
}
```
これで準備おーけーです
## Windows編
main.wxsはWiX Toolset用のファイルで、環境変数を変更する機能があります。
main.wxsに
```xml
<Feature Id="DefaultFeature" Title="!(loc.MainFeatureTitle)" Level="1">
    <ComponentGroupRef Id="Shortcuts"/>
    <ComponentGroupRef Id="Files"/>
    <ComponentGroupRef Id="FileAssociations"/>
</Feature>
```
こんなのありますよね？(なかったら、、、えー、頑張ってください)
追記します
```diff_xml
<Feature Id="DefaultFeature" Title="!(loc.MainFeatureTitle)" Level="1">
    <ComponentGroupRef Id="Shortcuts"/>
    <ComponentGroupRef Id="Files"/>
    <ComponentGroupRef Id="FileAssociations"/>
+   <Component Id="pathEnvironmentVariable" Guid="{YOUR_GUID}" KeyPath="yes" Directory="TARGETDIR">
+       <Environment Id="UpdatePath" Name="Path" Value="[INSTALLDIR]" Action="set" System="yes"  Part="last" Separator=";" />
+   </Component>
</Feature>
```
`{YOUR_GUID}`には[こんなツール](https://www.guidgen.com/)を使って生成したGUIDで置き換えましょう。
これで環境変数`PATH`にインストールパスが追記されます。わーい
#### `Environment`の説明
`Name`を変更すればPath以外の環境変数も変えられます。
`Value`は設定するパスですね。この場合はインストール先が自動で設定されます。
`System`はyesにするとシステム環境変数に設定されます。
`Part`で全体を変更するかどこに追記するかを設定できます。
`Permanent`をyesにするとアンインストールしても残ります。
他にもあるので詳しくは[こちら](https://wixtoolset.org/documentation/manual/v3/xsd/wix/environment.html)
## Linux編
/usr/bin下にシンポジックリンクを作ることにしました。
他にいいやり方あったら教えて下さい。
先程resに入れた、
postinstはインストール時に実行されるシェルスクリプト
postrmはアンインストール時に実行されるシェルスクリプトです。
(更新時も実行されるのかな？あんま詳しくないからよく知りません)
```shell:postinst
#!bin/sh

ln -s /opt/{アプリ名}/{実行ファイル} /usr/bin/{実行ファイル名}
exit 0
```
```shell:postrm
#!bin/sh

rm /usr/bin/{実行ファイル名}
exit 0
```
`{}`の部分は適宜置き換えてください。
{アプリ名}はデフォルトではsettings.gradleの`rootProject.name`だと思います。
[オプションが指定されていない場合のデフォルト JPackage Docs](https://docs.oracle.com/javase/jp/14/jpackage/basic-packaging.html#GUID-EB1303BE-292F-4FEE-B91E-3C9CC367324B)
postinstで/usr/binにシンポジックリンクを作成し、postrmで削除しています。

ビルドした.debファイルを
`sudo apt install ./hoge.deb`
or
`dpkg -i ./hoge.deb`
でインストールできます。

# おわり
日本語記事が少ない気がしたので書いてみました。
思ったより長くなりましたね・・・
間違いは指摘してくださると幸いです。