Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# CI(docs/チェックリスト.md §7)。基本は ubuntu(ルート build / integration-test のモジュールビルド /
# TestKit の分割実行)で、OS 分岐は必要な範囲のみ: macos = Apple ターゲットのネイティブツールチェーン確認
# TestKit の分割実行 / Maven ビルド)で、OS 分岐は必要な範囲のみ: macos = Apple ターゲットのネイティブツールチェーン確認
# (ルート build のみ)・windows = mingwX64 の実行テストのみ(native の box 実行はホスト依存ゲートで
# Windows でしか走らない)。main への push では加えて依存グラフを登録する。
# アクションはコミットハッシュで固定する(末尾コメントの vX.Y.Z は参照用。更新は Dependabot が追随する)
Expand Down Expand Up @@ -67,8 +67,8 @@ jobs:
key: konan-${{ runner.os }}-${{ hashFiles('gradle/libs.versions.toml') }}
- run: ./gradlew build

# integration-test は 8 モジュールのビルドと TestKit テストを別ジョブへ分ける
# 同一ジョブだと両者がランナーのコアを奪い合い、TestKit の開始が待たされる
# integration-test は 9 モジュールのビルドと、重いテスト(TestKit / Maven)を別ジョブへ分ける
# 同一ジョブだと互いにランナーのコアを奪い合い、開始が待たされる
integration-modules:
runs-on: ubuntu-latest
timeout-minutes: 90
Expand All @@ -83,7 +83,9 @@ jobs:
with:
path: ~/.konan
key: konan-${{ runner.os }}-${{ hashFiles('gradle/libs.versions.toml') }}
- run: ./gradlew -p integration-test build -x :gradle-integration:test
- run: >-
./gradlew -p integration-test build
-x :gradle-integration:test -x :maven-integration:test

# TestKit テストはランナーを増やして分割する。ランナーのコア数ではジョブ内の並行度を上げても
# 実行時間が縮まらないため(同時実行 2 で 1 割・4 で頭打ち)、ジョブ内は直列にして分割数で稼ぐ。
Expand Down Expand Up @@ -112,6 +114,27 @@ jobs:
./gradlew -p integration-test :gradle-integration:test --rerun
-PtestShardIndex=${{ matrix.shard }} -PtestShardCount=3 -PtestKitParallelism=1

# Maven ビルドの検証(docs/test/ケース06-ビルド動態.md §7)。Maven 本体は Gradle 側の依存として
# 取得するためインストール手順は要らない。フィクスチャは Kotlin 一式をテスト専用のローカルリポジトリ
# (~/.m2/repository とは別。docs/test/フィクスチャ構成.md §6)へ取得するため、そこをキャッシュする
integration-maven:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
with:
distribution: temurin
java-version: 17
- uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: integration-test/maven-integration/build/maven-local-repo
key: maven-repo-${{ runner.os }}-${{ hashFiles('gradle/libs.versions.toml') }}
# フィクスチャの入力にはローカル Maven の非タイムスタンプ SNAPSHOT が含まれ UP-TO-DATE 判定が
# 信用できないため --rerun で必ず再実行する(TestKit と同じ理由)
- run: ./gradlew -p integration-test :maven-integration:test --rerun

build-macos:
runs-on: macos-latest
timeout-minutes: 60
Expand Down
59 changes: 55 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ operational API of enums on top.

## Setup

### Gradle

```kotlin
plugins {
kotlin("multiplatform") version "2.4.10" // or kotlin("jvm") — any Kotlin target plugin
Expand All @@ -38,6 +40,54 @@ sealedClassEnumizer {
}
```

### Maven

Declare the plugin as a dependency of kotlin-maven-plugin and name it in `compilerPlugins`; the
compiler plugin follows as a transitive dependency. It applies to every kotlin-maven-plugin
execution, production and test compilations alike.

```xml
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<configuration>
<compilerPlugins>
<plugin>sealed-class-enumizer</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>io.github.projectmapk</groupId>
<artifactId>sealed-class-enumizer-maven-plugin</artifactId>
<version>2.4.10-0.1.0</version>
</dependency>
</dependencies>
</plugin>
```

Unlike the Gradle plugin, the runtime API is not added for you — declare it as a project
dependency (Maven resolves the platform artifact, hence the `-jvm` suffix):

```xml
<dependency>
<groupId>io.github.projectmapk</groupId>
<artifactId>sealed-class-enumizer-runtime-api-jvm</artifactId>
<version>2.4.10-0.1.0</version>
</dependency>
```

The project-wide default label case is a property:

```xml
<properties>
<sealed-class-enumizer.labelCase>AS_DECLARED</sealed-class-enumizer.labelCase>
</properties>
```

The same option can be given through kotlin-maven-plugin's `pluginOptions`
(`sealed-class-enumizer:labelCase=...`), which takes precedence over the property.

