java&kotlinをオフラインでビルドする環境を作ったときのログ [gradle.kts][maven][shadowJar][IDEA]


# あらまし

- Kotlin版Gradle 7.1.1触った
- `src/main/java`にJavaコードとKotlinコードを混在させて双方向呼び出し
- Mavenリポジトリを推移的依存性ごと全部お取り寄せするのに奮闘した挙句最終的に`mvn dependency:copy-dependencies`した
- gradle.ktsでshadowJar 7.0.0を使ってfat jarを作った
    - おまけで選択的同封もした
- 成果物を公開した
  - GitHub Actionsの中でビルド＆実行もできた

https://github.com/MirrgieRiana/java-kotlin-project-sample

# 背景

通信速度がときどき激烈に遅くなる場所にあるRaspberry Pi端末上でGradleでのjavaのビルドをしようとしたら、依存jarのダウンロードだけで非常に時間がかかってしまうという問題を、知っていたけど放置していました。

それはそれとして、自分がGroovy版からKotlin版のgradleに移住して色んなプロジェクトのビルドスクリプトを最初から書き直す機会があったので、この際にオフラインビルドもできるようにしました。

重い依存jarをgitリポジトリ内に含めてしまい、最初から持たせた状態で戦地に送り出そうという作戦です。

# 環境

- コーディング環境
    - Windows 10
        - WSL
            - openjdk 11.0.11
            - javac 1.8.0_292
            - Apache Maven 3.6.3
        - IntelliJ IDEA (Android Studio 4.2.1)
            - ※Androidは今回関係ないです
- 実行環境
    - Raspberry Pi 4
        - java 8
- ビルド環境
    - Gradle 7.1.1
    - Kotlin 1.4.31

# 手順

## 作業ディレクトリ

適当な場所に作業ディレクトリを作って、その中でbashを開いて作業をします。

```bash
$ mkdir hoge
$ cd hoge
```

以下、`$`で始まるコマンドはWSL上のbashのコマンドで、`$`の前はカレントディレクトリを表します。

## 最新のgradlewの取得

`gradle download`とかでググるとたどり着けるページから、gradleの本体のビルド済みのバイナリを取得して、ついでに展開もします。

https://gradle.org/releases/

```bash
hoge$ wget https://services.gradle.org/distributions/gradle-7.1.1-bin.zip
hoge$ unzip gradle-7.1.1-bin.zip
```

これで、`hoge/gradle-7.1.1/bin/gradle`にgradleの実行ファイルが出現しました。

## プロジェクトの生成

プロジェクトのディレクトリを生成してその中に移動します。

```bash
hoge$ mkdir project1
hoge$ cd project1
```

## gradlewの生成

gradle本体のコマンドからgradlewを生成します。

### ダミービルドスクリプトの用意

gradleでwrapperコマンドを実行するにはビルドスクリプトがないといけないため、まずダミーのビルドスクリプトを書きます（【TODO】ビルドスクリプトなしで実行する方法はありそう）。

```bash
hoge/project1$ touch build.gradle.kts
```

### gradlewの生成

```bash
hoge/project1$ ../gradle-7.1.1/bin/gradle wrapper
```

これにより、次のファイルおよびディレクトリが出現します。

- `.gradle`
- `gradle/`
- `gradlew`
- `gradlew.bat`

## gradlewを起動してみる

```bash
hoge/project1$ bash gradlew
```

```:結果
Downloading https://services.gradle.org/distributions/gradle-7.1.1-bin.zip
..........10%...........20%...........30%..........40%...........50%...........60%..........70%...........80%...........90%...........100%

> Task :help

Welcome to Gradle 7.1.1.

To run a build, run gradlew <task> ...

To see a list of available tasks, run gradlew tasks

To see a list of command-line options, run gradlew --help

To see more detail about a task, run gradlew help --task <task>

For troubleshooting, visit https://help.gradle.org

BUILD SUCCESSFUL in 5s
1 actionable task: 1 executed
```

これで、この時点で単体で正常に動作するgradlewが手に入っていることが分かりました。

単体で動作するといっても、冒頭の`Downloading https://services.gradle.org/distributions/gradle-7.1.1-bin.zip`でダウンロードしたはずのファイルが`hoge/project1`以下の何処にも見当たらないため、おそらく`~/.gradle`か何かが何らかの影響を受けていると思います。

## プロジェクトをIDEAで開く

プロジェクトディレクトリはもうIDEAで開くことができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/bb8ad36e-2326-72e3-a9e2-3de0f96a8844.png)

## オフラインでjavaのコンパイルをしてみる

### ビルドスクリプトの編集

ビルドスクリプトにjavaをコンパイルするためのプラグインを追加します。

```kotlin:hoge/project1/build.gradle.kts
plugins {
    application
}
```

ビルドスクリプト上ではIDEAの補完が利きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/bbd81958-1bed-3d1e-9dff-171f65bfbf0a.png)

### gradleタスクの実行

jarを作ってみます。

----

IDEAでは次のボタンからgradleコマンドを実行できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/43498a33-2e91-1d27-d716-5fbc46268ec2.png)

----

次のトグルボタンをONにすると、gradleがタスクの実行中にインターネットに接続しないようです（ただしgradlewがgradle本体をダウンロードする部分は防げない模様）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/6599a63a-cd0a-a0d0-47d2-02d8ef063306.png)

----

jarタスクを実行してjarファイルを作ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/69d8e361-6e8b-8dad-9cf2-5e6255276cfd.png)

この呼び出しはコンソール上ではおそらく次のようになります。

```bash
hoge/project1$ bash gradlew jar --offline
```

```:結果
BUILD SUCCESSFUL in 606ms
1 actionable task: 1 executed
```

これにより、`hoge/project1/build/libs/project1.jar`が出現しました。

ソースコードが全くないため中身はメタ情報しかありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/b8b1ff11-5f37-5339-71e6-e32e9c098ae7.png)

### Javaソースファイルを追加してみる

次のファイルを作って再びjarを生成します。

```java:hoge/project1/src/main/java/Main.java
public class Main {
    public static void main(String[] args) {
        System.out.println("Main");
    }
}
```

----

jarの中にはMainクラスが現れました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/a6ee395d-9670-da64-4ab5-5b89ec0268d3.png)

----

実行もできます。

```bash
hoge/project1$ java -cp build/libs/project1.jar Main
Main
```

## mavenの依存性を追加してみる

JUnit 4.13.2を依存性に追加してみます。

https://mvnrepository.com/artifact/junit/junit/4.13.2

```kotlin:hoge/project1/build.gradle.kts(追加)
dependencies {
    implementation("junit:junit:4.13.2")
}
```

jarの中に含める実験のために、testImplementationではなく敢えてimplementationコンフィギュレーションに追加しています（jarタスクはtestImplementationコンフィギュレーションへの依存がない）。

