Domaが生成するソースコードもソースJARに含める

# 概要

[Doma](https://github.com/domaframework/doma)はPluggable Annotation Processing APIを用いてDAOの実装クラスやエンティティの補助クラスをコンパイル時に自動生成します。
Domaによって自動生成されるのはJavaファイルですが、MavenやGradleで普通に（？）紹介されている方法（下記のリンク先を参照）でソースJARを作っても自動生成されたJavaファイルはソースJARに含まれません。

- https://maven.apache.org/plugin-developers/cookbook/attach-source-javadoc-artifacts.html
- https://docs.gradle.org/current/userguide/publishing_maven.html#publishing_maven:complete_example

テーブルと1対1にマッピングされるエンティティは[Doma-Gen](https://doma-gen.readthedocs.io/ja/stable/)で生成してJARにまとめてWebプロジェクトやバッチプロジェクトから参照するといった構成にすることがあると思いますが、その際にエンティティのソースJARにDomaによって自動生成されるJavaファイルも含めたいというモチベーションがあります。

ここではMavenとGradleそれぞれどうすればDomaによって自動生成されるJavaファイルをソースJARに含むことができるのかを記載します。

# Maven

MavenでソースJARを作るには`org.apache.maven.plugins:maven-source-plugin`の`jar`ゴールを実行します。

パッケージングの対象が`src/main/java`と`src/main/resources`のようなので、そこにDomaによって自動生成されるJavaファイルの出力先ディレクトリを追加すれば良さそうです。
そう考えて[`jar`ゴールのドキュメント](https://maven.apache.org/plugins/maven-source-plugin/jar-mojo.html)を見たのですが、パッケージングの対象を追加できそうなオプションが見当たりませんでした。

仕方がないので`jar`ゴールの実装である[SourceJarMojo](https://github.com/apache/maven-source-plugin/blob/maven-source-plugin-3.0.1/src/main/java/org/apache/maven/plugins/source/SourceJarMojo.java)を見ました。
`SourceJarMojo`は[SourceJarNoForkMojo](https://github.com/apache/maven-source-plugin/blob/maven-source-plugin-3.0.1/src/main/java/org/apache/maven/plugins/source/SourceJarNoForkMojo.java)を継承していました。
`SourceJarNoForkMojo`の中で[ソースディレクトリ（パッケージングの対象）を取得している箇所](https://github.com/apache/maven-source-plugin/blob/maven-source-plugin-3.0.1/src/main/java/org/apache/maven/plugins/source/SourceJarNoForkMojo.java#L73)を見ると、`MavenProject#getCompileSourceRoots()`が呼ばれています。
[MavenProject#getCompileSourceRoots()のJavadoc](https://maven.apache.org/ref/3.6.0/apidocs/org/apache/maven/project/MavenProject.html#getCompileSourceRoots())を見ても何も書かれていませんでしたが、クラス名などから察するにプロジェクト全体に関わる設定のようです。

以上のことからDomaによって自動生成されるJavaファイルの出力先をリソースディレクトリとして追加することにしました。

```xml
<resources>
    <resource>
        <filtering>false</filtering>
        <directory>src/main/resources</directory>
    </resource>
    <resource>
        <filtering>false</filtering>
        <!--
        Pluggable Annotation Processing APIが生成するソースコードのデフォルト出力先を
        リソースディレクトリに追加することでsources-jarに含める。
        https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#generatedSourcesDirectory
        -->
        <directory>${project.build.directory}/generated-sources/annotations</directory>
    </resource>
</resources>
```

ただ、MavenはリソースのコピーをしてからコンパイルしてJARにパッケージングするので、何度か`mvn package`をすると2回目以降のJAR（ソースJARではなく、クラスファイルがパッケージングされている方のJAR）にDomaによって自動生成されたJavaファイルもパッケージングされてしまいます。

そのため、`org.apache.maven.plugins:maven-jar-plugin`の設定でJavaファイルはパッケージに含めない設定を追加しました。

```xml
<!--
ソースディレクトリをリソースに追加したのでJARファイルに
Javaファイルを含めないようにする。
-->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <excludes>
            <exclude>**/*.java</exclude>
        </excludes>
    </configuration>
</plugin>
```

これであとは`mvn package source:jar`とするだけです。

# Gradle

GradleはMavenと違ってソースJARを作るプラグインがあるわけではなく、`Jar`タスクをベースに自分でソースJARを作るタスクを定義します。

そのときにパッケージ対象のディレクトリへDomaによって自動生成されるJavaファイルの出力先を追加すれば良いです。

GradleではDomaによって自動生成されるJavaファイルの出力先は`sourceSets.main.output.classesDirs`がデフォルトです。
これはクラスファイルの出力先でもあるので、クラスファイルを除く設定も書きます。

```gradle
task sourcesJar(type: Jar) {
    from sourceSets.main.allJava, sourceSets.main.output.classesDirs //Domaが生成するソースはsourceSets.main.output.classesDirsに出力される
    classifier = 'sources'
    exclude '**/*.class' //sourceSets.main.output.classesDirsはclassファイルも含むのでフィルタリングする
}
```

前述の通り、Domaによって自動生成される（というかPluggable Annotation Processing APIを使って生成される）Javaファイルはクラスファイルと同じ場所に出力されます。
そのためデフォルトでJARファイルにJavaファイルがパッケージングされてしまうので、それを除く設定を追加します（なんでデフォルトでそんな場所に出力されるのか、、、）。

```gradle
//sourceSets.main.output.classesDirsに出力されるJavaファイルを除く
jar.excludes = ['**/*.java']
```

これであとは`gradle build　sourcesJar`とするだけです。

# 終わりに

動くコード例は次の場所に置いています。

- https://github.com/backpaper0/doma-sources-jar-example

ソースJARをローカルのNexusにデプロイするところまで体験できるようにしています。