分割したsources.jarをKDocの@sampleで活用する


# はじめに

いつからかJetpack ComposeのサンプルコードがIDE上で展開されるようになった
自分のライブラリでもソースコードに含まれない形でサンプルコードを配布する対応を行なったので説明する


# `sources.jar`

- Maven Repositoryに`$artifactId-sources.jar`としてpublishされるartifact
- IntelliJ系のIDEでは自動でimportされ参照可能
- `sources.jar`がpublishされていない場合、IntelliJ系のIDEはclassファイルをデコンパイルして表示する


|`sources.jar`がpublishされている場合|classファイルをデコンパイルしている場合|
|:--:|:--:|
|![sources.jar](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608715/bcff50a1-106d-4b7c-88a9-dce975621613.png)|![decompile](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608715/35a67980-a776-4e22-8786-d9b87c9bdf70.png)|


# KDocの`@sample`

- KDocの`@sample`を使うと、KDoc内にサンプルコードを埋め込むことができる
  - [公式ドキュメント](https://kotlinlang.org/docs/kotlin-doc.html#sample-identifier)
- FQDNを指定することで、IDE上でサンプルコードが展開される
- FQDNで参照できる必要があるため、sources.jarなどに含まれている必要がある


|![Jetpack Composeのソースにsampleが設定されている様子](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608715/658f18d0-705e-4e09-98bc-33121b1b445b.png)|
|:--:|
|Jetpack Composeのソースにsampleが設定されている様子|


# Jetpack Composeの`sources.jar`とGradle Module Metadata

- Jetpack ComposeのKDocはサンプルコードを展開して表示する
- 使われないソースコード（≒サンプルコード）が`aar`に含まれているのかというとそうでもない
- サンプルコードのみを含む `samples-sources.jar` を作成し、Gradle Module Metadataで`sources.jar`と同等として指定することで、IDEが参照可能になる
- Jetpack Composeは`1.7.0-alpha03`から対応している
- `releaseSourcesElements-published`というvariantのfilesに`samples-sources.jar`を追加することで実現している


## Gradle Module Metadata の例

### 1.6.8


https://maven.google.com/web/index.html#androidx.compose.runtime:runtime-android:1.6.8


```json:runtime-android-1.6.8.module
{
  "variants": [
    {
      "name": "releaseSourcesElements-published",
      "attributes": {
        "org.gradle.category": "documentation",
        "org.gradle.dependency.bundling": "external",
        "org.gradle.docstype": "fake-sources",
        "org.gradle.libraryelements": "jar",
        "org.gradle.usage": "java-runtime",
        "org.jetbrains.kotlin.platform.type": "androidJvm"
      },
      "files": [
        {
          "name": "runtime-android-1.6.8-sources.jar",
          "url": "runtime-android-1.6.8-sources.jar",
          "size": 371376,
          "sha512": "2b7bbc5ad5c4a228d2fdc407fd7e162c99eb93725079f17c3256f08a0d68226b917638e06352056284a5bd91134ff4225e4ec66e5e92a8f74f312d40f791b4c4",
          "sha256": "f824e4758db8d1d736bda0d7a21034b220a6c03eb729cc8c8731eee9d7549902",
          "sha1": "7cf63e5b791fa66bd7142a71fd20fde58162a47e",
          "md5": "5228ff7ddc27e22e43dc5a45fbdfdb3d"
        }
      ]
    }
  ]
}
```

### 1.7.0-alpha03


https://maven.google.com/web/index.html#androidx.compose.runtime:runtime-android:1.7.0-alpha03


```json:runtime-android-1.7.0-alpha03.module
{
  "variants": [
    {
      "name": "releaseSourcesElements-published",
      "attributes": {
        "org.gradle.category": "documentation",
        "org.gradle.dependency.bundling": "external",
        "org.gradle.docstype": "fake-sources",
        "org.gradle.libraryelements": "jar",
        "org.gradle.usage": "java-runtime",
        "org.jetbrains.kotlin.platform.type": "androidJvm"
      },
      "files": [
        {
          "name": "runtime-android-1.7.0-alpha03-sources.jar",
          "url": "runtime-android-1.7.0-alpha03-sources.jar",
          "size": 373460,
          "sha512": "af58b9a5edc56c3502907541eea87e31382b762c5b52f91d6d8badb4c68968dacb7fb26242549b0df7c66e01ddc0885c28abeae5ff84f90107e81b49a6e84111",
          "sha256": "89b714e1c72eb35c21fd0acaed5732b10d884c9f82d76c4d98f7cbbd3f9e24cf",
          "sha1": "0a40d532d15e0683514a2c5d0bb104999f259b32",
          "md5": "c66005d2198864179a9ea0ad9d55f0db"
        },
        {
          "name": "runtime-1.7.0-alpha03-samples-sources.jar",
          "url": "runtime-android-1.7.0-alpha03-samples-sources.jar",
          "size": 17531,
          "sha512": "b2d7de81c515fdc26818edd7cea3dd87014de5cf5aff6aa6b1cc7523295d0365095943efac80571a945dbf2d8d6b57ea3738cde944924e2097b6d3eba25e73c6",
          "sha256": "f089f05cf35a4a4f3285628f4aff8966d5714ae0b193f9c558bb4de1029b5b53",
          "sha1": "0322b5bc7b181cfc2e68cd37357b06215e358901",
          "md5": "b492a133684678fe67254ae80a69e416"
        }
      ]
    }
  ]
}
```


# やりかた

## 使用したGradle Plugin

- [vanniktech/gradle-maven-publish-plugin](https://github.com/vanniktech/gradle-maven-publish-plugin)を使用する前提
  - Gradleの`maven-publish` pluginが使用するAPIと同じAPIを使用しているので`maven-publish`pluginを使用している場合も同様の方法で対応可能
  - [Gradle Publishing Guide](https://docs.gradle.org/current/userguide/publishing_maven.html)


## 手順

- 自作ライブラリのGradle Module Metadataを確認する
  - single variantなandroid libraryをpublishしている場合は、`releaseVariantReleaseSourcePublication` というvariantで`sources.jar`を公開している場合が多い
- サンプルコードのみの`sources.jar`を作成するJarタスクを作成し、`releaseVariantReleaseSourcePublication`のartifactに追加する

```groovy:build.gradle
tasks.register("sampleSourcesJar", Jar) {
    group = LifecycleBasePlugin.BUILD_GROUP
    // サンプルコードのみのsource set
    from(project.layout.projectDirectory.dir("samples/src/main/kotlin"))
    // jarファイルの名前を設定
    archiveClassifier.set("samples-sources")
}

// sources.jarを対象のvariantに追加
afterEvaluate {
    artifacts.add("releaseVariantReleaseSourcePublication", sampleSourcesJar)
}
```


https://github.com/RyuNen344/AutoMuttonRecipe/blob/e9b3d8d52d3f6b7cb72f0d2eda7d698f3330dd62/auto-mutton-recipe-compose/build.gradle#L93-L102


:::note info
今回はIDE上でsyntax highlightingが効くようにするために、**子のgradle moduleを作成した**上でJarタスクのfromに設定した [参考](https://github.com/RyuNen344/AutoMuttonRecipe/blob/9bd696ed195548b78083ff2f8d78e6033a7c0261/settings.gradle#L22-L26)
:::


上記設定を行ったのち、`publish`タスクを実行すると`sources.jar`と`samples-sources.jar`が`releaseVariantReleaseSourcePublication`に追加されたGradle Module Metadataが生成される

```json:auto-mutton-recipe-compose-1.1.0.module
{
  "variants": [
    {
      "name": "releaseVariantReleaseSourcePublication",
      "attributes": {
        "org.gradle.category": "documentation",
        "org.gradle.dependency.bundling": "external",
        "org.gradle.docstype": "sources",
        "org.gradle.usage": "java-runtime"
      },
      "files": [
        {
          "name": "auto-mutton-recipe-compose-1.1.0-sources.jar",
          "url": "auto-mutton-recipe-compose-1.1.0-sources.jar",
          "size": 1834,
          "sha512": "9140cd45f1af7dfd7153b4c1c96ba2ce7e8cef18470967c4458bc6a02d8a458b13389212d8cb43f0e8acfaf34b183e787084e5870d4eb0a8b7d45f6c2b041f69",
          "sha256": "73ca9efd4f308ef2142a6aeaf8ec76d27f664da45e034950ab2fe75cd8a1cd62",
          "sha1": "d1269982a14b1f5259941ba26416e693b1e91a40",
          "md5": "2a180c18c04f17a1f7ae94353f32a40a"
        },
        {
          "name": "auto-mutton-recipe-compose-1.1.0-samples-sources.jar",
          "url": "auto-mutton-recipe-compose-1.1.0-samples-sources.jar",
          "size": 2976,
          "sha512": "36f91c4f64256fb33dd8eef939058c2c1f8d316c1038887c6698078567029e4d8d3b6d26e12216cf99a881d1508f157c91176d6a2e4e81beee35ae2783c38a63",
          "sha256": "b553eb05f4bd218fc7942ac4ecbc3442982ad1e85236a1d91738be9fbe68413a",
          "sha1": "19281f738104e754b25d0a94b6af8f70bfcbdd19",
          "md5": "8e2a1688cc9da6a06c7cd2ceba669429"
        }
      ]
    }
  ]
}
```


https://repo1.maven.org/maven2/io/github/ryunen344/mutton/auto-mutton-recipe-compose/1.1.0/auto-mutton-recipe-compose-1.1.0.module


あとはMavenPublishを実行するだけで、ソースコード上に含まれていないサンプルコードが展開されるようになる


|Editor Mode|Reader Mode|
|:--:|:--:|
![Editor Mode](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608715/f84dd2bc-8eeb-40a9-be8f-f3d4902876b8.png)|![Reader Mode](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608715/ead97606-174b-4ea8-8896-60f1991032c9.png)


:::note warn
**本記事はandroid向けライブラリを対象としている**
単純なJVM向けライブラリの場合は同様の考え方と対応で問題ない
KMP向けライブラリの場合はGradle Module Metadataの内容が少し異なるので注意が必要
:::

# おまけ


- Jetpackは力技で実現しているみたい
- パワー💪って感じ

https://github.com/androidx/androidx/blob/b61de2798bcf4bfb94de52a49e6cc36902ad78d6/buildSrc/private/src/main/kotlin/androidx/build/Samples.kt#L41

https://github.com/androidx/androidx/blob/b61de2798bcf4bfb94de52a49e6cc36902ad78d6/buildSrc/private/src/main/kotlin/androidx/build/MavenUploadHelper.kt

https://github.com/androidx/androidx/blob/b61de2798bcf4bfb94de52a49e6cc36902ad78d6/buildSrc/private/src/main/kotlin/androidx/build/sources/SourceJarTaskHelper.kt#L54