----

こうすると、次のように依存性の解決ができずビルドが失敗します。

```
Execution failed for task ':compileJava'.
> Could not resolve all files for configuration ':compileClasspath'.
   > Cannot resolve external dependency junit:junit:4.13.2 because no repositories are defined.
     Required by:
         project :

Possible solution:
 - Declare repository providing the artifact, see the documentation at https://docs.gradle.org/current/userguide/declaring_repositories.html
```

これは、ビルドスクリプト上でリポジトリが定義されていないからであり、そのためオフラインモードでなくても発生します。

----

これを解決するにはビルドスクリプト上でmavenセントラルリポジトリを登録します。

```kotlin:hoge/project1/build.gradle.kts
plugins {
    application
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("junit:junit:4.13.2")
}
```

これをすることでオフラインでのビルドが出来なくなりました（実は何も書かなくてもデフォルトでGradle Pluginsへの依存性があるのでオフラインでのビルドはできない）。
オンライン状態では問題なくコンパイルできますが、オフライン状態ではローカル（Windowsの場合は
`%HOMEPATH%\.gradle\caches\modules-2\files-2.1\`の下）にキャッシュがない限り失敗します。

----

目標はリポジトリの指定を次のようにプロジェクト内のローカルディレクトリにしてビルドおよび実行ができることです。

```kotlin
repositories {
    maven("./maven")
}
```

## maven依存関係をオフライン化するには

### 作戦1　依存jarをローカルディレクトリに入れて参照する

次のサイトの方法を試しました。

https://wadahiro.hatenablog.com/entry/20120324/1332562767

結果、成果物を作るためのコンパイル環境の依存性はこれで解決できましたが、のちの段階でkotlinのjavaクラスファイルへのコンパイルに使用するgradleプラグインの指定時にモジュールではなくファイル名指定でのやり方が分からず、断念しました。

```kotlin
plugins {
    id("org.jetbrains.kotlin.jvm") version "1.4.31"
}
```

プラグインを指定するときは`PluginDependenciesSpecScope#id(String)`関数を呼び出していますが、このときの引数はこのあと`DefaultPluginId#of(String)`に持っていかれ、異常なフォーマットをしていないか確認されます。
そして、その中でプラグインIDは正規表現で表すと、`[a-zA-Z0-9.\-_]`の文字しか受け付けません。
ディレクトリの区切り文字などを入れた場合、InvalidPluginIdExceptionが発生します。

つまりプラグインIDの指定段階でローカルファイルを指定することはできないようです。

----

次に`settings.gradle.kts`内でresolutionStrategyを使って`org.jetbrains.kotlin.jvm`というプラグインIDに特定のローカルファイルを関連付けるということを試みました。

https://uzzu.co/post/new-gradle-plugin-api-management.html

次のようにモジュール指定になっている記述を書き換えてファイル名指定にできればゴールです。

```kotlin
pluginManagement {
    resolutionStrategy {
        eachPlugin {
            when (requested.id.id) {
                "org.jetbrains.kotlin.jvm" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}")
            }
        }
    }
}
```

ここで、`PluginResolveDetails#useModule(notation : Object)`の中を辿ってみると、この`notation`という引数は`NotationParser`クラスに渡され、
`ModuleVersionSelector`クラスのオブジェクトに変換されています。

そして、プラグインの管理が`PluginRequest`インターフェースで行われていること、プラグインの実体の記述が`ModuleVersionSelector`というクラスでされていることが分かりました。

そしてなんだかんだでファイル名指定（もしくはクラス名指定）はできなそうな雰囲気を感じたので、諦めました（投げやり）。

----

以上により、kotlinコードをコンパイルするにはおそらくgroupとnameで表されるモジュール名を解決する仕組みの利用が必須であると結論付けました。

### 作戦2　`org.ysb33r.ivypot`Gradleプラグインを使用してローカルにivyのミラーを作る

`org.ysb33r.ivypot`を試しました。

https://stackoverflow.com/questions/41804511/make-gradle-use-maven-local-repository-for-downloading-artifacts

https://gitlab.com/ysb33rOrg/ivypot-gradle-plugin

結果、リポジトリのコピーが作られたものの、肝心のjarファイルが入っていないという問題が起こりました。

その理由は以下のもののようです。

https://www.glamenv-septzen.net/view/1099

> 実は当初勘違いしていて、Ivyって依存関係を".ivy"とかにDLして、そこに全部jarを保存しておいてくれるものかと思ってました。Mavenの".m2/repository/"みたいな感じで、classpathは適当にAntのbuild.xml中で自動解決してくれる～みたいな。
> しかしこれは完全な勘違いで、Ivyのメインはあくまでも依存関係を「ダウンロード」することにあります。classpathの調整はやってくれないんですね。じゃあどう使うのかというと、Antプロジェクトの中に"lib"ディレクトリを作っておいて、"<ivy:retrieve/>"を実行することでその中にjarをダウンロードしてくれます。 

これでは依存関係を先に計算しおき、jarだけ格納されたmavenリポジトリ風ディレクトリを読みに行った方が早いではないか、ということでこの作戦は諦めました。

### 作戦3　`maven`Gradleプラグインの`install`タスクを使用する

http://gradle.monochromeroad.com/docs/userguide/maven_plugin.html

> Mavenメタデータの生成を含め、関連するアーティファクトをローカルMavenキャッシュにインストールする。 デフォルトではinstallタスクはarchivesコンフィグレーションに関連付けられる。 このコンフィグレーションのデフォルトでは要素としてJARのみを持つ。 ローカルリポジトリへのインストールに関して詳しく学ぶためには「ローカルリポジトリへのインストール」を参照のこと。 

当初、このタスクは依存先のモジュールをローカルリポジトリへ格納するものだと考えていました。

実際にはプロジェクトの成果物をローカルリポジトリへ格納するというタスクでした。

----

【余談】Gradle 7で`maven`Gradleプラグインが全く見つからず、「`install`」タスクを含むアレがなんでこんなに見つからないの！と思ったら、丁度7で削除されていたというドラマがありました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/66f99725-5be6-bc80-6be4-148c1def5a13.png)

### 作戦4　`mvn`コマンドの`dependency:go-offline`ゴールを使う

次のサイトの手法を試しました。

https://takezoe.hatenablog.com/entry/20120302/p1

これは`dependency:go-offline`ゴールを使ってローカルリポジトリにすべての依存関係をダウンロードする手法です。

https://maven.apache.org/plugins/maven-dependency-plugin/go-offline-mojo.html

ビルド環境（実用上はgitホスティングサービスのCI上）で`mvn`コマンドが利用できる必要があります（※下記）が、「mavenリポジトリをローカルにミラーするgradleのタスクは存在しない」と思い始めたのでこの方法を試しました。

----

