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
8 changes: 5 additions & 3 deletions TICKETS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ Canonical write-up: `docs/VISION.md` § Root principles. Plugins design: `docs/T
| F-003 | done | Get plugins + sample `application/` building on modern toolchain | Plugins compile AGP 8.1.2 matches sample; Gradle 8.3 (plugins) / 8.4 (app); host builds green |
| 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 | **Shipped Phase 1:** Gradle **9.6.1** (embedded Kotlin **2.3.21**), AGP **9.3.0**, KSP **2.3.10**, Compose compiler **2.3.21**, aapt2-proto **9.3.0-15703166**, Dagger **2.60.1**. Sample uses `android.builtInKotlin=false` + `android.newDsl=false` (kapt/Dagger still). **Phase 2 backlog:** built-in Kotlin + kapt→KSP + AndroidX ceiling. |
| 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 still optional follow-up. |
| 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. |

## P1 — Android working product

Expand Down Expand Up @@ -100,7 +101,7 @@ Close the biggest call-site / multi-way gap: chain `withPlugin`. Design:

Close remaining gaps where code/docs still allow **multiple ways**, **fat call
sites**, or **missing fleet tooling**. P7 (F-070–F-073) and F-081–F-082 are **done**; **F-019 Phase 1 done**.
**Next coding:** **F-083**.
**Next coding:** **F-083**

| ID | Status | Title | Notes |
|----|--------|-------|-------|
Expand Down Expand Up @@ -128,7 +129,8 @@ Keep for reference; do not start unless higher tickets done or user prioritizes:
- GH #126 Target features configuration options → consider under **F-081**
- GH #111 Gradle project as buildscript classpath
- GH #103 Java 8+ API on Android API ≤26
- F-019 Phase 2: AGP built-in Kotlin migration + absolute-latest AndroidX / compileSdk bump (after Phase 1 green)
- F-019 AndroidX ceiling / compileSdk bump (after F-086)
- Legacy `.kapt` removal once no consumers remain

## How workers update this file

Expand Down
6 changes: 2 additions & 4 deletions application/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,5 @@ org.gradle.unsafe.configuration-cache-problems=warn
org.gradle.unsafe.configuration-cache.max-problems=5
# TODO apply via forma
android.nonTransitiveRClass=true
# F-019 Phase 1: AGP 9 + Gradle 9 while sample still uses kapt (Dagger).
# Built-in Kotlin + new DSL require kapt→KSP migration (Phase 2).
android.builtInKotlin=false
android.newDsl=false
# F-086: kapt→KSP — AGP 9 built-in Kotlin + new DSL (defaults). Do not set
# android.builtInKotlin=false / android.newDsl=false (those were F-019 Phase 1 kapt bridge).
2 changes: 1 addition & 1 deletion application/root-app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ androidApp(
androidx.navigation,
androidx.vectordrawable,
google.material,
// Kapt enabled based on provided dep
// KSP enabled based on provided dep (F-086)
google.dagger,
google.play,
) +
Expand Down
4 changes: 2 additions & 2 deletions application/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pluginManagement {

buildscript {
dependencies {
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.7.7")
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.9.8")
configurations.all {
resolutionStrategy {
force(
Expand Down Expand Up @@ -115,7 +115,7 @@ projectDependencies(
"androidx.room:room-common:$roomVersion",
),
plugin("tools.forma.demo:dependencies", "0.0.1"),
plugin("androidx.navigation:navigation-safe-args-gradle-plugin", "2.7.7"),
plugin("androidx.navigation:navigation-safe-args-gradle-plugin", "2.9.8"),
plugin("com.google.firebase:firebase-crashlytics-gradle", "3.0.7"),
plugin(
id = "com.google.devtools.ksp:symbol-processing-gradle-plugin",
Expand Down
2 changes: 1 addition & 1 deletion build-dependencies/dependencies/src/main/kotlin/Google.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ object google {
google.inject,
google.jakartaInject,
"com.google.dagger:dagger:${versions.google.dagger}".dep,
"com.google.dagger:dagger-compiler:${versions.google.dagger}".kapt
"com.google.dagger:dagger-compiler:${versions.google.dagger}".ksp
)

val play = deps(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ object versions {
const val loader = "1.1.0"
const val localbroadcastmanager = "1.0.0"
// Sample uses paging 2.x APIs (PagedList / PageKeyedDataSource) — do not jump to 3.x
const val navigation = "2.7.7"
const val navigation = "2.9.8"
const val savedstate = "1.3.1"
const val slidingpanelayout = "1.2.0"
const val swiperefreshlayout = "1.2.0"
Expand Down
2 changes: 1 addition & 1 deletion docs/ENV.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ sudo ln -sfn /usr/local/opt/openjdk@21/libexec/openjdk.jdk \
- Compose compiler default **2.3.21** (+ Kotlin Compose Compiler plugin when `compose=true`)
- Sample SDK: min **23** / target **35** / compile **35** (install platform 35 for full sample builds; 34/33 still useful)
- CI: Temurin **21** all jobs (`.github/workflows/main.yml`)
- F-019 in progress: Gradle 9.6.1 + Kotlin 2.3.21 + AGP 9.3.0
- F-019 Phase 1 + F-086 ksp/built-in Kotlin done

See `docs/PROGRESS.md` for the latest host build tails.

Expand Down
17 changes: 17 additions & 0 deletions docs/PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@

Newest entries first.

## 2026-07-20 — F-086: kapt → KSP + AGP built-in Kotlin

- **Ticket:** F-086 → `done`
- **Branch:** `forma/F-086-ksp-migration` (from origin/v2)
- **User OK:** “Schedule Migration to ksp” (topic 136)
- **Forma engine:**
- `ConfigurationType.Ksp` + `ksp()` / `String.ksp` + `DependencyHandler.ksp`
- `processorConfigurationFeatures()` auto-applies `com.google.devtools.ksp` (or legacy kapt)
- Android targets use AGP **built-in Kotlin** (no `kotlin-android` plugin)
- **Sample:**
- `google.dagger` → `dagger-compiler` on **ksp** (was kapt)
- Drop `android.builtInKotlin=false` / `android.newDsl=false`
- Navigation **2.9.8** + safe-args plugin **2.9.8** (AGP 9 new DSL via `com.android.base` / AndroidComponentsExtension)
- **Verify:** plugins + application (2322 tasks) + jvm-application **BUILD SUCCESSFUL**
- **Next:** F-083


## 2026-07-20 — F-019: Gradle 9.6.1 + Kotlin 2.3.21 + AGP 9.3.0

- **Ticket:** F-019 → `done` (Phase 1)
Expand Down
2 changes: 0 additions & 2 deletions examples/android/01-hello-apk/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,3 @@ android.nonTransitiveRClass=true



android.builtInKotlin=false
android.newDsl=false
2 changes: 0 additions & 2 deletions examples/android/02-feature-api-impl/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,3 @@ android.nonTransitiveRClass=true



android.builtInKotlin=false
android.newDsl=false
2 changes: 0 additions & 2 deletions examples/android/03-res-viewbinding/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,3 @@ android.nonTransitiveRClass=true



android.builtInKotlin=false
android.newDsl=false
2 changes: 0 additions & 2 deletions examples/android/04-shared-libs/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,3 @@ android.nonTransitiveRClass=true



android.builtInKotlin=false
android.newDsl=false
2 changes: 0 additions & 2 deletions examples/android/05-widget-ui/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,3 @@ android.nonTransitiveRClass=true



android.builtInKotlin=false
android.newDsl=false
2 changes: 0 additions & 2 deletions examples/android/06-compose/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,3 @@ android.nonTransitiveRClass=true



android.builtInKotlin=false
android.newDsl=false
2 changes: 0 additions & 2 deletions examples/android/07-multi-feature/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,3 @@ android.nonTransitiveRClass=true



android.builtInKotlin=false
android.newDsl=false
2 changes: 0 additions & 2 deletions examples/android/08-deps-catalog/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,3 @@ android.nonTransitiveRClass=true



android.builtInKotlin=false
android.newDsl=false
2 changes: 0 additions & 2 deletions examples/android/09-test-utils/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,3 @@ android.nonTransitiveRClass=true



android.builtInKotlin=false
android.newDsl=false
2 changes: 0 additions & 2 deletions examples/android/10-target-plugins/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,3 @@ android.nonTransitiveRClass=true



android.builtInKotlin=false
android.newDsl=false
4 changes: 2 additions & 2 deletions plugins/android/src/main/java/androidApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import org.gradle.api.Project
import tools.forma.android.feature.AndroidLibraryFeatureConfiguration
import tools.forma.android.feature.androidLibraryFeatureDefinition
import tools.forma.android.feature.applyFeatures
import tools.forma.android.feature.kaptConfigurationFeature
import tools.forma.android.feature.processorConfigurationFeatures
import tools.forma.android.feature.kotlinAndroidFeatureDefinition
import tools.forma.android.target.AndroidTargetRegistry
import tools.forma.android.target.AndroidTargetTypes
Expand Down Expand Up @@ -65,6 +65,6 @@ fun Project.androidApp(
dependencies = dependencies,
testDependencies = testDependencies,
androidTestDependencies = androidTestDependencies,
configurationFeatures = kaptConfigurationFeature()
configurationFeatures = processorConfigurationFeatures()
)
}
4 changes: 2 additions & 2 deletions plugins/android/src/main/java/androidUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import tools.forma.android.validation.disallowResources
import tools.forma.android.visibility.Public
import tools.forma.android.visibility.Visibility
import org.gradle.api.Project
import tools.forma.android.feature.kaptConfigurationFeature
import tools.forma.android.feature.processorConfigurationFeatures
import tools.forma.deps.core.applyDependencies
import tools.forma.deps.core.applyTargetPlugins
import tools.forma.deps.core.FormaDependency
Expand Down Expand Up @@ -63,6 +63,6 @@ fun Project.androidUtil(
validator = AndroidTargetRegistry.validatorFor(AndroidTargetTypes.androidUtil).asValidator(),
dependencies = dependencies,
testDependencies = testDependencies,
configurationFeatures = kaptConfigurationFeature()
configurationFeatures = processorConfigurationFeatures()
)
}
4 changes: 2 additions & 2 deletions plugins/android/src/main/java/impl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import org.gradle.api.Project
import tools.forma.android.feature.AndroidLibraryFeatureConfiguration
import tools.forma.android.feature.androidLibraryFeatureDefinition
import tools.forma.android.feature.applyFeatures
import tools.forma.android.feature.kaptConfigurationFeature
import tools.forma.android.feature.processorConfigurationFeatures
import tools.forma.android.feature.kotlinAndroidFeatureDefinition
import tools.forma.android.target.AndroidTargetRegistry
import tools.forma.android.target.AndroidTargetTypes
Expand Down Expand Up @@ -63,6 +63,6 @@ fun Project.impl(
dependencies = dependencies,
testDependencies = testDependencies,
androidTestDependencies = androidTestDependencies,
configurationFeatures = kaptConfigurationFeature()
configurationFeatures = processorConfigurationFeatures()
)
}
4 changes: 2 additions & 2 deletions plugins/android/src/main/java/library.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import tools.forma.owners.Owner
import tools.forma.android.visibility.Public
import tools.forma.android.visibility.Visibility
import org.gradle.api.Project
import tools.forma.android.feature.kaptConfigurationFeature
import tools.forma.android.feature.processorConfigurationFeatures
import tools.forma.deps.core.applyDependencies
import tools.forma.deps.core.applyTargetPlugins
import tools.forma.deps.core.FormaDependency
Expand Down Expand Up @@ -36,6 +36,6 @@ fun Project.library(
validator = AndroidTargetRegistry.validatorFor(AndroidTargetTypes.jvmLibrary).asValidator(),
dependencies = dependencies,
testDependencies = testDependencies,
configurationFeatures = kaptConfigurationFeature()
configurationFeatures = processorConfigurationFeatures()
)
}
36 changes: 25 additions & 11 deletions plugins/android/src/main/java/tools/forma/android/feature/Kotlin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ 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.KotlinAndroidProjectExtension
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) {
val jvm = androidProjectSettings.javaVersionCompatibility.toString()
Expand All @@ -29,16 +29,17 @@ private val sharedFeatureConfiguration:
{ _, _, project, configuration -> defaultConfiguration(project, configuration) }

/**
* F-019 Phase 1: keep `kotlin-android` + kapt under
* `android.builtInKotlin=false` + `android.newDsl=false`.
* Phase 2: built-in Kotlin + migrate kapt→KSP (Dagger) and drop these plugins.
* AGP 9 built-in Kotlin: do **not** apply `kotlin-android`.
* Only wires Java/Kotlin jvmTarget from project settings (F-086).
*/
private val kotlinAndroidFeatureDefinitionInstance =
FeatureDefinition(
pluginName = "kotlin-android",
pluginExtension = KotlinAndroidProjectExtension::class,
FeatureDefinition<Unit, Unit>(
pluginName = "",
pluginExtension = null,
featureConfiguration = Unit,
configuration = sharedFeatureConfiguration
configuration = { _, _, project, configuration ->
defaultConfiguration(project, configuration)
}
)

/** Cached — same definition for every pure-JVM target (F-017). */
Expand All @@ -50,6 +51,7 @@ private val kotlinFeatureDefinitionInstance =
configuration = sharedFeatureConfiguration
)

/** Legacy kapt path — still supported if a target declares `.kapt` deps. */
private val kotlinKaptFeatureDefinitionInstance =
FeatureDefinition(
pluginName = "kotlin-kapt",
Expand All @@ -61,17 +63,29 @@ private val kotlinKaptFeatureDefinitionInstance =

fun kotlinFeatureDefinition() = kotlinFeatureDefinitionInstance

/** Config-only under AGP 9 built-in Kotlin (no `kotlin-android` plugin). */
fun kotlinAndroidFeatureDefinition() = kotlinAndroidFeatureDefinitionInstance

fun kotlinKaptFeatureDefinition() = kotlinKaptFeatureDefinitionInstance

/**
* Lazy kapt plugin application when a target declares kapt deps.
* Map is small and shared; the lambda closes over the [Project] receiver.
* 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)
*/
fun Project.kaptConfigurationFeature(): Map<ConfigurationType, () -> Unit> =
fun Project.processorConfigurationFeatures(): Map<ConfigurationType, () -> Unit> =
mapOf(
Ksp to {
if (!pluginManager.hasPlugin("com.google.devtools.ksp")) {
pluginManager.apply("com.google.devtools.ksp")
}
},
Kapt to {
applyFeatures(kotlinKaptFeatureDefinition())
}
)

/** @deprecated Use [processorConfigurationFeatures] (includes KSP). */
@Deprecated("Use processorConfigurationFeatures()", ReplaceWith("processorConfigurationFeatures()"))
fun Project.kaptConfigurationFeature(): Map<ConfigurationType, () -> Unit> =
processorConfigurationFeatures()
4 changes: 2 additions & 2 deletions plugins/android/src/main/java/uiLibrary.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import org.gradle.api.Project
import tools.forma.android.feature.AndroidLibraryFeatureConfiguration
import tools.forma.android.feature.androidLibraryFeatureDefinition
import tools.forma.android.feature.applyFeatures
import tools.forma.android.feature.kaptConfigurationFeature
import tools.forma.android.feature.processorConfigurationFeatures
import tools.forma.android.feature.kotlinAndroidFeatureDefinition
import tools.forma.android.target.AndroidTargetRegistry
import tools.forma.android.target.AndroidTargetTypes
Expand Down Expand Up @@ -57,7 +57,7 @@ fun Project.uiLibrary(
dependencies = dependencies,
testDependencies = testDependencies,
androidTestDependencies = androidTestDependencies,
configurationFeatures = kaptConfigurationFeature()
configurationFeatures = processorConfigurationFeatures()
)
}

7 changes: 7 additions & 0 deletions plugins/deps/src/main/java/dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ 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
import tools.forma.deps.core.NamedDependency
Expand Down Expand Up @@ -226,6 +227,9 @@ fun deps(vararg dependencies: TargetDependency): TargetDependency {
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) })

fun String.dep(configuration: CustomConfiguration, transitive: Boolean = true) =
NamedDependency(listOf(NameSpec(this, configuration, transitive)))

Expand All @@ -235,6 +239,9 @@ val String.dep: NamedDependency
val String.kapt: NamedDependency
get() = kapt(this)

val String.ksp: NamedDependency
get() = ksp(this)

val Project.target: FormaTarget
get() = FormaTarget(this)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ object Kapt : ConfigurationType {
override val name: String = "kapt"
}

/** Kotlin Symbol Processing configuration (`ksp`). Prefer over [Kapt] for new code (F-086). */
object Ksp : ConfigurationType {
override val name: String = "ksp"
}

@JvmInline value class CustomConfiguration(override val name: String) : ConfigurationType

sealed class DepSpec(val config: ConfigurationType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ import org.gradle.kotlin.dsl.create
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)

fun DependencyHandler.addDependencyTo(
configurationName: String,
dependencyNotation: String,
Expand Down
Loading