diff --git a/TICKETS.md b/TICKETS.md index 6ef6d5b..cbff22f 100644 --- a/TICKETS.md +++ b/TICKETS.md @@ -24,7 +24,7 @@ Canonical write-up: `docs/VISION.md` § Root principles. Plugins design: `docs/T | F-004 | done | CI green on GitHub Actions for plugins + application | Temurin 17 all jobs + Android SDK 33 for app; PR #153 GHA green | | F-018 | done | JDK 21 + Gradle/AGP staged modernization | **Shipped on `v2`:** Gradle **8.14.5**, AGP **8.13.2**, Kotlin **2.0.21**, KSP **2.0.21-1.0.28**, Compose **1.9.4**/compiler **2.0.21**, JDK **21** host/CI, sample SDK min23/target35/compile35 + deps at AGP-8.13 ceiling. PRs **#179–#182**. Superseded by **F-019** for 9.x. | | F-019 | done | Gradle 9 + Kotlin 2.3 + AGP 9 toolchain | Phase 1 done (9.6.1 / 2.3.21 / AGP 9.3.0). **F-086** = kapt→KSP + built-in Kotlin. AndroidX ceiling → **F-087**. | -| F-086 | done | Migrate kapt → KSP + AGP built-in Kotlin | First-class `Ksp` + `String.ksp`; `processorConfigurationFeatures()`; sample Dagger on **ksp**; drop F-019 kapt bridge flags; legacy `.kapt` kept until **F-093**. | +| F-086 | done | Migrate kapt → KSP + AGP built-in Kotlin | First-class `Ksp` + `String.ksp`; `processorConfigurationFeatures()`; sample Dagger on **ksp**; drop F-019 kapt bridge flags; legacy `.kapt` removed in **F-093**. | ## P1 — Android working product @@ -124,7 +124,7 @@ Promoted from daily next-actions / historical GH. Workers pick top `todo` in ord | F-090 | done | Exclude modules from dependency validation | GH **#97** — `dependencyValidationExclusions` on root `androidProjectConfiguration` / `AndroidProjectSettings`; `applyDependencies` skips project-dep suffix validation for exact path/name matches only. Self-type validation unchanged. Unit tests in `:config` + `:deps`; matrix + PROJECT-CONFIGURATION docs. | | F-091 | done | BuildFeatures under Forma | GH **#88** — `FormaBuildFeatures` project-global (defaults **off**); central apply on library+binary; type-owned viewBinding/composeWidget + minimal `impl`/`compose` attrs; docs CALL-SITE + PROJECT-CONFIGURATION. | | F-092 | done | versionCode / versionName on binary (and app) | GH **#82** — **done:** required `versionCode`/`versionName` on `androidBinary` only (wired to AGP `defaultConfig`); **not** on `AndroidProjectSettings` / `androidProjectConfiguration`; `androidApp` stays library shell (no version attrs — Gradle app-module limit). Sample + examples already use call-site API; docs/KDoc locked. | -| F-093 | todo | Remove legacy `.kapt` once unused | After F-086 sample is KSP-only: hard-remove or hard-deprecate `String.kapt` / `kapt()` / `Kapt` configuration path if no in-repo consumers; keep only if a documented external-compat shim is still required. Grep tree + tests + DEPS docs. | +| F-093 | done | Remove legacy `.kapt` once unused | **KSP-only:** hard-removed `String.kapt` / `kapt()` / `object Kapt` / `kotlin-kapt` auto-apply / `kaptConfigurationFeature`. Zero in-repo consumers after F-086; no external-compat shim. | | F-094 | blocked | Gradle Plugin Portal Forma org/user | GH **#133** — human/admin: create shared Portal user/org and credentials for team publish. Worker cannot finish without Stepan Portal access. Track only; see `docs/PLUGIN-PUBLISH.md`. | ## Backlog (lower priority / historical GitHub) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index b9962b0..a48a542 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -185,7 +185,7 @@ Under `tools.forma.android.feature`: - `FeatureDefinition` + `applyFeatures` - `androidLibraryFeatureDefinition` / `androidBinaryFeatureDefinition` / `androidNativeDefinition` - Kotlin JVM vs Kotlin Android feature definitions -- `kaptConfigurationFeature` (auto when kapt-ish deps present) +- `processorConfigurationFeatures` (auto-applies KSP when `.ksp` deps present) Configuration singleton: `Forma` object delegates to `FormaSettingsStore` (`AndroidProjectSettings`: min/target/compile SDK, AGP/Kotlin versions, repos, diff --git a/docs/CONFIGURATION-PERFORMANCE.md b/docs/CONFIGURATION-PERFORMANCE.md index 41533d1..1638322 100644 --- a/docs/CONFIGURATION-PERFORMANCE.md +++ b/docs/CONFIGURATION-PERFORMANCE.md @@ -64,7 +64,7 @@ definitions, skip empty repo blocks), not as a claim of a fixed wall-clock %. | Area | Change | |------|--------| | `validator(...)` | Identity-cache single- and multi-suffix validators; hot path uses precomputed `endsWith` strings, no intermediate `map`/`contains` lists | -| `kotlinFeatureDefinition` / `kotlinAndroidFeatureDefinition` / kapt | Singleton `FeatureDefinition` instances; read live `Forma.settings` at apply time | +| `kotlinFeatureDefinition` / `kotlinAndroidFeatureDefinition` / KSP processor | Singleton `FeatureDefinition` instances; read live `Forma.settings` at apply time | | `applyDependencies` | Skip `repositories {}` when config is the empty sentinel; skip entire `dependencies {}` block when all three dep args are `EmptyDependency`; skip plugin lookup when no plugin deps registered | | `deps` / `FormaDependency.plus` | Prefer typed fields over `filterIsInstance`; pre-size / single-pass merges; indexed `forEach` over specs | | Catalog generators | `filteredTokens` is a `Set` for O(1) membership | diff --git a/docs/DEPS-CATALOG.md b/docs/DEPS-CATALOG.md index e2fecd1..74292a7 100644 --- a/docs/DEPS-CATALOG.md +++ b/docs/DEPS-CATALOG.md @@ -108,6 +108,9 @@ custom configuration automatically. ### Plugin + processor wiring +**Annotation processing happy path is KSP only** (`String.ksp` / `ksp()` / `Ksp`). +Legacy `.kapt` / `kotlin-kapt` was hard-removed in F-093 — there is no dual path. + ```kotlin plugin( id = "com.google.devtools.ksp:symbol-processing-gradle-plugin", @@ -115,10 +118,14 @@ plugin( configuration = CustomConfiguration("ksp"), "androidx.room:room-compiler:2.5.1" // registered on the `ksp` configuration ) + +// Or at the call site with the typed DSL: +dependencies = deps("com.google.dagger:dagger-compiler:…".ksp) ``` -At dependency application time, consuming the room-compiler coordinate applies the KSP -plugin once and adds the processor with `isTransitive = true`. +At dependency application time, consuming a `.ksp` coordinate applies +`com.google.devtools.ksp` once via `processorConfigurationFeatures()` and adds the +processor with `isTransitive = true`. --- diff --git a/docs/PRINCIPLE-AUDIT.md b/docs/PRINCIPLE-AUDIT.md index fd86b8e..de966a6 100644 --- a/docs/PRINCIPLE-AUDIT.md +++ b/docs/PRINCIPLE-AUDIT.md @@ -98,7 +98,7 @@ Not opened as new product tickets here (board empty after F-085; promote only on - Optional Path B `firebaseBinary` for Crashlytics/GMS apply (sample currently classpath + comment) - F-084 follow-ups (Gradle tasks, AST migrate) — listed in FLEET-TOOLING.md - F-019 AndroidX absolute ceiling / compileSdk chase -- Legacy `.kapt` API removal once no consumers +- ~~Legacy `.kapt` API removal once no consumers~~ → **F-093** done (KSP-only) - Backlog GH issues (#110, #97, #88, #82, …) ## Re-audit recipe diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 38dbdde..9fcc754 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -2,6 +2,22 @@ Newest entries first. +## 2026-07-22 — F-093: hard-remove legacy kapt (KSP-only) + +- **Ticket:** F-093 → `done` +- **Branch:** `forma/F-093-remove-kapt` (from `origin/v2`) +- **Actions:** + - Removed public DSL: `fun kapt` / `String.kapt` (`dependencies.kt`), `DependencyHandler.kapt` + - Removed `object Kapt` from `ConfigurationType.kt` + - Removed `kotlinKaptFeatureDefinition` / `Kapt` branch / deprecated `kaptConfigurationFeature` from `Kotlin.kt`; `processorConfigurationFeatures()` is KSP-only + - Tests: drop Kapt assertion; add `ksp` helper coverage + - JaCoCo: drop dead `Kapt*` include + - Docs: DEPS-CATALOG, CONFIGURATION-PERFORMANCE, ARCHITECTURE, forma-core-api, PRINCIPLE-AUDIT, impl KDoc; TICKETS F-093 done +- **Verify:** (this run) plugins `test jacocoHappyPathCoverageVerification build`; application `:binary:assembleDebug` +- **Commits/PRs:** commit on branch; Hermes may open PR +- **Blockers:** none +- **Next step:** F-094 (blocked Portal) or next open P8/backlog ticket + ## 2026-07-22 — F-092: versionCode / versionName on binary (GH #82) - **Ticket:** F-092 → `done` diff --git a/docs/forma-core-api.md b/docs/forma-core-api.md index ff3b02c..21de494 100644 --- a/docs/forma-core-api.md +++ b/docs/forma-core-api.md @@ -39,7 +39,7 @@ free of Bazel dependencies. JVM kit is the initial target set. | Concern | Stays in | |---------|----------| -| AGP / Kotlin Android / Compose / kapt feature appliers | `:android` (or future platform plugins) | +| AGP / Kotlin Android / Compose / KSP feature appliers | `:android` (or future platform plugins) | | User-facing Gradle DSL entrypoints (`fun Project.impl(…)`) | platform plugins | | Plugin Portal publish DSL (`formaPluginConfiguration`) | `plugins/buildSrc` | | Includer / depgen | separate builds | @@ -328,7 +328,7 @@ fun Project.impl(/* platform params */) { | Type | Notes | |------|--------| | `FormaDependency` sealed hierarchy | `Empty`, `Named`, `Target`, `File`, `Platform`, `Mixed` | -| `DepSpec` / `ConfigurationType` | `Implementation`, `Kapt`, `CustomConfiguration`, … | +| `DepSpec` / `ConfigurationType` | `Implementation`, `Ksp`, `CustomConfiguration`, … | | `deps(…)`, `plus`, `forEach` | pure merge helpers (F-017 hot path) | | Project-edge validation hook | call `TargetValidator` before adding project dep | diff --git a/plugins/android/src/main/java/impl.kt b/plugins/android/src/main/java/impl.kt index 835ba38..b4f2509 100644 --- a/plugins/android/src/main/java/impl.kt +++ b/plugins/android/src/main/java/impl.kt @@ -17,7 +17,7 @@ import tools.forma.validation.validate /** - * Feature **implementation** (Android library + optional view binding / kapt / Compose). + * Feature **implementation** (Android library + optional view binding / KSP / Compose). * * Dagger2-friendly boundaries: * - **May** depend on feature `api` contracts, shared libraries/utils, and UI diff --git a/plugins/android/src/main/java/tools/forma/android/feature/Kotlin.kt b/plugins/android/src/main/java/tools/forma/android/feature/Kotlin.kt index df041c0..fe023cb 100644 --- a/plugins/android/src/main/java/tools/forma/android/feature/Kotlin.kt +++ b/plugins/android/src/main/java/tools/forma/android/feature/Kotlin.kt @@ -1,16 +1,12 @@ package tools.forma.android.feature -import deps -import kapt import tools.forma.config.AndroidProjectSettings import org.gradle.api.Project import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension -import org.jetbrains.kotlin.gradle.plugin.KaptExtension import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.gradle.api.tasks.compile.JavaCompile import tools.forma.deps.core.ConfigurationType -import tools.forma.deps.core.Kapt import tools.forma.deps.core.Ksp private fun defaultConfiguration(project: Project, androidProjectSettings: AndroidProjectSettings) { @@ -51,27 +47,14 @@ private val kotlinFeatureDefinitionInstance = configuration = sharedFeatureConfiguration ) -/** Legacy kapt path — still supported if a target declares `.kapt` deps. */ -private val kotlinKaptFeatureDefinitionInstance = - FeatureDefinition( - pluginName = "kotlin-kapt", - pluginExtension = KaptExtension::class, - featureConfiguration = Unit, - defaultDependencies = deps("org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.2.0".kapt), - configuration = sharedFeatureConfiguration - ) - fun kotlinFeatureDefinition() = kotlinFeatureDefinitionInstance /** Config-only under AGP 9 built-in Kotlin (no `kotlin-android` plugin). */ fun kotlinAndroidFeatureDefinition() = kotlinAndroidFeatureDefinitionInstance -fun kotlinKaptFeatureDefinition() = kotlinKaptFeatureDefinitionInstance - /** - * Lazy processor plugins when a target declares kapt/ksp deps. - * - [Ksp] → apply `com.google.devtools.ksp` (preferred; F-086) - * - [Kapt] → apply `kotlin-kapt` (legacy; incompatible with built-in Kotlin) + * Lazy processor plugins when a target declares KSP deps. + * - [Ksp] → apply `com.google.devtools.ksp` (sole annotation-processing path; F-093) */ fun Project.processorConfigurationFeatures(): Map Unit> = mapOf( @@ -80,12 +63,4 @@ fun Project.processorConfigurationFeatures(): Map Unit> pluginManager.apply("com.google.devtools.ksp") } }, - Kapt to { - applyFeatures(kotlinKaptFeatureDefinition()) - } ) - -/** @deprecated Use [processorConfigurationFeatures] (includes KSP). */ -@Deprecated("Use processorConfigurationFeatures()", ReplaceWith("processorConfigurationFeatures()")) -fun Project.kaptConfigurationFeature(): Map Unit> = - processorConfigurationFeatures() diff --git a/plugins/buildSrc/src/main/kotlin/formaCoverage.kt b/plugins/buildSrc/src/main/kotlin/formaCoverage.kt index b5bddda..10eb1b4 100644 --- a/plugins/buildSrc/src/main/kotlin/formaCoverage.kt +++ b/plugins/buildSrc/src/main/kotlin/formaCoverage.kt @@ -53,7 +53,6 @@ internal val happyPathClassIncludes = "**/tools/forma/deps/core/CompileOnly*", "**/tools/forma/deps/core/RuntimeOnly*", "**/tools/forma/deps/core/AnnotationProcessor*", - "**/tools/forma/deps/core/Kapt*", "**/tools/forma/deps/core/Ksp*", "**/tools/forma/deps/core/DepSpec*", "**/tools/forma/deps/core/TargetSpec*", diff --git a/plugins/deps/src/main/java/dependencies.kt b/plugins/deps/src/main/java/dependencies.kt index fc5eeaa..bbded2f 100644 --- a/plugins/deps/src/main/java/dependencies.kt +++ b/plugins/deps/src/main/java/dependencies.kt @@ -13,7 +13,6 @@ import tools.forma.deps.core.FileDependency import tools.forma.deps.core.FileSpec import tools.forma.deps.core.FormaDependency import tools.forma.deps.core.Implementation -import tools.forma.deps.core.Kapt import tools.forma.deps.core.Ksp import tools.forma.deps.core.MixedDependency import tools.forma.deps.core.NameSpec @@ -224,9 +223,6 @@ fun deps(vararg dependencies: TargetDependency): TargetDependency { return TargetDependency(out) } -fun kapt(vararg names: String): NamedDependency = - NamedDependency(names.map { NameSpec(it, Kapt, true) }) - fun ksp(vararg names: String): NamedDependency = NamedDependency(names.map { NameSpec(it, Ksp, true) }) @@ -236,9 +232,6 @@ fun String.dep(configuration: CustomConfiguration, transitive: Boolean = true) = val String.dep: NamedDependency get() = deps(this) -val String.kapt: NamedDependency - get() = kapt(this) - val String.ksp: NamedDependency get() = ksp(this) diff --git a/plugins/deps/src/main/java/tools.forma/deps/core/ConfigurationType.kt b/plugins/deps/src/main/java/tools.forma/deps/core/ConfigurationType.kt index 24912f8..ac0fa99 100644 --- a/plugins/deps/src/main/java/tools.forma/deps/core/ConfigurationType.kt +++ b/plugins/deps/src/main/java/tools.forma/deps/core/ConfigurationType.kt @@ -23,11 +23,7 @@ object AnnotationProcessor : ConfigurationType { override val name: String = "annotationProcessor" } -object Kapt : ConfigurationType { - override val name: String = "kapt" -} - -/** Kotlin Symbol Processing configuration (`ksp`). Prefer over [Kapt] for new code (F-086). */ +/** Kotlin Symbol Processing configuration (`ksp`). Sole annotation-processing path (F-093). */ object Ksp : ConfigurationType { override val name: String = "ksp" } diff --git a/plugins/deps/src/main/java/tools.forma/deps/core/DependencyHandler.kt b/plugins/deps/src/main/java/tools.forma/deps/core/DependencyHandler.kt index d4fe1fd..79d64d8 100644 --- a/plugins/deps/src/main/java/tools.forma/deps/core/DependencyHandler.kt +++ b/plugins/deps/src/main/java/tools.forma/deps/core/DependencyHandler.kt @@ -15,16 +15,6 @@ import org.gradle.kotlin.dsl.accessors.runtime.addExternalModuleDependencyTo import org.gradle.kotlin.dsl.add import org.gradle.kotlin.dsl.create -/** - * Adds a dependency to the `kapt` configuration. - * - * @param dependencyNotation name of dependency to add at specific configuration - * - * @return the dependency - */ -fun DependencyHandler.kapt(dependencyNotation: String): Dependency? = - add("kapt", dependencyNotation) - /** Adds a dependency to the `ksp` configuration. */ fun DependencyHandler.ksp(dependencyNotation: String): Dependency? = add("ksp", dependencyNotation) diff --git a/plugins/deps/src/test/kotlin/tools/forma/deps/core/DepsModelAndPluginHappyPathTest.kt b/plugins/deps/src/test/kotlin/tools/forma/deps/core/DepsModelAndPluginHappyPathTest.kt index 9a17b70..63d80df 100644 --- a/plugins/deps/src/test/kotlin/tools/forma/deps/core/DepsModelAndPluginHappyPathTest.kt +++ b/plugins/deps/src/test/kotlin/tools/forma/deps/core/DepsModelAndPluginHappyPathTest.kt @@ -5,6 +5,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertSame import kotlin.test.assertTrue +import ksp import tools.forma.core.target.targetType /** @@ -19,11 +20,21 @@ class DepsModelAndPluginHappyPathTest { assertEquals("compileOnly", CompileOnly.name) assertEquals("runtimeOnly", RuntimeOnly.name) assertEquals("annotationProcessor", AnnotationProcessor.name) - assertEquals("kapt", Kapt.name) assertEquals("ksp", Ksp.name) assertEquals("ksp", CustomConfiguration("ksp").name) } + @Test + fun `ksp helpers place specs on Ksp configuration`() { + val viaFun = ksp("g:processor:1") + val viaExt = "g:processor:1".ksp + assertEquals(1, viaFun.names.size) + assertEquals(Ksp, viaFun.names.single().config) + assertTrue(viaFun.names.single().transitive) + assertEquals("g:processor:1", viaFun.names.single().name) + assertEquals(Ksp, viaExt.names.single().config) + } + @Test fun `dependency model constructors preserve specs`() { val name = NameSpec("g:a:1", Implementation, transitive = true)