【※】gradleのビルドスクリプトの依存関係にmavenを追加して直接呼び出すという方法がありそうですが、やってみたところヤケクソに長いエラーが出て諦めました。

https://fits.hatenablog.com/entry/2013/07/31/004620

----

まず`mvn`コマンドを利用できるようにします。

```bash
$ sudo apt update
$ sudo apt install maven
```

----

`pom.xml`をいい感じに編集したあとで、次のコマンドでローカルリポジトリの場所を指定して`go-offline`を実行します。

```bash
hoge/project1$ mvn dependency:go-offline -Dmaven.repo.local=maven
```

----

その結果、ちゃんと目的のファイルはありましたが、やたら色んなものがダウンロードされていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/ed10551a-aa23-9dd2-f9eb-35abebc1e741.png)

本当に必要な`junit-4.13.2.jar`のサイズは375KBですが、このmavenディレクトリのサイズは100MB以上ありました。

----

ログを見たところ、mavenプラグインの
依存先もすべてダウンロードされていました。

```
【物凄い上のmavenプラグインの列】
[INFO] Resolved plugin: doxia-core-1.9.1.jar
[INFO] Resolved plugin: plexus-utils-1.5.10.jar
[INFO] Resolved plugin: jtidy-4aug2000r7-dev.jar
[INFO] Resolved plugin: maven-artifact-2.0.9.jar
[INFO] Resolved plugin: plexus-io-2.0.2.jar
[INFO] Resolved plugin: maven-artifact-manager-2.0.6.jar
[INFO] Resolved dependency: junit-4.13.2.jar
[INFO] Resolved dependency: hamcrest-core-1.3.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.090 s
[INFO] Finished at: 2021-07-09T15:53:41+09:00
[INFO] ------------------------------------------------------------------------
```

どうにかして
mavenプラグインに起因する依存関係を含めずにダウンロードできればよいみたいです。

----

そこでソースを調べたところ、このゴールは実行時にプラグインのダウンロードを行わないという選択肢は持っていないことが分かりました。

https://github.com/apache/maven-dependency-plugin/blob/master/src/main/java/org/apache/maven/plugins/dependency/resolvers/GoOfflineMojo.java

「mavenモジュールを依存関係ごとローカルにコピーすること」を目的としたゴールがあれば解決できそうです。

### 作戦5　`mvn`コマンドの`dependency:copy-dependencies`ゴールを使う

次に試したのは次のサイトの手法です。

https://qiita.com/rk05231977/items/9dcf8b6cc3cdc9d50a2d

----

結果、mavenリポジトリではなくjarのみがダウンロードされました。

また、`mvn`コマンドはどのゴールを実行しても初回起動時にすべてのmavenプラグインに起因する依存jarをローカルリポジトリにダウンロードするようなので、ローカルリポジトリにダウンロードさせてそれをいただくという手法は無理っぽいです。

### 作戦6　`dependency:copy`ゴールの`localRepositoryDirectory`オプションを使う

Apache Maven Dependency Pluginの中を漁りました。

https://maven.apache.org/plugins/maven-dependency-plugin/index.html

----

そして、`copy`ゴールには`<localRepositoryDirectory>`というオプションがあり、

> Path to override default local repository during plugin's execution. To remove all downloaded artifacts as part of the build, set this value to a location under your project's target directory

とありました（ちなみに`copy-dependencies`側にはこのオプションは存在しませんでした）。

よくわからないけど、このオプションを付与するとローカルリポジトリが2個になるっぽいです。そして、新たに指定した方のローカルリポジトリにはこのタスクの生成物しか格納されないような気がします。

`<localRepositoryDirectory>`オプションに対するもう一つ見つけた説明文です。

https://maven.apache.org/plugins/maven-dependency-plugin/examples/copying-artifacts.html

> dependency:copy always downloads artifacts to default local repository first, and then copy the artifacts to the desired locations. For large size unique snapshot artifacts, the downloads can quickly fill up default local repository, and therefore local disk, after many executions. To clean up the downloaded artifacts as part the build, set localRepositoryDirectory's value to a location in your project's target directory.

このオプションの本来の用途は、大きな単一jarでローカルリポジトリを圧迫しないためにゴミ箱を別途指定することのようです。つまりmavenリポジトリの依存ツリーが丸ごと出力される場所ではないようです。

----

このオプションが使われているゴールが他にないか調べたところ、このオプションは`AbstractFromConfigurationMojo#localRepositoryDirectory`フィールドに由来し、そのサブクラスは`copy`タスクと`unpack`タスクの丁度2個のみでした。後者は出力先ディレクトリにjarそのものではなくjarの内容物をドバッと出します。

----

まとめると、

- `mvn`コマンドの
- `dependency:copy`ゴールを
- `outputDirectory`オプションにダミーの値を指定したうえで
- `localRepositoryDirectory`にリポジトリ格納先ディレクトリを指定して
- `artifact`で依存アーティファクトを指定して

起動すると、初回実行時にホームディレクトリ下のローカルリポジトリに大量の余計なjarをダウンロードしつつ、別途指定した固有ローカルリポジトリに指定されたアーティファクトのmavenリポジトリをキャッシュしながら、出力先にそのjarを配置します。

----

それを行うには、カレントディレクトリに次のpomファイルを配置したうえで、次のコマンドを打ちます。

```xml:hoge/project1/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>null</groupId>
    <artifactId>null</artifactId>
    <version>0.0.0</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.2.0</version>
                <configuration>
                    <localRepositoryDirectory>./maven</localRepositoryDirectory>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
```

```bash
hoge/project1$ mvn dependency:copy -DoutputDirectory=build/maven-dependency-copy -Dartifact=junit:junit:4.13.2
```

----

その結果、無事タスクは完了しました。

```:結果
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------------< null:null >------------------------------
[INFO] Building null 0.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:3.2.0:copy (default-cli) @ null ---
[INFO] Configured Artifact: junit:junit:4.13.2:jar
Downloading from central: https://repo.maven.apache.org/maven2/junit/junit/4.13.2/junit-4.13.2.pom
Downloaded from central: https://repo.maven.apache.org/maven2/junit/junit/4.13.2/junit-4.13.2.pom (27 kB at 35 kB/s)
Downloading from central: https://repo.maven.apache.org/maven2/junit/junit/4.13.2/junit-4.13.2.jar
Downloaded from central: https://repo.maven.apache.org/maven2/junit/junit/4.13.2/junit-4.13.2.jar (385 kB at 704 kB/s)
[INFO] Copying junit-4.13.2.jar to /mnt/i/workspace/hoge/project1/build/maven-dependency-copy/junit-4.13.2.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.600 s
[INFO] Finished at: 2021-07-10T07:50:15+09:00
[INFO] ------------------------------------------------------------------------
```

