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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ Configuration-time performance: **F-017**
forma-core API design: **F-020** ([`docs/forma-core-api.md`](docs/forma-core-api.md)).
forma-core extraction (restriction engine + TargetType + Android matrix kit): **F-021** done (`plugins/core`).
forma-core validation SPI + content rules (Gradle facade in `:validation`): **F-022** done.
forma-core TargetRegistry + Android DSL consumers: **F-023** done.

Icons made by <a href="https://www.flaticon.com/authors/freepik" title="Freepik">Freepik</a>
from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a>
2 changes: 1 addition & 1 deletion TICKETS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Update this file when picking or finishing work. Cron workers must pick the **hi
| F-020 | done | Design forma-core public API (types, restrictions, validation, target registry) | `docs/forma-core-api.md` — types, restriction graph, validator SPI, registry, coords, library-suffix decision |
| F-021 | done | Extract dependency-type / restriction engine into `forma-core` | `plugins/core` + TargetType/NameMatcher/RestrictionGraph + AndroidTargetTypes + AndroidRestrictionKit (distinct jvm.library vs android.library); facades preserved. Related GH #39 |
| F-022 | done | Extract validation framework into `forma-core` | `plugins/core` TargetValidator + ContentRule; `:validation` facade; Android helpers call pure rules |
| F-023 | todo | Wire Android implementation as first consumer of forma-core | Sample still builds |
| F-023 | done | Wire Android implementation as first consumer of forma-core | TargetRegistry + AndroidTargetRegistry; DSL uses registry validators; sample green |
| F-024 | todo | Publish/coordinate coordinates: `tools.forma:core` vs android plugins | |

## P3 — JVM applications
Expand Down
7 changes: 4 additions & 3 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,14 +308,14 @@ Aligned with `docs/VISION.md`: core must not assume Android/AGP/Dagger.

| Concern | Current home | forma-core? | Notes |
|---------|--------------|-------------|-------|
| Target type identity (`TargetTemplate` / suffix) | `:target` + `:core` (F-021) | **Yes** | Parallel `TargetType` in core; templates kept for compat. Registry in F-023. |
| Target type identity (`TargetTemplate` / suffix) | `:target` + `:core` (F-021) | **Yes** | Parallel `TargetType` in core; templates kept for compat. **Registry F-023 done.** |
| Name + dep-type `Validator` | `:core` + `:validation` facade (F-022) | **Yes** | Core `TargetValidator` + factories; legacy API re-exports |
| Content validators (`onlyAllowResources`, …) | core predicates + `:android` helpers (F-022) | **Split** | Pure `ContentRule` in core; Gradle listing + Android helpers in platform |
| Dependency model + apply | `:deps` | **Mostly yes** | Strip AGP-ish config features; catalog generators may stay tooling |
| Settings store | `:config` | **Split** | Generic `SettingsStore` / plugin registry → core; `AndroidProjectSettings` → android |
| Owners | `:owners` | **Optional / yes** | Platform-agnostic metadata |
| Feature definitions (AGP library/binary/native) | `:android` | **No** | Stay platform |
| DSL entrypoints (`api`/`impl`/…) | `:android` | **No** (register *onto* core) | First consumer of core (F-023) |
| DSL entrypoints (`api`/`impl`/…) | `:android` | **No** (register *onto* core) | First consumer of core — **F-023 done** (`AndroidTargetRegistry`) |
| Includer / depgen | separate builds | **No** | Adjacent tooling |
| build-dependencies catalogs | sample support | **No** | Demo-only; pattern informs F-012 |

Expand All @@ -326,7 +326,8 @@ Suggested extraction order (tickets F-020…F-024):
`library` suffix decision).
2. Create `plugins/core` + TargetType + RestrictionGraph + wire Android matrix from DEPENDENCY-MATRIX — **done (F-021)**.
3. Validation SPI + content predicates in core; `:validation` thin facade — **done (F-022)**.
4. Wire Android DSL / registry as first consumer of core (F-023); sample stays green.
4. Wire Android DSL / registry as first consumer of core — **done (F-023)**
(`TargetRegistry` + `AndroidTargetRegistry`; sample green).
5. Coordinates: e.g. `tools.forma:core` vs `tools.forma.android` (F-024).

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

