diff --git a/README.md b/README.md index 4a99a319..907c3020 100644 --- a/README.md +++ b/README.md @@ -109,9 +109,9 @@ project-dep type checks + content layout rules from live validators | Supported target types | implemented | purpose | validation | |:----------------------:|:-----------:|:-------:|:----------:| -| `androidBinary` | ✅ | Generate single APK | name + no `res/`; project deps **unrestricted** (`EmptyValidator`) | -| `androidApp` | ✅ | Application / root Android library | name + no `res/`; project deps **unrestricted** | -| `androidLibrary` | ✅ | Android library | name only; project deps **unrestricted** | +| `androidBinary` | ✅ | Generate single APK | name + no `res/`; composition-root project-dep list | +| `androidApp` | ✅ | Application / root Android library | name + no `res/`; composition-root project-dep list | +| `androidLibrary` | ✅ | Android library | name + project-dep list (**no** `impl`) | | `uiLibrary` | ✅ | Shared UI library for impl/widget | project-dep list | | `widget` | ✅ | Custom View / UI component | project-dep list | | `androidRes` | ✅ | Resources only | only `res/` under `src/main` + project-dep list | @@ -137,7 +137,7 @@ That document is generated from each target’s | `api` | `api`, `library` | | `impl` | `api`, `android-util`, `test-util`, `util`, `library`, `ui-library`, `res`, `viewbinding`, `widget` | | `library` (JVM) | `util`, `test-util` | -| `androidLibrary` | *any* (`EmptyValidator`) | +| `androidLibrary` | `library`, `util`, `android-util`, `test-util`, `res`, `api` | | `uiLibrary` | `widget`, `util`, `android-util`, `res` | | `util` | `util`, `library` | | `androidUtil` | `android-util`, `test-util`, `res` | @@ -146,11 +146,11 @@ That document is generated from each target’s | `androidRes` | `res`, `widget` | | `widget` | `ui-library`, `widget`, `util`, `android-util`, `res` | | `viewBinding` | `api`, `widget`, `res`, `library`, `android-util` | -| `androidApp` / `androidBinary` | *any* (`EmptyValidator`) | +| `androidApp` | `api`, `impl`, `library`, `util`, `android-util`, `test-util`, `res`, `viewbinding`, `widget`, `ui-library` | +| `androidBinary` | `app`, `api`, `impl`, `library`, `util`, `android-util`, `test-util`, `res`, `viewbinding`, `widget`, `ui-library` | | `androidNative` | *(no project-dep check)* | -Tightening unrestricted entry targets and Dagger-friendly `api`/`impl` -boundaries is tracked as **F-011**. +Dagger2-friendly `api`/`impl` + composition roots: **F-011** (landed). Icons made by Freepik from www.flaticon.com diff --git a/TICKETS.md b/TICKETS.md index 9e7fe771..05fc515b 100644 --- a/TICKETS.md +++ b/TICKETS.md @@ -18,7 +18,7 @@ Update this file when picking or finishing work. Cron workers must pick the **hi | ID | Status | Title | Notes | |----|--------|-------|-------| | F-010 | done | Document strict dependency matrix from live code (not only README) | `docs/DEPENDENCY-MATRIX.md` + README summary aligned to validators | -| F-011 | todo | Tighten validation for `api` / `impl` (Dagger2-friendly boundaries) | Align with current types; related GH #56, #18 | +| F-011 | done | Tighten validation for `api` / `impl` (Dagger2-friendly boundaries) | app/binary/androidLibrary no longer EmptyValidator; impl still no→impl; docs matrix updated | | F-012 | todo | External deps catalog UX + tooling polish | `plugins/deps` catalog generators | | F-013 | todo | Compose support for Android library/ui targets | GH #96 | | F-014 | todo | Sample app: gold-standard multi-feature structure | Home/characters already present; modernize | diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 5d6fcdfd..126f487e 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -139,7 +139,7 @@ when validators change. | `api` | `api`, `library` | no `res/` under `src/main` | | `impl` | `api`, `android-util`, `test-util`, `util`, `library`, `ui-library`, `res`, `viewbinding`, `widget` | — | | `library` (JVM) | `util`, `test-util` | — | -| `androidLibrary` | **EmptyValidator** (any project) | — | +| `androidLibrary` | `library`, `util`, `android-util`, `test-util`, `res`, `api` | — | | `uiLibrary` | `widget`, `util`, `android-util`, `res` | — | | `util` | `util`, `library` | no `res/` | | `androidUtil` | `android-util`, `test-util`, `res` | no `res/` | @@ -148,14 +148,14 @@ when validators change. | `androidRes` | `res`, `widget` | **only** `res/` under `src/main` | | `widget` | `ui-library`, `widget`, `util`, `android-util`, `res` | — | | `viewBinding` | `api`, `widget`, `res`, `library`, `android-util` | only `layout*` under `src/main/res` | -| `androidApp` | **EmptyValidator** | no `res/` | -| `androidBinary` | **EmptyValidator** | no `res/` | +| `androidApp` | `api`, `impl`, `library`, `util`, `android-util`, `test-util`, `res`, `viewbinding`, `widget`, `ui-library` | no `res/` | +| `androidBinary` | `app`, `api`, `impl`, `library`, `util`, `android-util`, `test-util`, `res`, `viewbinding`, `widget`, `ui-library` | no `res/` | | `androidNative` | (no `applyDependencies` in current code) | no `res/` | Notes for later tickets: -- `impl` cannot depend on other `impl` (good for Dagger-ish boundaries) — F-011. -- `androidLibrary` / `androidApp` / `androidBinary` skip dep-type checks — partial validation in README sense. +- `impl` cannot depend on other `impl` (Dagger-friendly) — enforced; F-011. +- `androidLibrary` / `androidApp` / `androidBinary` use restricted project-dep lists — F-011 done. - JVM `library` and Android `androidLibrary` share `LibraryTargetTemplate` suffix `library` — naming collision risk for forma-core registry design (F-020). ### 2.3 Feature stack (Android module) @@ -313,7 +313,7 @@ Suggested extraction order (tickets F-020…F-024): | Item | Ticket | |------|--------| | ~~README dependency matrix ≠ live validators~~ → [`docs/DEPENDENCY-MATRIX.md`](DEPENDENCY-MATRIX.md) | F-010 done | -| `EmptyValidator` on app/binary/androidLibrary | F-011 | +| ~~`EmptyValidator` on app/binary/androidLibrary~~ (composition-root + library allowlists) | F-011 done | | ~~AGP 7.4.2 compile vs 8.1.2 runtime~~ (aligned 8.1.2) | F-003 done | | ~~CI missing SDK + Java on some jobs~~ (GHA green on PR #153) | F-004 done | | Compose flag in settings, limited target support | F-013 | diff --git a/docs/DEPENDENCY-MATRIX.md b/docs/DEPENDENCY-MATRIX.md index 26aac42a..ea80540d 100644 --- a/docs/DEPENDENCY-MATRIX.md +++ b/docs/DEPENDENCY-MATRIX.md @@ -8,7 +8,7 @@ Source of truth for **F-010**. Every rule below is taken from the live When validators change, update **this file first**, then the README summary matrix. Do not invent rules from aspirational docs. -Last verified: **2026-07-11** against `v2` tip (post F-003 / F-004). +Last verified: **2026-07-11** against `v2` tip + F-011 validator tighten. --- @@ -76,7 +76,7 @@ these suffixes (or are unrestricted if `EmptyValidator`). | `api` | `api.kt` | `validator(api, library)` | `api`, `library` | | `impl` | `impl.kt` | `validator(api, android-util, test-util, util, library, ui-library, res, viewbinding, widget)` | `api`, `android-util`, `test-util`, `util`, `library`, `ui-library`, `res`, `viewbinding`, `widget` | | `library` (JVM) | `library.kt` | `validator(util, test-util)` | `util`, `test-util` | -| `androidLibrary` | `androidLibrary.kt` | **`EmptyValidator`** | *any project* | +| `androidLibrary` | `androidLibrary.kt` | `validator(library, util, android-util, test-util, res, api)` | `library`, `util`, `android-util`, `test-util`, `res`, `api` | | `uiLibrary` | `uiLibrary.kt` | `validator(widget, util, android-util, res)` | `widget`, `util`, `android-util`, `res` | | `util` | `util.kt` | `validator(util, library)` | `util`, `library` | | `androidUtil` | `androidUtil.kt` | `validator(android-util, test-util, res)` | `android-util`, `test-util`, `res` | @@ -85,21 +85,25 @@ these suffixes (or are unrestricted if `EmptyValidator`). | `androidRes` | `androidRes.kt` | `validator(res, widget)` | `res`, `widget` | | `widget` | `widget.kt` | `validator(ui-library, widget, util, android-util, res)` | `ui-library`, `widget`, `util`, `android-util`, `res` | | `viewBinding` | `viewBinding.kt` | `validator(api, widget, res, library, android-util)` | `api`, `widget`, `res`, `library`, `android-util` | -| `androidApp` | `androidApp.kt` | **`EmptyValidator`** | *any project* | -| `androidBinary` | `androidBinary.kt` | **`EmptyValidator`** | *any project* | +| `androidApp` | `androidApp.kt` | `validator(api, impl, library, util, android-util, test-util, res, viewbinding, widget, ui-library)` | `api`, `impl`, `library`, `util`, `android-util`, `test-util`, `res`, `viewbinding`, `widget`, `ui-library` | +| `androidBinary` | `androidBinary.kt` | `validator(app, api, impl, library, util, android-util, test-util, res, viewbinding, widget, ui-library)` | `app`, `api`, `impl`, `library`, `util`, `android-util`, `test-util`, `res`, `viewbinding`, `widget`, `ui-library` | | `androidNative` | `androidNative.kt` | *(no `applyDependencies`)* | *no project-dep validation* | ### Explicit non-edges (important product rules) These follow from the table (not from separate deny-lists): -- **`impl` cannot depend on `impl`** — only listed suffixes; good for - Dagger-style feature boundaries (tighten further in F-011). +- **`impl` cannot depend on `impl`** — only listed suffixes; Dagger-style feature + boundaries (implementations compose only at app/binary). - **`api` cannot depend on `impl` / widgets / res / viewbinding** — JVM API surface only (`api` + `library`). - **`library` (JVM) cannot depend on `api` / `impl`** — comment in code: “Can't depend on api\impl”. - **`util` cannot depend on `api` / `impl`** — same intent as library. +- **`androidLibrary` cannot depend on `impl` / widgets / viewbinding** — + shared libs may use `api` contracts only (F-011). +- **`androidApp` / `androidBinary` cannot depend on other `binary`** — + single APK composition root (F-011; `app` may not depend on `app`/`binary`). - **Circular-ish UI graph is intentional while experimental:** `uiLibrary` may depend on `widget`, and `widget` may depend on `ui-library` (`uiLibrary.kt` comment). @@ -142,7 +146,7 @@ Rows = **consumer**. Columns = **dependency type (suffix)**. | **api** | Y | — | Y | — | — | — | — | — | — | — | — | — | — | — | | **impl** | Y | — | Y | Y | Y | Y | Y | — | Y | Y | Y | — | — | — | | **library** (JVM) | — | — | — | — | Y | Y | — | — | — | — | — | — | — | — | -| **androidLibrary** | * | * | * | * | * | * | * | * | * | * | * | * | * | * | +| **androidLibrary** | Y | — | Y | — | Y | Y | Y | — | Y | — | — | — | — | — | | **uiLibrary** | — | — | — | — | Y | — | Y | — | Y | — | Y | — | — | — | | **util** | — | — | Y | — | Y | — | — | — | — | — | — | — | — | — | | **androidUtil** | — | — | — | — | — | Y | Y | — | Y | — | — | — | — | — | @@ -151,8 +155,8 @@ Rows = **consumer**. Columns = **dependency type (suffix)**. | **androidRes** | — | — | — | — | — | — | — | — | Y | — | Y | — | — | — | | **widget** | — | — | — | Y | Y | — | Y | — | Y | — | Y | — | — | — | | **viewBinding** | Y | — | Y | — | — | — | Y | — | Y | — | Y | — | — | — | -| **androidApp** | * | * | * | * | * | * | * | * | * | * | * | * | * | * | -| **androidBinary** | * | * | * | * | * | * | * | * | * | * | * | * | * | * | +| **androidApp** | Y | Y | Y | Y | Y | Y | Y | — | Y | Y | Y | — | — | — | +| **androidBinary** | Y | Y | Y | Y | Y | Y | Y | — | Y | Y | Y | Y | — | — | | **androidNative** | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | Self-deps: a type may depend on itself only if its own suffix is in its @@ -171,8 +175,8 @@ Notable mismatches fixed by treating **this document** as truth: | Topic | Old README implication | Live code | |-------|------------------------|-----------| -| `androidLibrary` project deps | selective ✅/❌ grid | **`EmptyValidator`** (unrestricted) | -| `androidApp` / `androidBinary` project deps | selective grid | **`EmptyValidator`** | +| `androidLibrary` project deps | selective ✅/❌ grid | **restricted** (no `impl`/widget/viewbinding) after F-011 | +| `androidApp` / `androidBinary` project deps | selective grid | **restricted** composition-root lists after F-011 | | `impl` → `impl` | ❌ (agrees) | not in allowed list | | `api` → `library` | not clearly shown | **allowed** | | `impl` → `ui-library` / `viewbinding` / `widget` / `res` | partially missing | **allowed** | @@ -181,8 +185,7 @@ Notable mismatches fixed by treating **this document** as truth: | `uiLibrary` | missing / confused with library | full row above | | DSL names | `androidWidget`, `utils`, … | `widget`, `util`, … | -Tightening `EmptyValidator` targets and Dagger-friendly `api`/`impl` edges is -**F-011**, not documentation fiction. +Tightening unrestricted entry targets landed in **F-011**. --- @@ -193,7 +196,7 @@ Tightening `EmptyValidator` targets and Dagger-friendly `api`/`impl` edges is - `feature/*/api` → JVM `api` - `feature/*/impl` → Android `impl` + viewbinding/res/widget/core libraries - `binary` → `androidBinary` with explicit deps on root-app + feature api/impl - (legal today because binary uses `EmptyValidator`) + (legal under composition-root allowlist; F-011) See `docs/ARCHITECTURE.md` §2.2 and §3 for the module map. @@ -206,5 +209,5 @@ See `docs/ARCHITECTURE.md` §2.2 and §3 for the module map. 3. Refresh the compact README matrix (link here as canonical). 4. Note the change in `docs/PROGRESS.md` / ticket notes when user-facing. -Related tickets: **F-011** (tighten validation), **F-020** (forma-core type +Related tickets: **F-012** (deps catalog UX), **F-020** (forma-core type registry / shared `library` suffix). diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 9d5b7a40..d25358d4 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -2,27 +2,34 @@ Newest entries first. +## 2026-07-11 — F-011 api/impl + composition-root validation + +- **Ticket:** F-011 → `done` +- **Branch:** `forma/F-011-api-impl-validation` +- **Code:** + - `androidApp` / `androidBinary` / `androidLibrary`: replace `EmptyValidator` with Dagger-friendly project-dep allowlists + - `androidLibrary` **cannot** depend on `impl` (or widget/viewbinding) + - `impl` still cannot depend on `impl`; KDoc on `api`/`impl` documents boundaries + - Clearer validation error text in `Validator.kt` +- **Docs:** `docs/DEPENDENCY-MATRIX.md`, README summary, TICKETS +- **Verify:** `plugins` + `application` `./gradlew build` (see commit/PR notes) +- **Next:** F-012 external deps catalog UX + ## 2026-07-11 — F-010 Dependency matrix from live validators - **Ticket:** F-010 → `done` - **Branch:** `forma/F-010-dependency-matrix` (from `origin/v2` after F-003/F-004 merge) - **Actions:** - - Added `docs/DEPENDENCY-MATRIX.md` as canonical code-truth matrix: - - How name / project-dep / content validation works - - Per-DSL allowed suffixes from each `applyDependencies(validator=…)` - - Content rules (`disallowResources` / `onlyAllowResources` / `onlyAllowLayouts`) - - Full consumer×dependency table (Y / * / — / n/a) - - README vs code divergence notes - - Replaced aspirational README ✅/❌ grid with code-aligned summary + link - - Expanded Progress target table (`uiLibrary`, `viewBinding`, `androidNative`, real DSL names) - - Pointed `docs/ARCHITECTURE.md` §2.2 + §7 at the new doc; marked F-010 done -- **Also this run (P0 land on `v2`):** - - Squash-merge path unavailable (repo allows squash only); merged #152 then #153 into `v2` locally and pushed (`123ec44`) - - PR #152 F-003 and #153 F-004 both **MERGED** into `v2` -- **Build:** docs-only slice; no Gradle re-run required for matrix extraction (rules read from Kotlin sources) -- **Commits/PRs:** this branch → PR base `v2` -- **Blockers:** none -- **Next step:** F-011 tighten `api`/`impl` (+ optional replace `EmptyValidator` on app/binary/androidLibrary) + - Added `docs/DEPENDENCY-MATRIX.md` as canonical code-truth matrix + - Replaced aspirational README matrix with code-aligned summary + - Pointed `docs/ARCHITECTURE.md` at the new doc; marked F-010 done +- **Also that run:** F-003 #152 and F-004 #153 merged into `v2` (`123ec44`) +- **PR:** #154 squash-merged to `v2` as `619ce05` + +## 2026-07-11 — v2 base of operations (merge open PRs) + +- Created `v2` from `master`; integrated F-001/F-002; workers branch/PR against `v2` +- Tip later advanced with F-003/F-004/F-010/F-011 ## 2026-07-11 — F-004 CI green (workflow + badge) diff --git a/plugins/android/src/main/java/androidApp.kt b/plugins/android/src/main/java/androidApp.kt index bec76d4a..bb2b98fa 100644 --- a/plugins/android/src/main/java/androidApp.kt +++ b/plugins/android/src/main/java/androidApp.kt @@ -2,13 +2,23 @@ import tools.forma.android.feature.AndroidLibraryFeatureConfiguration import tools.forma.android.feature.androidLibraryFeatureDefinition import tools.forma.android.feature.applyFeatures 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.owners.NoOwner import tools.forma.owners.Owner import tools.forma.android.utils.BuildConfiguration -import tools.forma.validation.EmptyValidator 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 @@ -18,7 +28,11 @@ import org.gradle.api.Project import tools.forma.android.feature.kaptConfigurationFeature /** - * TODO Can't depend on widgets, cant depend on databindings + * Root Android application library (feature composition, not the APK entry). + * + * Project-deps allowlist is Dagger2-friendly: may wire feature [api]/[impl], + * shared libraries/utils, and UI building blocks. Cannot depend on other `app` + * or `binary` targets (composition stays single-rooted via [androidBinary]). */ fun Project.androidApp( packageName: String, @@ -48,9 +62,19 @@ fun Project.androidApp( kotlinAndroidFeatureDefinition() ) - applyDependencies( - validator = EmptyValidator, + validator = validator( + ApiTargetTemplate, + ImplTargetTemplate, + LibraryTargetTemplate, + UtilTargetTemplate, + AndroidUtilTargetTemplate, + TestUtilTargetTemplate, + ResourcesTargetTemplate, + ViewBindingTargetTemplate, + WidgetTargetTemplate, + UiLibraryTargetTemplate, + ), dependencies = dependencies, repositoriesConfiguration = Forma.settings.repositories, testDependencies = testDependencies, @@ -58,4 +82,3 @@ fun Project.androidApp( configurationFeatures = kaptConfigurationFeature() ) } - diff --git a/plugins/android/src/main/java/androidBinary.kt b/plugins/android/src/main/java/androidBinary.kt index e09601bc..ab9ea451 100644 --- a/plugins/android/src/main/java/androidBinary.kt +++ b/plugins/android/src/main/java/androidBinary.kt @@ -4,27 +4,39 @@ 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.utils.BuildConfiguration import tools.forma.android.validation.disallowResources import tools.forma.deps.core.FormaDependency import tools.forma.deps.core.applyDependencies -import tools.forma.validation.EmptyValidator import tools.forma.validation.validate +import tools.forma.validation.validator /** - * Android Binary target - application entry point. + * Android Binary target — application entry point (single APK). * - * Manifest + minimal set of resources + root android project dependency only. - * No library dependencies, no source code. + * Composition root for Dagger graphs: may depend on root [androidApp], feature + * [api]/[impl], and shared libraries/utils. Project-dep types are restricted + * (no longer [tools.forma.validation.EmptyValidator]). * - * @param packageName - Application package name, used for publishing - * @param owner - owner of the target, team responsible for maintenance - * @param dependencies - list of external and project dependencies for the target - * @param buildConfiguration - Android Gradle Plugin configuration DSL - * @param testInstrumentationRunner - class name used for instrumentation tests execution - * @param consumerMinificationFiles - Proguard/R8 minification files list - * @param manifestPlaceholders - placeholders ot be injected in manifest + * @param packageName Application package name, used for publishing + * @param owner owner of the target, team responsible for maintenance + * @param dependencies list of external and project dependencies for the target + * @param buildConfiguration Android Gradle Plugin configuration DSL + * @param testInstrumentationRunner class name used for instrumentation tests execution + * @param consumerMinificationFiles Proguard/R8 minification files list + * @param manifestPlaceholders placeholders to be injected in manifest */ fun Project.androidBinary( packageName: String, @@ -56,11 +68,22 @@ fun Project.androidBinary( ) applyDependencies( - validator = EmptyValidator, + validator = validator( + ApplicationTargetTemplate, + ApiTargetTemplate, + ImplTargetTemplate, + LibraryTargetTemplate, + UtilTargetTemplate, + AndroidUtilTargetTemplate, + TestUtilTargetTemplate, + ResourcesTargetTemplate, + ViewBindingTargetTemplate, + WidgetTargetTemplate, + UiLibraryTargetTemplate, + ), dependencies = dependencies, repositoriesConfiguration = Forma.settings.repositories ) return TargetBuilder(this) } - diff --git a/plugins/android/src/main/java/androidLibrary.kt b/plugins/android/src/main/java/androidLibrary.kt index 6e01a6c2..31039256 100644 --- a/plugins/android/src/main/java/androidLibrary.kt +++ b/plugins/android/src/main/java/androidLibrary.kt @@ -1,7 +1,4 @@ import org.gradle.api.Project -import org.gradle.api.artifacts.type.ArtifactTypeDefinition -import org.gradle.api.internal.artifacts.ArtifactAttributes.ARTIFACT_FORMAT -import org.gradle.kotlin.dsl.dependencies import tools.forma.android.feature.AndroidLibraryFeatureConfiguration import tools.forma.android.feature.androidLibraryFeatureDefinition import tools.forma.android.feature.applyFeatures @@ -9,18 +6,28 @@ import tools.forma.android.feature.kaptConfigurationFeature import tools.forma.android.feature.kotlinAndroidFeatureDefinition 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.EmptyValidator import tools.forma.validation.validate +import tools.forma.validation.validator /** - * TODO Can't depend on widgets, cant depend on databindings + * Shared Android library (not a feature [impl]). + * + * Dagger-friendly: may depend on other libraries/utils/res and feature [api] + * contracts, but **not** on [impl] (implementations are composed only at + * [androidApp] / [androidBinary]). Also disallows widgets/viewbinding as + * project deps (use dedicated targets). */ fun Project.androidLibrary( packageName: String, @@ -48,7 +55,14 @@ fun Project.androidLibrary( ) applyDependencies( - validator = EmptyValidator, + validator = validator( + LibraryTargetTemplate, + UtilTargetTemplate, + AndroidUtilTargetTemplate, + TestUtilTargetTemplate, + ResourcesTargetTemplate, + ApiTargetTemplate, + ), dependencies = dependencies, testDependencies = testDependencies, androidTestDependencies = androidTestDependencies, @@ -58,4 +72,3 @@ fun Project.androidLibrary( return TargetBuilder(this) } - diff --git a/plugins/android/src/main/java/api.kt b/plugins/android/src/main/java/api.kt index 6c4b0530..f353c9b5 100644 --- a/plugins/android/src/main/java/api.kt +++ b/plugins/android/src/main/java/api.kt @@ -11,6 +11,13 @@ import tools.forma.deps.core.applyDependencies import tools.forma.deps.core.FormaDependency import tools.forma.validation.validate +/** + * Feature **API** surface (JVM). Dagger2-friendly public contracts only. + * + * May depend on other `api` and pure `library` modules. Must not depend on + * `impl`, Android UI (`widget` / `viewbinding` / `res`), or utils that pull + * Android into the contract layer. + */ fun Project.api( packageName: String, owner: Owner = NoOwner, @@ -29,4 +36,3 @@ fun Project.api( repositoriesConfiguration = Forma.settings.repositories ) } - diff --git a/plugins/android/src/main/java/impl.kt b/plugins/android/src/main/java/impl.kt index d468e08e..00f1ccfc 100644 --- a/plugins/android/src/main/java/impl.kt +++ b/plugins/android/src/main/java/impl.kt @@ -21,6 +21,15 @@ import tools.forma.deps.core.applyDependencies import tools.forma.validation.validate import tools.forma.validation.validator +/** + * Feature **implementation** (Android library + optional view binding / kapt). + * + * Dagger2-friendly boundaries: + * - **May** depend on feature `api` contracts, shared libraries/utils, and UI + * building blocks (`res`, `viewbinding`, `widget`, `ui-library`). + * - **Must not** depend on other `impl` modules — feature graphs compose only + * at [androidApp] / [androidBinary] so implementations stay independent. + */ fun Project.impl( packageName: String, dependencies: FormaDependency = emptyDependency(), @@ -67,4 +76,3 @@ fun Project.impl( configurationFeatures = kaptConfigurationFeature() ) } - diff --git a/plugins/validation/src/main/java/tools/forma/validation/Validator.kt b/plugins/validation/src/main/java/tools/forma/validation/Validator.kt index d4f8decd..9fc6db8f 100644 --- a/plugins/validation/src/main/java/tools/forma/validation/Validator.kt +++ b/plugins/validation/src/main/java/tools/forma/validation/Validator.kt @@ -43,8 +43,9 @@ fun throwProjectValidationError( ) { throw ProjectValidationError( """ - Project ${name}: name does not match type requirements - Projects of type "${targets.joinToString { it::class.simpleName ?: "" }} should contain name suffix from the list: "${targets.joinToString { it.suffix }}" + Project $name: name does not match allowed target type(s) + Allowed name suffix(es): ${targets.joinToString { it.suffix }} + (Used for self-type checks and project-dependency type checks.) """.trimIndent() ) }