### IntelliJ IDEA

IntelliJ's K2 mode does not load third-party compiler plugins by default, so generated
Expand Down Expand Up @@ -161,7 +211,8 @@ Labels default to the leaf's simple name and resolve with this priority:
1. `@EnumishLabel("...")` on the leaf — used as-is (no case conversion); for resolving label
clashes and keeping persisted labels stable across renames
2. `@Enumize(labelCase = ...)` on the hierarchy
3. The project-wide default from the Gradle DSL (`sealedClassEnumizer { labelCase = ... }`)
3. The project-wide default from the build — the Gradle DSL `sealedClassEnumizer { labelCase }`
or the Maven `sealed-class-enumizer.labelCase` property
4. `AS_DECLARED` (no conversion)

Available cases: `AS_DECLARED`, `UPPER_SNAKE_CASE`, `SNAKE_CASE`, `KEBAB_CASE`.
Expand All @@ -176,7 +227,7 @@ Label uniqueness within a hierarchy is enforced at compile time.
|---|---|
| Kotlin | 2.4.x, K2 only. The plugin version encodes the Kotlin minor it targets; applying it to another Kotlin minor produces a build warning |
| Version format | `<KotlinVersion>-<pluginVersion>`, e.g. `2.4.10-0.1.0` |
| Build environment | Gradle 9+ / JDK 17+ (verified with Gradle 9.6) |
| Build environment | Gradle 9+ or Maven 3.9+ / JDK 17+ (verified with Gradle 9.6 and Maven 3.9) |
| runtime-api (JVM) | Java 8+ bytecode |
| runtime-api targets | `jvm`, `js`, `wasmJs`, `wasmWasi`, `linuxX64`, `mingwX64`, `macosX64`, `macosArm64`, `iosArm64`, `iosSimulatorArm64`, `iosX64` |

Expand All @@ -192,8 +243,8 @@ Label uniqueness within a hierarchy is enforced at compile time.
## Versioning and support policy

- Versions follow `<KotlinVersion>-<pluginVersion>`.
All published artifacts (runtime API, compiler plugin, Gradle plugin and its marker) share the
same version.
All published artifacts (runtime API, compiler plugin, Gradle plugin and its marker, Maven
plugin) share the same version.
- The Kotlin compiler plugin API has no stability guarantee, so each release targets exactly one
Kotlin minor; support covers the latest stable Kotlin minor including its patch releases.
- New Kotlin releases are followed by new plugin releases; older minors receive no backports.
Expand Down
5 changes: 3 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ allprojects {
ktfmt { kotlinLangStyle() }
}