Newest entries first.

## 2026-07-13 — F-023 Wire Android as first consumer of forma-core

- **Ticket:** F-023 → `done`
- **Branch:** `forma/F-023-android-registry` (from `origin/v2`)
- **Skills/modes:** Grok Build `--mode full` (design plan) then `--mode implement` (hit max-turns twice; tree completed by Hermes verify/docs)
- **Core (`tools.forma.core.target`):**
- `TargetRegistration`, `TargetRegistry`, `DefaultTargetRegistry`
- `validatorFor` / `selfValidator` via core factories + registry-level caches
- `restrictionGraph()` rebuilt from registrations (replace semantics)
- Unit tests: `TargetRegistryTest` (9 tests, all green)
- **Android consumer:**
- `AndroidTargetRegistry` + `registerAndroidDefaults()` (matrix + content-rule metadata)
- `androidProjectConfiguration` initializes registry after `Forma.store`
- All DSL entrypoints (`api`/`impl`/`library`/`androidLibrary`/…/app/binary/native) use registry validators
- **jvm.library** vs **android.library** distinguished for `library()` vs `androidLibrary()`
- **Facade:** `TargetValidator.asValidator()` → legacy `Validator` / `ProjectValidationError`
- **Docs:** forma-core-api checklist + status; ARCHITECTURE §6; README Progress; TICKETS
- **Verify (OpenJDK 17 + env-mac.sh):**
- `plugins/`: `./gradlew :core:test` → **BUILD SUCCESSFUL** (`TargetRegistryTest` 9/9)
- `plugins/`: `./gradlew build` → **BUILD SUCCESSFUL** (68 tasks)
- `application/`: `./gradlew :binary:assembleDebug` → **BUILD SUCCESSFUL** (578 tasks)
- **Commits/PRs:** this run — push + PR base `v2`
- **Blockers:** none (Grok implement max-turns; code/docs finished under Hermes)
- **Next step:** F-024 Publish/coordinate coordinates (`tools.forma:core` vs android plugins)

## 2026-07-13 — F-022 Extract validation framework into forma-core

- **Ticket:** F-022 → `done`
Expand Down
9 changes: 5 additions & 4 deletions docs/forma-core-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ plugins under `plugins/` and in [`ARCHITECTURE.md`](ARCHITECTURE.md) §6 +

**Status:** accepted design. **Implemented so far:** F-021 (types +
restriction graph + Android kit), **F-022** (validation SPI + content
predicates in `plugins/core`; `:validation` is a Gradle facade). Registry /
DSL consumption remains **F-023**.
predicates in `plugins/core`; `:validation` is a Gradle facade), **F-023**
(`TargetRegistry` + Android DSL consumers via `AndroidTargetRegistry`).
Publishing coordinates remain **F-024**.

---

Expand Down Expand Up @@ -403,7 +404,7 @@ Bazel adapter (later): same registry + restriction graph; replace
| **F-020** (this doc) | Public API design only |
| **F-021** | Create `plugins/core` (or move packages); target types + **restriction graph** + wire Android matrix data; keep binary/API facades so sample builds |
| **F-022** | **Done:** validation SPI, identity-cached factories, content predicates in `plugins/core`; Android helpers call core; `:validation` facade |
| **F-023** | Android DSL uses registry; delete duplicated allow-lists from individual `*.kt` entrypoints where safe; sample green |
| **F-023** | **Done:** Android DSL uses registry; duplicated allow-lists removed from entrypoints; sample green |
| **F-024** | Publishing coordinates, README/Portal metadata, deprecate old plugin jars if merged |

Do not skip to JVM targets (F-030) until F-023 is done.
Expand All @@ -415,7 +416,7 @@ Do not skip to JVM targets (F-030) until F-023 is done.
**Must ship in forma-core v1**