しかしながら、指定したローカルリポジトリの中にはjunitの依存先である`org.hamcrest:hamcrest-core:1.3`が存在しません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/b2a5facc-ea3a-7fb8-a4ad-4ac26a5e1a41.png)

依存性を推移的に解決しているなら同じ場所にダウンロードされているはずです。

```:gradlew_dependencies
compileClasspath - Compile classpath for source set 'main'.
\--- junit:junit:4.13.2
     \--- org.hamcrest:hamcrest-core:1.3
```

----

この挙動は、`-Dmaven.repo.local=maven2`オプションにより空のローカルリポジトリの下で実行しても変わりませんでした。

また、junitではなく`org.jetbrains.kotlin:kotlin-stdlib:1.4.31`で試しても推移的に依存性を解決しないようでした。

```:gradlew_dependencies
testRuntimeClasspath - Runtime classpath of source set 'test'.
+--- junit:junit:4.13.2
|    \--- org.hamcrest:hamcrest-core:1.3
\--- org.jetbrains.kotlin:kotlin-stdlib:1.4.31
     +--- org.jetbrains.kotlin:kotlin-stdlib-common:1.4.31
     \--- org.jetbrains:annotations:13.0
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/440b988c-f14d-3e95-d0f8-6d6892afe726.png)

----

### 作戦7　`dependency:copy-dependencies`ゴールの`useRepositoryLayout`オプションを使う

`copy`ゴールが依存関係を推移的に解決しないとしても、`copy-dependencies`ゴールの方は少なくとも推移的に解決はしてくれるだろう（実際説明文にもtransitiveって書いてあるし）と思いました。

https://maven.apache.org/plugins/maven-dependency-plugin/index.html

> takes the list of project direct dependencies and optionally transitive dependencies and copies them to a specified location, stripping the version if desired. This goal can also be run from the command line.

----

このタスクの問題は出力がjarファイルしかないところなので、mavenリポジトリをそのまま出力するオプションがないかと調べたところ、`<useRepositoryLayout>`（`mdep.useRepositoryLayout`）がまさにそんな感じでした。

https://maven.apache.org/plugins/maven-dependency-plugin/copy-dependencies-mojo.html#useRepositoryLayout

> Place each artifact in the same directory layout as a default repository.

このオプションは`AbstractFromDependenciesMojo#useRepositoryLayout`フィールドに由来するらしく、`copy-dependencies`ゴールの他に`unpack-dependencies`ゴールでも使えるようでした（逆に言うと`copy` `get`などでは利用不可（別のフィールド実体に由来する同名のオプションも見当たらず））。

----

また、`copyPom`および`addParentPoms`オプションを付けなければgradleの段階で例外が発生したため、これも付与します。

これらのオプションは、`pom.xml`上でプラグインの使用を明示しなければ無視されるようです。

```xml
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.2.0</version>
            </plugin>
        </plugins>
    </build>
```

----

このタスクは`groupId`のような個別のアーティファクトを特定するオプションを持たないため、多分pom上に定義された依存関係しか見に行かないんじゃないかと思うので、`pom.xml`に依存関係を追加します。

```xml:hoge/project1/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>null</groupId>
    <artifactId>null</artifactId>
    <version>0.0.0</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.10</version>
            </plugin>
        </plugins>
    </build>
    <dependencies>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>

    </dependencies>
</project>
```

そして次のコマンドで実行します。

```bash
hoge/project1$ mvn dependency:copy-dependencies -DoutputDirectory=maven -Dmdep.useRepositoryLayout=true -Dmdep.copyPom=true -Dmdep.addParentPoms=true
```

----

結果、無事推移的に依存するすべてのリポジトリがその構造のままjarとpomがダウンロードされました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/8631d906-94df-4438-0d2f-1451712cb5d7.png)

----

なお、`maven-dependency-plugin`のバージョンは3.0.0以上では「親のpomがMaven Central Repositoryに存在しないような依存性を（推移的に）含むアーティファクト（具体的には`org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.4.31`など）」を`addParentPoms`を付けて指定するとなぜか解決失敗するということがのちに分かりました。

バージョン2.10では正常に解決できました。理由は謎です（`AbstractDependencyFilterMojo#getDependencySets`内で依存先の親を解決する部分で落ちているようですが、なぜか追加リポジトリが無視されているようでした【TODO】）。

```
org.apache.maven.plugins:maven-dependency-plugin:3.0.0:copy-dependencies (default-cli) on project null: Error resolving project artifact: Failure to find org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:pom:1.4.31 in https://repo.maven.apache.org/maven2 was cached in the local repository, resolution will not be reattempted until the update interval of central has elapsed or updates are forced for project org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:pom:1.4.31
```

## プロジェクト下のリポジトリを指定してjavaコンパイル

前節で作戦7によって`hoge/project1/maven`ディレクトリがmavenリポジトリになっているので、それをそのまま指定してビルドします。

作戦1～6は全部ボツです。

### ビルドスクリプト側でプロジェクト内のリポジトリを見るように変更

```kotlin:hoge/project1/build.gradle.kts
plugins {
    application
}

repositories {
    maven("./maven")
}

dependencies {
    implementation("junit:junit:4.13.2")
}
```

----

### javaコード上で依存jarのクラスを呼び出してみる

正常にクラスがロードできていなければ例外になるようなコードを書いてみます。

```java:hoge/project1/src/main/java/Main.java
import org.junit.Test;

import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) {
        System.out.println("Main");
        for (Method method : Test.class.getMethods()) {
            System.out.println(method);
        }
    }
}

```

この時点で既にIDEA上でgradleプロジェクトのsyncをするとコードの補完ができるようになっていました。

### キャッシュ削除

ここで、一旦IDEAのGradleのオフラインモードをONにし、IDEAを終了し、他に起動しているEclipseなどのjavaコンパイラもすべて閉じて、次のキャッシュディレクトリを削除しておきます。

- `%HOMEPATH%\.gradle`
- `%HOMEPATH%\.m2`
- `hoge/project1/.gradle`
- `hoge/project1/build`

念のためIDEAのここにあるこれも行いました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/2e0ed9ba-ae0b-770b-2cbe-b3699c7126a5.png)

### ビルド・実行

この状態でIDEAから`gradle jar`を実行すると、無事jarファイルが生成されました。

```:結果
9:13:40: タスクを実行中 'jar'...

> Task :compileJava
> Task :processResources NO-SOURCE
> Task :classes
> Task :jar

BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed
9:13:42: タスクの実行を完了しました 'jar'。
```

----

出力されたjarを依存先とともに呼び出してみると、ちゃんと依存先のjarに依存した動作を成功させました。

```bash
hoge/project1$ java -cp build/libs/project1.jar:maven/junit/junit/4.13.2/junit-4.13.2.jar Main
```