// API ドキュメント(GitHub Pages 掲載用)の集約。対象は公開 API を持つ 2 モジュールで、
// compiler-plugin は内部実装のため含めない。集約出力は build/dokka/html
// API ドキュメント(GitHub Pages 掲載用)の集約。対象は利用者がコードから触る公開 API を持つ 2 モジュールで、
// compiler-plugin(内部実装)と maven-plugin(利用面は POM の設定であり Kotlin API ではない)は含めない。
// 集約出力は build/dokka/html
dependencies {
dokka(project(":sealed-class-enumizer-runtime-api"))
dokka(project(":sealed-class-enumizer-gradle-plugin"))
Expand Down
18 changes: 16 additions & 2 deletions docs/test/ケース06-ビルド動態.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# ケース06 ビルド動態

本資料は K14(ビルド動態)× O2/O5 の正典である。
本資料は K14(ビルド動態)× K17(ビルドツール)× O2/O5 の正典である。
編集ケース #1〜#12(7-b/7-c 含む)・@Enumize 付与除去・決定性・基底不在ラウンド・
空⇔非空遷移・跨 module ABI 伝播・旧バイナリ差し替え・runtime-api 公開・MPP klib ラウンドを担う。
空⇔非空遷移・跨 module ABI 伝播・旧バイナリ差し替え・runtime-api 公開・MPP klib ラウンド・
Maven ビルドを担う。
実行時 OUT 照合(O1)は clean 一致判定の手段として併用する。
他資料の IC・決定性に関する観測は本資料への参照で足りる(テスト戦略 §3 C4)。

Expand Down Expand Up @@ -83,3 +84,16 @@
| ID | 次元/値 | 観測と期待 | 実装 | 備考(関連V/診断) |
|---|---|---|---|---|
| BLD-48 | K14/K16=プロジェクト既定の伝達 | DSL `sealedClassEnumizer { labelCase = SNAKE_CASE }` がコンパイラオプション(labelCase。初のオプション経路)として届き、labelCase 未指定の階層へ適用される・@Enumize の具体指定(KEBAB_CASE)は DSL に勝つ。検証は label-case フィクスチャ内 JUnit(test タスク)の成功で行う | LabelCaseOptionTest#dslDefaultPropagatesToCompilerOption | convention 側(DSL 未設定 = AS_DECLARED)はケース01 API-55 |

## 7. Maven ビルド

Gradle 以外のビルドツール上での適用を担う(実 Maven ビルドで駆動。フィクスチャは
[フィクスチャ構成](フィクスチャ構成.md) §6)。

| ID | 次元/値 | 観測と期待 | 実装 | 備考(関連V/診断) |
|---|---|---|---|---|
| BLD-49 | K17=適用範囲 | `<compilerPlugins>` へ記載すると kotlin-maven-plugin の compile / test-compile の双方へ適用され(適用ログ 2 件)、main / test 双方の階層で生成 API が解決・実行できる。label はプロジェクト既定を置かない構成で組み込み既定(AS_DECLARED) | MavenBuildTest#appliesToProductionAndTestCompilations | コンパイラプラグイン本体は maven-plugin の推移依存として載る・runtime-api は利用側宣言(概要 §7) |
| BLD-50 | K17/K16=プロジェクト既定の伝達 | プロパティ `sealed-class-enumizer.labelCase` の値がコンパイラオプションとして届き、labelCase 未指定の階層へ適用される・@Enumize の具体指定はプロジェクト既定に勝つ | MavenBuildTest#projectDefaultLabelCaseReachesTheCompiler | Gradle 側の対応は BLD-48 |
| BLD-51 | K17/K16=実行単位指定の優先 | `<pluginOptions>` の `sealed-class-enumizer:labelCase=...` がプロジェクト既定に優先する(拡張側が渡さないことで成立する。コンパイラのオプションは複数指定を受け付けない) | MavenBuildTest#executionLevelPluginOptionOverridesTheProjectDefault | 二重指定はコンパイラ側の失敗になるため、優先関係は拡張の抑止で実現する |
| BLD-52 | K17×O2=診断のビルド失敗 | 非 sealed 宣言への `@Enumize` が ENUMIZE_NOT_SEALED としてコンパイルを失敗させ、Maven ビルドが失敗する | MavenBuildTest#compilerDiagnosticFailsTheBuild | 診断内容の正典はケース04 |
| BLD-53 | K17=設定値の不正 | labelCase プロパティの不正値は、受け付ける値の一覧を添えたビルド失敗になる(コンパイラへ渡る前に設定面の名前を示す) | MavenBuildTest#unknownLabelCasePropertyFailsTheBuild | — |
12 changes: 8 additions & 4 deletions docs/test/テスト戦略.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@

## 1. 検証対象と非対象

- 検証対象は CLI / Gradle ビルドの成否・診断・生成物・実行時挙動に限る。
- 検証対象は CLI / Gradle / Maven ビルドの成否・診断・生成物・実行時挙動に限る。
IDE(K2) はサードパーティ FIR プラグインを既定でロードしない(KTIJ-29248)ため、
IDE / Analysis API 側は検証対象外とする(既定の IDE 体験は概要 §6・設計00 §11 が既知の制限として持つ)。
- 環境: Kotlin 2.4.10 / Gradle 9.6.1 / JVM toolchain 17 / `group=io.github.projectmapk`。
- 環境: Kotlin 2.4.10 / Gradle 9.6.1 / Maven 3.9.12 / JVM toolchain 17 / `group=io.github.projectmapk`。
- 比較規約: 値の比較はなるべく観測用 data class を組み立て 1 回の `assertEquals` で行う。
kind の同一性は参照比較(`===`)で行う。

Expand All @@ -38,6 +38,7 @@ Kotlin 言語仕様がこのプラグインに対して張る空間を次の次
| K14 | ビルド動態 | clean / 編集ケース #1〜#12 / @Enumize 付与・除去 / 決定性 4 形態+ロケール / 基底不在ラウンド / 空⇔非空遷移 |
| K15 | 実行時状態 | 遅延初期化 / 初期化子再入 / マルチスレッド初回 / boxing / SAM 変換 / 多段サブタイプ / 無名 object・local クラスによる末端実装 |
| K16 | label 決定 | labelCase(未指定 / PROJECT_DEFAULT 明示 / 具体 4 種)× 明示 @EnumishLabel(無 / 有 / 空白 / 不正な付与先 4 形 = 基底・中間 sealed・階層外・kind companion)× プロジェクト既定(DSL 指定 / convention / 組み込み既定)‖ 衝突(変換由来・明示由来)・enum 末端 / companion 末端への一律適用・data object toString の乖離 |
| K17 | ビルドツール | Gradle(`KotlinCompilerPluginSupportPlugin`)/ Maven(kotlin-maven-plugin 拡張)‖ 各ツールの設定面: 適用範囲(production / test)・プロジェクト既定の label ケース・実行単位のオプション指定・runtime-api の供給(Gradle=自動 / Maven=利用側宣言)・設定値の不正 |

観測面(1 ケースは 1 つ以上の観測面を持つ):

Expand Down Expand Up @@ -67,9 +68,11 @@ Kotlin 言語仕様がこのプラグインに対して張る空間を次の次

## 4. テストモジュール構成

integration-test は親ビルドを composite 参照する独立 Gradle ビルドであり、8 モジュールから成る。
integration-test は親ビルドを composite 参照する独立 Gradle ビルドであり、9 モジュールから成る。
「コンパイル成功で証明できる正値」は通常モジュール、「コンパイル失敗・2 回ビルド差分・バイト一致・別版組合せ」は
TestKit(gradle-integration)に置く。
TestKit(gradle-integration)に置く。
Gradle 以外のビルドツール上の適用は専用モジュール(maven-integration)が担い、
ツール本体を配布物として取得して実ビルドを駆動する(ホストへのインストールを前提にしない)。

| モジュール | プラグイン | 役割 |
|---|---|---|
Expand All @@ -81,6 +84,7 @@ TestKit(gradle-integration)に置く。
| mpp-producer | 適用 | MPP 生成側。common フィクスチャ+全 5 ターゲット box・klib/metadata(V5) |
| mpp-consumer | 未適用 | MPP 純消費。common からの生成 API 参照・跨 module×MPP の when |
| gradle-integration | TestKit | 診断(負値・near-miss)・IC 回帰・決定性・ABI 伝播・旧バイナリ・基底不在ラウンド |
| maven-integration | Maven 駆動 | kotlin-maven-plugin 拡張としての適用範囲・label ケースの伝達と優先・診断と設定不正のビルド失敗(K17) |

## 5. 状態と ID 規約

Expand Down
29 changes: 29 additions & 0 deletions docs/test/フィクスチャ構成.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,32 @@ src/test には test compilation 配線検証用の小さな @Enumize 階層(T
- 順序フィクスチャの期待値は FQN の UTF-16 序数から機械的に導出し、期待列をコメントで併記する。
- producer-jvm のフィクスチャで「1 ファイル 1 クラス」規約より配置制約(同一ファイル要求・
複数トップレベル宣言)が優先される場合は、ファイル先頭コメントで適用外を明示する。

## 6. maven-integration(Maven フィクスチャ)

フィクスチャは 1 プロジェクト = 1 POM の合成 Maven プロジェクトであり、実 Maven ビルドで駆動する。

| フィクスチャ | 構成 | 駆動内容 |
|---|---|---|
| basic | main = labelCase 未指定階層 + 具体指定階層(KEBAB_CASE)・test = 独自階層 + JUnit ‖ プロファイル 2 種(プロジェクト既定のプロパティ・実行単位の `<pluginOptions>` 指定) | 適用範囲(production / test の両コンパイレーション)と label ケースの決定(組み込み既定 / プロジェクト既定 / 実行単位指定 / 階層個別の具体指定)を実行時 OUT 行で照合 |
| diag | 非 sealed 宣言への `@Enumize` ‖ プロファイル(labelCase の不正値) | 診断と設定不正値がビルド失敗として表面化することの照合 |

運用方針:

- プラグイン注入は local-repo 経路(前段で `publishAllToMavenLocal`)。
ただし Maven のローカルリポジトリは**テスト専用の場所へ隔離**し、本プロジェクトの成果物だけを
そこへ複製して解決する(複製は MavenHarness)。
既定の `~/.m2/repository` を使うと、Maven が取得する Gradle module metadata を伴わない成果物が
そこへ入り、`mavenLocal()` を宣言する TestKit フィクスチャの variant 解決(capability による
`kotlin-test` の選択など)を壊すためである。
- フィクスチャは版を直書きせず、`%%ENUMIZER_VERSION%%` / `%%KOTLIN_VERSION%%` を展開時に置換する
(値の正は親ビルド。展開は MavenHarness が行う)。
- Maven 本体は配布物(`org.apache.maven:apache-maven`)をビルドの依存として取得し、
ホストへのインストールを前提にしない。
起動は `bin` のスクリプトではなく classworlds の Launcher を直接叩く(zip
展開では実行権限が落ちるため)。JVM はテストと同じ toolchain を使う。
- 実行は直列とする(Maven のローカルリポジトリは共有資源であり、並行ダウンロードでの破損を避ける)。
- 展開先は実行前に回収する(§4 と同じ)。
- 検証手段は 2 種: ビルドの成否と出力照合(適用ログ・診断文言)・フィクスチャ内 JUnit が出す OUT 行。
設定で変わる値(label)は OUT 行で照合し、設定に依らない不変はフィクスチャ内で表明する。
- runtime-api は Maven では自動追加されないため(概要 §7)、フィクスチャが依存として明示宣言する。
Loading