- [x] `TargetType`, `TargetRef`, `NameMatcher` / `SuffixNameMatcher` (F-021)
- [ ] `TargetRegistry` + `TargetRegistration` + `DefaultTargetRegistry` (F-023)
- [x] `TargetRegistry` + `TargetRegistration` + `DefaultTargetRegistry` (F-023)
- [x] `RestrictionGraph` / `RestrictionRule` / `EdgeKind` (F-021)
- [x] `TargetValidator`, `AcceptAny`, `dependencyTypeValidator`, `selfTypeValidator` (F-022)
- [x] Content rule interfaces + `NoResourcesUnderMain` / `OnlyResourcesUnderMain` / `OnlyLayoutResources` (F-022)
Expand Down
44 changes: 12 additions & 32 deletions plugins/android/src/main/java/androidApp.kt
Original file line number Diff line number Diff line change
@@ -1,32 +1,22 @@
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.kotlinAndroidFeatureDefinition
import tools.forma.android.target.AndroidUtilTargetTemplate
import tools.forma.android.target.ApiTargetTemplate
import tools.forma.android.target.ApplicationTargetTemplate
import tools.forma.android.target.ImplTargetTemplate
import tools.forma.android.target.LibraryTargetTemplate
import tools.forma.android.target.ResourcesTargetTemplate
import tools.forma.android.target.TestUtilTargetTemplate
import tools.forma.android.target.UiLibraryTargetTemplate
import tools.forma.android.target.UtilTargetTemplate
import tools.forma.android.target.ViewBindingTargetTemplate
import tools.forma.android.target.WidgetTargetTemplate
import tools.forma.android.target.ComposeWidgetTargetTemplate
import tools.forma.owners.NoOwner
import tools.forma.owners.Owner
import tools.forma.android.target.AndroidTargetRegistry
import tools.forma.android.target.AndroidTargetTypes
import tools.forma.android.utils.BuildConfiguration
import tools.forma.android.validation.disallowResources
import tools.forma.validation.validate
import tools.forma.validation.validator
import tools.forma.android.visibility.Public
import tools.forma.android.visibility.Visibility
import tools.forma.deps.core.FormaDependency
import tools.forma.deps.core.NamedDependency
import tools.forma.deps.core.applyDependencies
import org.gradle.api.Project
import tools.forma.android.feature.kaptConfigurationFeature
import tools.forma.owners.NoOwner
import tools.forma.owners.Owner
import tools.forma.validation.asValidator
import tools.forma.validation.validate