```:結果
Main
public abstract java.lang.Class org.junit.Test.expected()
public abstract long org.junit.Test.timeout()
public abstract boolean java.lang.annotation.Annotation.equals(java.lang.Object)
public abstract java.lang.String java.lang.annotation.Annotation.toString()
public abstract int java.lang.annotation.Annotation.hashCode()
public abstract java.lang.Class java.lang.annotation.Annotation.annotationType()
```

## kotlinをjvmコードにコンパイルする

### ビルドスクリプトの改変

gradleにkotlinプラグインを追加して、ソースコードの依存性にkotlinのライブラリを追加します。

```kotlin:hoge/project1/build.gradle.kts
plugins {
    application
    kotlin("jvm") version "1.4.31"
}

repositories {
    maven("./maven")
}

dependencies {
    implementation("junit:junit:4.13.2")
    implementation(kotlin("stdlib-jdk8", "1.4.31"))
}
```

gradle.ktsを使う場合、kotlinプラグインのバージョンはgradle.ktsのランタイムのkotlinバージョンに合わせなければバージョン衝突になるようです。Gradle 7.1.1ではKotlin 1.4.31が指定のバージョンでした。

### プラグインの依存性解決の失敗

オフラインかつプロジェクト内のリポジトリにもkotlinのプラグインの依存性を追加していないため、当然次のような問題が起こります。

```:問題
* What went wrong:
Plugin [id: 'org.jetbrains.kotlin.jvm', version: '1.4.31'] was not found in any of the following sources:

- Gradle Core Plugins (plugin is not in 'org.gradle' namespace)
- Plugin Repositories (could not resolve plugin artifact 'org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.4.31')
  Searched in the following repositories:
    Gradle Central Plugin Repository
```

`org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.4.31`を依存性に追加すればいいということが分かりました。

### `org.jetbrains.kotlin.jvm.gradle.plugin`はGradle Pluginsリポジトリにある

当初pomに単に依存アーティファクトを追加しましたが、解決に失敗しました。

Maven Central Repositoryには無いようです。

https://mvnrepository.com/artifact/org.jetbrains.kotlin.jvm/org.jetbrains.kotlin.jvm.gradle.plugin?repo=gradle-plugins

オンライン状態で実行するとちゃんとプラグインが使えたため、デフォルトで追加されるGradle Core Pluginsというリポジトリの方にあるということになります。

----

https://plugins.gradle.org/plugin/org.jetbrains.kotlin.jvm

このページを見ると、そのURLは`https://plugins.gradle.org/m2/`のようです。

----

中身を確認してみると、このアーティファクトはpomのみでjarがないようです。そのため、`pom.xml`内ではこの依存性の`<type>`を`pom`にしておきます。

https://plugins.gradle.org/m2/org/jetbrains/kotlin/jvm/org.jetbrains.kotlin.jvm.gradle.plugin/1.4.31/

依存性の`<type>`と依存先成果物の`<packaging>`には次のような対応があるようです。

https://maven.apache.org/ref/3.8.1/maven-core/artifact-handlers.html

----

ちなみに`kotlin("stdlib-jdk8", "1.4.31")`は`org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.31`というアーティファクトを表します。

### `settings.gradle.kts`の記述

プラグインの依存関係は（おそらくビルドスクリプトよりも先にコンパイルおよび実行されるために）特別な場所に記述しなければなりません。

```kotlin:hoge/project1/settings.gradle.kts
pluginManagement {
    repositories {
        maven("./maven")
    }
}
```

### `pom.xml`の編集・リポジトリダウンロード

アーティファクト名とリポジトリが分かったので`pom.xml`を更新します。ついでにオプションもここに書いてしまいます。

依存性の記述時には`<type>`に気を付けてください。コピペすると地味に`<type>pom</type>`がくっついてきたりします。

```diff_xml:hoge/project1/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>null</groupId>
    <artifactId>null</artifactId>
    <version>0.0.0</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.10</version>
                <configuration>
                    <outputDirectory>maven</outputDirectory>
                    <useRepositoryLayout>true</useRepositoryLayout>
                    <copyPom>true</copyPom>
                    <addParentPoms>true</addParentPoms>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <repositories>

        <repository>
            <id>plugins.gradle.org</id>
            <name>plugins.gradle.org</name>
            <url>https://plugins.gradle.org/m2/</url>
        </repository>

    </repositories>
    <dependencies>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>

        <dependency>
            <groupId>org.jetbrains.kotlin.jvm</groupId>
            <artifactId>org.jetbrains.kotlin.jvm.gradle.plugin</artifactId>
            <version>1.4.31</version>
            <type>pom</type>
        </dependency>

        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib-jdk8</artifactId>
            <version>1.4.31</version>
        </dependency>

    </dependencies>
</project>
```

これで次のコマンドによりkotlinのGradleプラグインをダウンロードします。

```bash
hoge/project1$ mvn dependency:copy-dependencies
```

----

結果、めっちゃ大量の依存性が解決できました。