/**
* Root Android application library (feature composition, not the APK entry).
Expand All @@ -52,13 +42,15 @@ fun Project.androidApp(

disallowResources()

target.validate(ApplicationTargetTemplate)
val selfV = AndroidTargetRegistry.selfValidator(AndroidTargetTypes.app).asValidator()
selfV.validate(target)
val libraryFeatureConfiguration = AndroidLibraryFeatureConfiguration(
packageName,
buildConfiguration,
testInstrumentationRunner,
consumerMinificationFiles,
manifestPlaceholders,
selfValidator = selfV,
compose = compose,
)
applyFeatures(
Expand All @@ -67,19 +59,7 @@ fun Project.androidApp(
)

applyDependencies(
validator = validator(
ApiTargetTemplate,
ImplTargetTemplate,
LibraryTargetTemplate,
UtilTargetTemplate,
AndroidUtilTargetTemplate,
TestUtilTargetTemplate,
ResourcesTargetTemplate,
ViewBindingTargetTemplate,
WidgetTargetTemplate,
ComposeWidgetTargetTemplate,
UiLibraryTargetTemplate,
),
validator = AndroidTargetRegistry.validatorFor(AndroidTargetTypes.app).asValidator(),
dependencies = dependencies,
testDependencies = testDependencies,
androidTestDependencies = androidTestDependencies,
Expand Down
41 changes: 9 additions & 32 deletions plugins/android/src/main/java/androidBinary.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,16 @@ import org.gradle.api.Project
import tools.forma.android.feature.AndroidBinaryFeatureConfiguration
import tools.forma.android.feature.androidBinaryFeatureDefinition
import tools.forma.android.feature.applyFeatures
import tools.forma.owners.NoOwner
import tools.forma.owners.Owner
import tools.forma.android.target.AndroidUtilTargetTemplate
import tools.forma.android.target.ApiTargetTemplate
import tools.forma.android.target.ApplicationTargetTemplate
import tools.forma.android.target.BinaryTargetTemplate
import tools.forma.android.target.ImplTargetTemplate
import tools.forma.android.target.LibraryTargetTemplate
import tools.forma.android.target.ResourcesTargetTemplate
import tools.forma.android.target.TestUtilTargetTemplate
import tools.forma.android.target.UiLibraryTargetTemplate
import tools.forma.android.target.UtilTargetTemplate
import tools.forma.android.target.ViewBindingTargetTemplate
import tools.forma.android.target.WidgetTargetTemplate
import tools.forma.android.target.ComposeWidgetTargetTemplate
import tools.forma.android.target.AndroidTargetRegistry
import tools.forma.android.target.AndroidTargetTypes
import tools.forma.android.utils.BuildConfiguration
import tools.forma.android.validation.disallowResources
import tools.forma.deps.core.FormaDependency
import tools.forma.deps.core.applyDependencies
import tools.forma.owners.NoOwner
import tools.forma.owners.Owner
import tools.forma.validation.asValidator
import tools.forma.validation.validate
import tools.forma.validation.validator

/**
* Android Binary target — application entry point (single APK).
Expand Down Expand Up @@ -55,8 +44,8 @@ fun Project.androidBinary(

disallowResources()

target.validate(BinaryTargetTemplate)

val selfV = AndroidTargetRegistry.selfValidator(AndroidTargetTypes.binary).asValidator()
selfV.validate(target)
val binaryFeatureConfiguration = AndroidBinaryFeatureConfiguration(
packageName,
versionCode,
Expand All @@ -65,27 +54,15 @@ fun Project.androidBinary(
testInstrumentationRunner,
consumerMinificationFiles,
manifestPlaceholders,
selfValidator = selfV,
compose = compose,
)
applyFeatures(
androidBinaryFeatureDefinition(binaryFeatureConfiguration)
)

applyDependencies(
validator = validator(
ApplicationTargetTemplate,
ApiTargetTemplate,
ImplTargetTemplate,
LibraryTargetTemplate,
UtilTargetTemplate,
AndroidUtilTargetTemplate,
TestUtilTargetTemplate,
ResourcesTargetTemplate,
ViewBindingTargetTemplate,
WidgetTargetTemplate,
ComposeWidgetTargetTemplate,
UiLibraryTargetTemplate,
),
validator = AndroidTargetRegistry.validatorFor(AndroidTargetTypes.binary).asValidator(),
dependencies = dependencies
)

Expand Down
21 changes: 5 additions & 16 deletions plugins/android/src/main/java/androidLibrary.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,18 @@ import tools.forma.android.feature.androidLibraryFeatureDefinition
import tools.forma.android.feature.applyFeatures
import tools.forma.android.feature.kaptConfigurationFeature
import tools.forma.android.feature.kotlinAndroidFeatureDefinition
import tools.forma.android.target.AndroidTargetRegistry
import tools.forma.android.target.AndroidTargetTypes
import tools.forma.owners.NoOwner
import tools.forma.owners.Owner
import tools.forma.android.target.AndroidUtilTargetTemplate
import tools.forma.android.target.ApiTargetTemplate
import tools.forma.android.target.LibraryTargetTemplate
import tools.forma.android.target.ResourcesTargetTemplate
import tools.forma.android.target.TestUtilTargetTemplate
import tools.forma.android.target.UtilTargetTemplate
import tools.forma.android.utils.BuildConfiguration
import tools.forma.android.visibility.Public
import tools.forma.android.visibility.Visibility
import tools.forma.deps.core.FormaDependency
import tools.forma.deps.core.NamedDependency
import tools.forma.deps.core.applyDependencies
import tools.forma.validation.asValidator
import tools.forma.validation.validate
import tools.forma.validation.validator

/**
* Shared Android library (not a feature [impl]).
Expand All @@ -43,7 +39,7 @@ fun Project.androidLibrary(
/** Enable Jetpack Compose; defaults to project-wide `compose` setting. */
compose: Boolean = Forma.settings.compose,
): TargetBuilder {
target.validate(LibraryTargetTemplate)
AndroidTargetRegistry.selfValidator(AndroidTargetTypes.library).asValidator().validate(target)
val libraryFeatureConfiguration = AndroidLibraryFeatureConfiguration(
packageName,
buildConfiguration,
Expand All @@ -58,14 +54,7 @@ fun Project.androidLibrary(
)

applyDependencies(
validator = validator(
LibraryTargetTemplate,
UtilTargetTemplate,
AndroidUtilTargetTemplate,
TestUtilTargetTemplate,
ResourcesTargetTemplate,
ApiTargetTemplate,
),
validator = AndroidTargetRegistry.validatorFor(AndroidTargetTypes.library).asValidator(),
dependencies = dependencies,
testDependencies = testDependencies,
androidTestDependencies = androidTestDependencies,
Expand Down
18 changes: 10 additions & 8 deletions plugins/android/src/main/java/androidNative.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import org.gradle.api.Project
import tools.forma.android.config.NdkAbi
import tools.forma.android.config.NdkBuildSystem
import tools.forma.owners.NoOwner
import tools.forma.owners.Owner
import tools.forma.android.target.NativeTarget
import tools.forma.android.feature.AndroidNativeConfiguration
import tools.forma.android.feature.androidNativeDefinition
import tools.forma.android.feature.applyFeatures
import tools.forma.android.target.AndroidTargetRegistry
import tools.forma.android.target.AndroidTargetTypes
import tools.forma.android.validation.disallowResources
import tools.forma.android.visibility.Public
import tools.forma.android.visibility.Visibility
import tools.forma.owners.NoOwner
import tools.forma.owners.Owner
import tools.forma.validation.asValidator
import tools.forma.validation.validate
import tools.forma.android.validation.disallowResources
import tools.forma.android.feature.applyFeatures
import tools.forma.android.feature.androidNativeDefinition
import tools.forma.android.feature.AndroidNativeConfiguration

fun Project.androidNative(
packageName: String,
Expand All @@ -20,7 +22,7 @@ fun Project.androidNative(
visibility: Visibility = Public
) {
disallowResources()
target.validate(NativeTarget)
AndroidTargetRegistry.selfValidator(AndroidTargetTypes.native).asValidator().validate(target)

val configuration = AndroidNativeConfiguration(
packageName = packageName,
Expand Down
3 changes: 3 additions & 0 deletions plugins/android/src/main/java/androidProjectConfiguration.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.gradle.kotlin.dsl.embeddedKotlinVersion
import org.gradle.kotlin.dsl.repositories
import org.gradle.plugin.use.PluginDependency
import tools.forma.android.utils.register
import tools.forma.android.target.registerAndroidDefaults
import tools.forma.config.AndroidProjectSettings
import tools.forma.config.FormaSettingsStore
import tools.forma.config.PluginInfoStore
Expand Down Expand Up @@ -75,6 +76,7 @@ fun ScriptHandlerScope.androidProjectConfiguration(
)

Forma.store(configuration)
registerAndroidDefaults()
}

@Deprecated("Old approach to configuration, use ScriptHandlerScope Extension")
Expand Down Expand Up @@ -116,6 +118,7 @@ fun Project.androidProjectConfiguration(
)

Forma.store(configuration)
registerAndroidDefaults()
}

/** Compose Compiler matching Kotlin 1.9.10 (application Gradle 8.4 embedded Kotlin). */
Expand Down
Loading
Loading