```:結果
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------------< null:null >------------------------------
[INFO] Building null 0.0.0
[INFO] --------------------------------[ jar ]---------------------------------
Downloading from plugins.gradle.org: https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.4.31/kotlin-stdlib-jdk8-1.4.31.pom
Downloaded from plugins.gradle.org: https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.4.31/kotlin-stdlib-jdk8-1.4.31.pom (1.6 kB at 876 B/s)
Downloading from plugins.gradle.org: https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.4.31/kotlin-stdlib-jdk7-1.4.31.pom
Downloaded from plugins.gradle.org: https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.4.31/kotlin-stdlib-jdk7-1.4.31.pom (1.4 kB at 875 B/s)
Downloading from plugins.gradle.org: https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.4.31/kotlin-stdlib-jdk7-1.4.31.jar
Downloaded from plugins.gradle.org: https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.4.31/kotlin-stdlib-jdk7-1.4.31.jar (23 kB at 14 kB/s)
[INFO]
[INFO] --- maven-dependency-plugin:2.10:copy-dependencies (default-cli) @ null ---
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-stdlib/1.4.31/kotlin-stdlib-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-stdlib/1.4.31/kotlin-stdlib-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-stdlib/1.4.31/kotlin-stdlib-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-stdlib/1.4.31/kotlin-stdlib-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.4.31/kotlin-stdlib-jdk8-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.4.31/kotlin-stdlib-jdk8-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/antlr/antlr4-master/4.5.2-1/antlr4-master-4.5.2-1.pom to /mnt/i/workspace/hoge/project1/maven/org/antlr/antlr4-master/4.5.2-1/antlr4-master-4.5.2-1.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-compiler-runner/1.4.31/kotlin-compiler-runner-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-compiler-runner/1.4.31/kotlin-compiler-runner-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-compiler-runner/1.4.31/kotlin-compiler-runner-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-compiler-runner/1.4.31/kotlin-compiler-runner-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.4.31/kotlin-gradle-plugin-model-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.4.31/kotlin-gradle-plugin-model-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.4.31/kotlin-gradle-plugin-model-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.4.31/kotlin-gradle-plugin-model-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/antlr/antlr4-runtime/4.5.2-1/antlr4-runtime-4.5.2-1.jar to /mnt/i/workspace/hoge/project1/maven/org/antlr/antlr4-runtime/4.5.2-1/antlr4-runtime-4.5.2-1.jar
[INFO] Installing /home/riana/.m2/repository/org/antlr/antlr4-runtime/4.5.2-1/antlr4-runtime-4.5.2-1.pom to /mnt/i/workspace/hoge/project1/maven/org/antlr/antlr4-runtime/4.5.2-1/antlr4-runtime-4.5.2-1.pom
[INFO] Installing /home/riana/.m2/repository/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar to /mnt/i/workspace/hoge/project1/maven/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar
[INFO] Installing /home/riana/.m2/repository/com/google/code/gson/gson/2.8.6/gson-2.8.6.pom to /mnt/i/workspace/hoge/project1/maven/com/google/code/gson/gson/2.8.6/gson-2.8.6.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.4.31/kotlin-scripting-compiler-embeddable-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.4.31/kotlin-scripting-compiler-embeddable-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.4.31/kotlin-scripting-compiler-embeddable-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.4.31/kotlin-scripting-compiler-embeddable-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-scripting-jvm/1.4.31/kotlin-scripting-jvm-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-scripting-jvm/1.4.31/kotlin-scripting-jvm-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-scripting-jvm/1.4.31/kotlin-scripting-jvm-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-scripting-jvm/1.4.31/kotlin-scripting-jvm-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-android-extensions/1.4.31/kotlin-android-extensions-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-android-extensions/1.4.31/kotlin-android-extensions-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-android-extensions/1.4.31/kotlin-android-extensions-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-android-extensions/1.4.31/kotlin-android-extensions-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/com/github/gundy/semver4j/0.16.4/semver4j-0.16.4.jar to /mnt/i/workspace/hoge/project1/maven/com/github/gundy/semver4j/0.16.4/semver4j-0.16.4.jar
[INFO] Installing /home/riana/.m2/repository/com/github/gundy/semver4j/0.16.4/semver4j-0.16.4.pom to /mnt/i/workspace/hoge/project1/maven/com/github/gundy/semver4j/0.16.4/semver4j-0.16.4.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.4.31/kotlin-compiler-embeddable-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.4.31/kotlin-compiler-embeddable-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.4.31/kotlin-compiler-embeddable-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.4.31/kotlin-compiler-embeddable-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.4.31/kotlin-gradle-plugin-api-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.4.31/kotlin-gradle-plugin-api-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.4.31/kotlin-gradle-plugin-api-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.4.31/kotlin-gradle-plugin-api-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-util-io/1.4.31/kotlin-util-io-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-util-io/1.4.31/kotlin-util-io-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-util-io/1.4.31/kotlin-util-io-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-util-io/1.4.31/kotlin-util-io-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-util-klib/1.4.31/kotlin-util-klib-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-util-klib/1.4.31/kotlin-util-klib-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-util-klib/1.4.31/kotlin-util-klib-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-util-klib/1.4.31/kotlin-util-klib-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-gradle-plugin/1.4.31/kotlin-gradle-plugin-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-gradle-plugin/1.4.31/kotlin-gradle-plugin-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-gradle-plugin/1.4.31/kotlin-gradle-plugin-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-gradle-plugin/1.4.31/kotlin-gradle-plugin-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-scripting-common/1.4.31/kotlin-scripting-common-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-scripting-common/1.4.31/kotlin-scripting-common-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-scripting-common/1.4.31/kotlin-scripting-common-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-scripting-common/1.4.31/kotlin-scripting-common-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-stdlib-common/1.4.31/kotlin-stdlib-common-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-stdlib-common/1.4.31/kotlin-stdlib-common-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-stdlib-common/1.4.31/kotlin-stdlib-common-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-stdlib-common/1.4.31/kotlin-stdlib-common-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/junit/junit/4.13.2/junit-4.13.2.jar to /mnt/i/workspace/hoge/project1/maven/junit/junit/4.13.2/junit-4.13.2.jar
[INFO] Installing /home/riana/.m2/repository/junit/junit/4.13.2/junit-4.13.2.pom to /mnt/i/workspace/hoge/project1/maven/junit/junit/4.13.2/junit-4.13.2.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-script-runtime/1.4.31/kotlin-script-runtime-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-script-runtime/1.4.31/kotlin-script-runtime-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-script-runtime/1.4.31/kotlin-script-runtime-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-script-runtime/1.4.31/kotlin-script-runtime-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar to /mnt/i/workspace/hoge/project1/maven/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar
[INFO] Installing /home/riana/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.pom to /mnt/i/workspace/hoge/project1/maven/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/jvm/org.jetbrains.kotlin.jvm.gradle.plugin/1.4.31/org.jetbrains.kotlin.jvm.gradle.plugin-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/jvm/org.jetbrains.kotlin.jvm.gradle.plugin/1.4.31/org.jetbrains.kotlin.jvm.gradle.plugin-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-build-common/1.4.31/kotlin-build-common-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-build-common/1.4.31/kotlin-build-common-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-build-common/1.4.31/kotlin-build-common-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-build-common/1.4.31/kotlin-build-common-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/com/google/code/gson/gson-parent/2.8.6/gson-parent-2.8.6.pom to /mnt/i/workspace/hoge/project1/maven/com/google/code/gson/gson-parent/2.8.6/gson-parent-2.8.6.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-annotation-processing-gradle/1.4.31/kotlin-annotation-processing-gradle-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-annotation-processing-gradle/1.4.31/kotlin-annotation-processing-gradle-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-annotation-processing-gradle/1.4.31/kotlin-annotation-processing-gradle-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-annotation-processing-gradle/1.4.31/kotlin-annotation-processing-gradle-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.4.31/kotlin-stdlib-jdk7-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.4.31/kotlin-stdlib-jdk7-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.4.31/kotlin-stdlib-jdk7-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.4.31/kotlin-stdlib-jdk7-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/sonatype/oss/oss-parent/7/oss-parent-7.pom to /mnt/i/workspace/hoge/project1/maven/org/sonatype/oss/oss-parent/7/oss-parent-7.pom
[INFO] Installing /home/riana/.m2/repository/de/undercouch/gradle-download-task/4.0.2/gradle-download-task-4.0.2.jar to /mnt/i/workspace/hoge/project1/maven/de/undercouch/gradle-download-task/4.0.2/gradle-download-task-4.0.2.jar
[INFO] Installing /home/riana/.m2/repository/de/undercouch/gradle-download-task/4.0.2/gradle-download-task-4.0.2.pom to /mnt/i/workspace/hoge/project1/maven/de/undercouch/gradle-download-task/4.0.2/gradle-download-task-4.0.2.pom
[INFO] Installing /home/riana/.m2/repository/org/hamcrest/hamcrest-parent/1.3/hamcrest-parent-1.3.pom to /mnt/i/workspace/hoge/project1/maven/org/hamcrest/hamcrest-parent/1.3/hamcrest-parent-1.3.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.4.31/kotlin-daemon-embeddable-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.4.31/kotlin-daemon-embeddable-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.4.31/kotlin-daemon-embeddable-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.4.31/kotlin-daemon-embeddable-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-reflect/1.4.31/kotlin-reflect-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-reflect/1.4.31/kotlin-reflect-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-reflect/1.4.31/kotlin-reflect-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-reflect/1.4.31/kotlin-reflect-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.4.31/kotlin-scripting-compiler-impl-embeddable-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.4.31/kotlin-scripting-compiler-impl-embeddable-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.4.31/kotlin-scripting-compiler-impl-embeddable-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.4.31/kotlin-scripting-compiler-impl-embeddable-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/intellij/deps/trove4j/1.0.20181211/trove4j-1.0.20181211.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/intellij/deps/trove4j/1.0.20181211/trove4j-1.0.20181211.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/intellij/deps/trove4j/1.0.20181211/trove4j-1.0.20181211.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/intellij/deps/trove4j/1.0.20181211/trove4j-1.0.20181211.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.3.8/kotlinx-coroutines-core-1.3.8.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.3.8/kotlinx-coroutines-core-1.3.8.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.3.8/kotlinx-coroutines-core-1.3.8.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.3.8/kotlinx-coroutines-core-1.3.8.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-daemon-client/1.4.31/kotlin-daemon-client-1.4.31.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-daemon-client/1.4.31/kotlin-daemon-client-1.4.31.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/kotlin/kotlin-daemon-client/1.4.31/kotlin-daemon-client-1.4.31.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/kotlin/kotlin-daemon-client/1.4.31/kotlin-daemon-client-1.4.31.pom
[INFO] Installing /home/riana/.m2/repository/org/sonatype/oss/oss-parent/9/oss-parent-9.pom to /mnt/i/workspace/hoge/project1/maven/org/sonatype/oss/oss-parent/9/oss-parent-9.pom
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/annotations/13.0/annotations-13.0.jar
[INFO] Installing /home/riana/.m2/repository/org/jetbrains/annotations/13.0/annotations-13.0.pom to /mnt/i/workspace/hoge/project1/maven/org/jetbrains/annotations/13.0/annotations-13.0.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  7.830 s
[INFO] Finished at: 2021-07-11T03:28:38+09:00
[INFO] ------------------------------------------------------------------------
```

----

実はここで1回前述のエラーで止まりましたが、次の記事にあるとおり`.m2`ディレクトリを削除してもできず、結局`maven-dependency-plugin`のバージョンを2.10に下げました。

https://qiita.com/ponsuke0531/items/bfa1998d4ac24788529d

### ビルド

キャッシュを削除してjarの生成を試みます。

```bash:IDEA上で
gradle jar
```

無事jarができました。よく見るとKotlinを含むタスクが実行されているのが分かります。

```:結果
3:33:13: タスクを実行中 'jar'...

> Task :compileKotlin
> Task :compileJava
> Task :processResources NO-SOURCE
> Task :classes
> Task :inspectClassesForKotlinIC
> Task :jar

BUILD SUCCESSFUL in 432ms
4 actionable tasks: 4 executed
3:33:14: タスクの実行を完了しました 'jar'。
```

### `src/main/java`にKotlinコードを書いてjavaと双方向に呼び出す

JavaとKotlinで双方向にメソッドの呼び出しを行うような対称的なソースコードを書いてみます。

```java:hoge/project1/src/main/java/Main.java
public class Main {
    public static void main(String[] args) {
        System.out.println(MainKotlin.functionKotlin("Hello from Java!"));
    }

    public static String functionJava(String param) {
        return "JAVA[" + param + "]";
    }
}
```

```kotlin:hoge/project1/src/main/java/MainKotlin.kt
class MainKotlin {
    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            println(Main.functionJava("Hello from Kotlin!"))
        }

        @JvmStatic
        fun functionKotlin(param: String) = "KOTLIN[$param]"
    }
}
```

これらは同ディレクトリに混在して配置することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/d40d08da-5c3c-b697-46d9-397510de2871.png)

----

ビルドするとjarの中身は実質的に同じ動きをするコードがちゃんと生成されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/4e5b509d-e97b-7e27-06e6-f7556c20e03d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/93d3cacf-dfa8-6f79-4b35-f42c74caf581.png)

たとえKotlinらしいことをしなくてもNullチェックのために`kotlin.jvm.internal.Intrinsics`のようなKotlinのクラスを利用しているので、実行時にロードしなければならないことが分かります。

### 実行のためのコマンドの生成

`application`Gradleプラグインにより提供される`installDist`タスクを実行すると、`build/install/project1/bin/project1`という場所にシェルスクリプトが出力されます。

これは依存先のjarがすべてクラスパスに追加されたjavaコマンドと考えてよいです。

----

ちなみに実行時の依存性は次のようになってしました。

```:gradle_dependenciesの結果
runtimeClasspath - Runtime classpath of compilation 'main' (target  (jvm)).
+--- junit:junit:4.13.2
|    \--- org.hamcrest:hamcrest-core:1.3
\--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.31
     +--- org.jetbrains.kotlin:kotlin-stdlib:1.4.31
     |    +--- org.jetbrains.kotlin:kotlin-stdlib-common:1.4.31
     |    \--- org.jetbrains:annotations:13.0
     \--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.31
          \--- org.jetbrains.kotlin:kotlin-stdlib:1.4.31 (*)
```

これは`installDist`タスクの出力結果とも対応します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/56ba393c-86cd-e00c-8f0a-a73b2584791f.png)

----

先のクラスを実行するためのコマンドは次のようになります。

```bash
hoge/project1$ ./build/install/project1/bin/project1 Main
hoge/project1$ ./build/install/project1/bin/project1 MainKotlin
```

### 実行

結果、プログラム通りの挙動が得られました。

```bash
hoge/project1$ ./build/install/project1/bin/project1 Main
KOTLIN[Hello from Java!]
```

```bash
hoge/project1$ ./build/install/project1/bin/project1 MainKotlin
JAVA[Hello from Kotlin!]
```

## 一体化Jarを作る

`com.github.johnrengelman.shadow`プラグインを使用してfat jarを作ります。

https://qiita.com/kmdsbng/items/ab093146a62afa34b739

https://github.com/johnrengelman/shadow

### プロジェクト内のMavenリポジトリへ追加

ここを見ると、依存先は`gradle.plugin.com.github.jengelman.gradle.plugins:shadow:7.0.0`とあります。

https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow

おそらくアーティファクトは`com.github.johnrengelman.shadow:com.github.johnrengelman.shadow.gradle.plugin:pom:7.0.0`です。

https://plugins.gradle.org/m2/com/github/johnrengelman/shadow/com.github.johnrengelman.shadow.gradle.plugin/7.0.0/

----

よってpomに追加し、`mvn dependency:copy-dependencies`コマンドを叩きます。

```xml:hoge/project1/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>null</groupId>
    <artifactId>null</artifactId>
    <version>0.0.0</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.10</version>
                <configuration>
                    <outputDirectory>maven</outputDirectory>
                    <useRepositoryLayout>true</useRepositoryLayout>
                    <copyPom>true</copyPom>
                    <addParentPoms>true</addParentPoms>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <repositories>

        <repository>
            <id>plugins.gradle.org</id>
            <name>plugins.gradle.org</name>
            <url>https://plugins.gradle.org/m2/</url>
        </repository>

    </repositories>
    <dependencies>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>

        <dependency>
            <groupId>org.jetbrains.kotlin.jvm</groupId>
            <artifactId>org.jetbrains.kotlin.jvm.gradle.plugin</artifactId>
            <version>1.4.31</version>
            <type>pom</type>
        </dependency>

        <dependency>
            <groupId>com.github.johnrengelman.shadow</groupId>
            <artifactId>com.github.johnrengelman.shadow.gradle.plugin</artifactId>
            <version>7.0.0</version>
            <type>pom</type>
        </dependency>

        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib-jdk8</artifactId>
            <version>1.4.31</version>
        </dependency>

    </dependencies>
</project>
```

### Gradleプラグインを追加

shadowプラグインを`plugins`ブロックに追加します。

```diff_kotlin
  plugins {
      application
      kotlin("jvm") version "1.4.31"
+     id("com.github.johnrengelman.shadow") version "7.0.0"
  }
```

----

この時点でIDEAから`gradle tasks --all`を実行すると`shadowJar`タスクが出来上がっています。

```
【中略】

Shadow tasks
------------
knows - Do you know who knows?
shadowJar - Create a combined JAR of project and runtime dependencies

【中略】
```

### `shadowJar`タスクのためのメインクラスの設定

メインクラスがなければエラーになるらしいので、ダミーの値をセットしておきます。

https://stackoverflow.com/questions/53853474/shadowjar-no-value-has-been-specified-for-property-mainclassname

```kotlin
application {
    mainClass.set("dummy")
}
```

### ビルドスクリプトの状態

ここまでのことを反映させたビルドスクリプトが次のコードです。

```kotlin:hoge/project1/build.gradle.kts
plugins {
    application
    kotlin("jvm") version "1.4.31"
    id("com.github.johnrengelman.shadow") version "7.0.0"
}

repositories {
    maven("./maven")
}

dependencies {
    implementation("junit:junit:4.13.2")
    implementation(kotlin("stdlib-jdk8", "1.4.31"))
}

application {
    mainClass.set("dummy")
}
```

### fat jarを出力する

`shadowJar`タスクを実行すると、`build/libs/project1-all.jar`が出力されます。

```:結果
5:20:41: タスクを実行中 'shadowJar'...

> Task :compileKotlin UP-TO-DATE
> Task :compileJava
> Task :processResources NO-SOURCE
> Task :classes
> Task :shadowJar


BUILD SUCCESSFUL in 2s
3 actionable tasks: 2 executed, 1 up-to-date
5:20:43: タスクの実行を完了しました 'shadowJar'。
```

----

生成されたjarの中には依存性に追加したjunitやkotlinなどのパッケージが同封されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/323f8947-d0b3-d2d3-6bcf-3a9713c6ddbb.png)

重さも約2MBあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/85a8ca1c-b4e4-6c10-f1aa-3d8140297168.png)

### 実行

今度は出力されたjarをjavaコマンドから呼び出します。

```bash
hoge/project1$ java -cp build/libs/project1-all.jar Main
KOTLIN[Hello from Java!]
```

```bash
hoge/project1$ java -cp build/libs/project1-all.jar MainKotlin
JAVA[Hello from Kotlin!]
```

## 【応用】依存性を選択的にfat jarに追加する

詳しいことは省略しますが、次のコードが参考になります。

Groovy版Gradleでは検索で出てきたけど、Kotlin版ではどう書くの？的なメモです。

```kotlin
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar

lateinit var implementationAppending: Configuration

configurations {
    register("implementationAppending") {
        implementationAppending = this
        configurations.implementation.get().extendsFrom(this)
    }
}

dependencies {
    // 同封しない依存性
    implementation("junit:junit:4.13.2")
    // 同封する依存性
    implementationAppending(kotlin("stdlib-jdk8", "1.4.31"))
}

(tasks["shadowJar"] as ShadowJar).run {
    configurations = listOf(implementationAppending)
}
```

----

同封するようにした依存性に由来するクラスのみが格納されているの図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/48fe1924-ddcb-297a-7f0d-cbf87f56b5ad.png)

# 公開

この記事に書かれているものを公開しました。

https://github.com/MirrgieRiana/java-kotlin-project-sample

# 【応用】GitHub Actions上でのビルド

完全オフラインでできるかどうかはともかく、GitHub Actionsの中でも同じことが出来たら安心感があるのでやりました。

mavenリポジトリを取り寄せてビルドしてjavaコマンドで叩くだけです。

https://github.com/MirrgieRiana/java-kotlin-project-sample/blob/main/.github/workflows/ci.yml

```yaml/.github/workflows/ci.yml
name: CI

on:
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Build
        run: |
          bash maven-copy.sh || exit
          bash gradlew shadowJar || exit
      - name: Test
        run: |
          command=(java -cp build/libs/java-kotlin-project-sample-all.jar Main)
          out=$("${command[@]}")
          echo "$out"
          [ 'KOTLIN[Hello from Java!]' = "$out" ] || exit
          command=(java -cp build/libs/java-kotlin-project-sample-all.jar MainKotlin)
          out=$("${command[@]}")
          echo "$out"
          [ 'JAVA[Hello from Kotlin!]' = "$out" ] || exit
```

----

無事テストが通りました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280627/ba160763-b3b1-4443-b3e9-3bf780f857f4.png)

よかった。

# TODO

`%HOMEPATH%/.gradle` `%HOMEPATH%/.m2` `./maven`を削除してダウンロード済みのmavenリポジトリをすべて削除したうえで、初めてgradlewを起動すると、次のようにgradleの本体（なぜかバージョン6.5）のダウンロードを勝手に行ってしまう。

```
Download https://services.gradle.org/distributions/gradle-6.5-bin.zip finished, took 2 s 925 ms (102.36 MB)
```

`gradle-6.5-bin.zip`は100MB近くあるので結局解決してなさそう。