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
19 changes: 8 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,14 @@ buildscript {
}
```

Your kotlin android library
Your kotlin android library (role-specific — prefer `androidUtil` / `uiLibrary` /
`impl` over deprecated `androidLibrary`)

``` gradle
// Single method, type-safe creation of your target
// Plugins applied automatically
// Project configuration shared between targets
androidLibrary(
androidUtil(
// Mandatory, visible from build configuration
packageName = "tools.forma.sample.example",
// External dependencies declaration, one universal syntax
Expand All @@ -103,15 +104,11 @@ androidLibrary(
androidx.appcompat,
) + deps(
// Internal project dependencies, declared separately from externals
project(":demo-library")
project(":demo-android-util")
),
// Test dependencies declaration
testDependencies = deps(
test.junit
),
// Android test dependencies declaration
androidTestDependencies = deps(
test.espresso
)
)
```
Expand Down Expand Up @@ -150,7 +147,7 @@ project-dep type checks + content layout rules from live validators
|:----------------------:|:-----------:|:-------:|:----------:|
| `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`) |
| `androidLibrary` | ⚠️ deprecated | Generic Android lib escape hatch — **do not use** | see [`docs/ANDROID-LIBRARY-DEPRECATION.md`](docs/ANDROID-LIBRARY-DEPRECATION.md) |
| `uiLibrary` | ✅ | Shared UI library for impl/widget | project-dep list |
| `widget` | ✅ | Custom View / UI component | project-dep list |
| `composeWidget` | ✅ | Compose UI component | always Compose + project-dep list |
Expand All @@ -177,16 +174,16 @@ That document is generated from each target’s
| `api` | `api`, `library` |
| `impl` | `api`, `android-util`, `test-util`, `util`, `library`, `ui-library`, `res`, `viewbinding`, `widget`, `compose-widget` |
| `library` (JVM) | `util`, `test-util` |
| `androidLibrary` | `library`, `util`, `android-util`, `test-util`, `res`, `api` |
| `androidLibrary` (deprecated) | `library`, `util`, `android-util`, `test-util`, `res`, `api` |
| `uiLibrary` | `widget`, `compose-widget`, `util`, `android-util`, `res` |
| `util` | `util`, `library` |
| `androidUtil` | `android-util`, `test-util`, `res` |
| `androidUtil` | `android-util`, `test-util`, `res`, `library` |
| `testUtil` | `test-util`, `util` |
| `androidTestUtil` | `android-test-util`, `test-util` |
| `androidRes` | `res`, `widget`, `compose-widget` |
| `widget` | `ui-library`, `widget`, `compose-widget`, `util`, `android-util`, `res` |
| `composeWidget` | `ui-library`, `compose-widget`, `widget`, `util`, `android-util`, `res` |
| `viewBinding` | `api`, `widget`, `compose-widget`, `res`, `library`, `android-util` |
| `viewBinding` | `api`, `widget`, `compose-widget`, `res`, `library`, `android-util`, `ui-library` |
| `androidApp` | `api`, `impl`, `library`, `util`, `android-util`, `test-util`, `res`, `viewbinding`, `widget`, `compose-widget`, `ui-library` |
| `androidBinary` | `app`, `api`, `impl`, `library`, `util`, `android-util`, `test-util`, `res`, `viewbinding`, `widget`, `compose-widget`, `ui-library` |
| `androidNative` | *(no project-dep check)* |
Expand Down
13 changes: 13 additions & 0 deletions TICKETS.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,19 @@ Update this file when picking or finishing work. Cron workers must pick the **hi
|----|--------|-------|-------|
| F-050 | done | Progressive examples + agent skills for all supported features | `examples/{jvm,android}/*` + `examples/agent-skills/` + `docs/PROGRESSIVE-EXAMPLES.md`; JVM build+run and Android assembleDebug verified |

## P6 — Flat structure (deprecate generic androidLibrary)

Forma’s job is to **keep the graph flat and role-typed**. `androidLibrary` was a
temporary generic AGP-library escape hatch (shared suffix with JVM `library`) that
encourages dumping mixed concerns into one bucket. Prefer specific targets.

| ID | Status | Title | Notes |
|----|--------|-------|-------|
| F-060 | done | Deprecate `androidLibrary` + document flat-structure policy | `@Deprecated` on DSL; VISION + `docs/ANDROID-LIBRARY-DEPRECATION.md`; matrix/README |
| F-061 | done | Migrate `application/` sample off `androidLibrary` | di→`androidUtil`, mvvm→`uiLibrary`, navigation→`androidRes`; path renames |
| F-062 | done | Reimplement progressive examples / skills without `androidLibrary` | `examples/android/04` + agent skills + README curriculum |
| F-063 | todo | Hard-remove `androidLibrary` target (after consumers migrated) | Drop registry type / docs row once no in-repo callers |

## Backlog (lower priority / historical GitHub)

Keep for reference; do not start unless higher tickets done or user prioritizes:
Expand Down
4 changes: 2 additions & 2 deletions application/binary/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ androidBinary(
target(":common:extensions:android-util"),
// target(":common:util-native"),
target(":common:greeting:compose-widget"),
target(":core:mvvm:library"),
target(":core:di:library")
target(":core:mvvm:ui-library"),
target(":core:di:android-util")
)
)
// TODO: enable when create crashlytics project
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
androidLibrary(
androidUtil(
packageName = "tools.forma.sample.core.di.library",
dependencies = deps(
google.dagger
)
)
)
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@startuml

together {
package ":core:di:library" {
package ":core:di:android-util" {
class BaseModule {
+ providesContext(app: Application): Context
}
Expand Down Expand Up @@ -71,7 +71,7 @@ package ":app" {
":app" ..> ":root" : deps
":app" ..> ":feature:home:api" : deps
":app" ..> ":feature:home:impl" : deps
":app" ..> ":core:di:library" : deps
":app" ..> ":core:di:android-util" : deps


@enduml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// TODO Change module type from `androidLibary` to `databinding`
androidLibrary(
// Shared MVVM / ViewBinding UI bases — typed as uiLibrary (not generic androidLibrary).
uiLibrary(
packageName = "tools.forma.sample.core.mvvm.library",

dependencies = deps(
Expand All @@ -12,7 +12,7 @@ androidLibrary(
androidx.paging,
viewbinding.viewBinding,
google.dagger,
)+ deps(
) + deps(
libs.jakewhartonTimber,
),

Expand Down
6 changes: 0 additions & 6 deletions application/core/navigation/library/build.gradle.kts

This file was deleted.

7 changes: 7 additions & 0 deletions application/core/navigation/res/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Navigation graphs only — resources target (flat structure, not androidLibrary).
androidRes(
packageName = "tools.forma.sample.core.navigation.library",
dependencies = deps(
androidx.navigation
)
).withPlugin(Plugins.navigationSafeArgs)
4 changes: 2 additions & 2 deletions application/feature/characters/core/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ impl(
) + deps(
target(":feature:characters:core:api"),

target(":core:di:library"),
target(":core:di:android-util"),
target(":core:theme:android-util"),
target(":core:mvvm:library"),
target(":core:mvvm:ui-library"),
target(":core:network:library"),

target(":common:extensions:util"),
Expand Down
6 changes: 3 additions & 3 deletions application/feature/characters/detail/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ impl(
target(":feature:characters:detail:res"),
target(":feature:characters:detail:viewbinding"),

target(":core:di:library"),
target(":core:di:android-util"),
target(":core:theme:android-util"),
target(":core:mvvm:library"),
target(":core:mvvm:ui-library"),
target(":core:network:library"),
target(":core:navigation:library"),
target(":core:navigation:res"),

target(":common:util"),
target(":common:extensions:android-util"),
Expand Down
6 changes: 3 additions & 3 deletions application/feature/characters/favorite/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ impl(
target(":feature:characters:favorite:res"),
target(":feature:characters:favorite:viewbinding"),

target(":core:di:library"),
target(":core:di:android-util"),
target(":core:theme:android-util"),
target(":core:mvvm:library"),
target(":core:mvvm:ui-library"),
target(":core:network:library"),
target(":core:navigation:library"),
target(":core:navigation:res"),

target(":common:util"),
target(":common:extensions:android-util"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ viewBinding(
target(":feature:characters:favorite:api"),
target(":feature:characters:favorite:res"),

target(":core:navigation:library"),
target(":core:mvvm:library"),
target(":core:navigation:res"),
target(":core:mvvm:ui-library"),
target(":core:theme:res")
)
)
6 changes: 3 additions & 3 deletions application/feature/characters/list/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ impl(
target(":feature:characters:list:api"),
target(":feature:characters:list:viewbinding"),
target(":feature:characters:list:res"),
target(":core:di:library"),
target(":core:di:android-util"),
target(":core:theme:android-util"),
target(":core:mvvm:library"),
target(":core:mvvm:ui-library"),
target(":core:network:library"),
target(":core:navigation:library"),
target(":core:navigation:res"),
target(":common:util"),
target(":common:extensions:android-util"),
target(":common:recyclerview:widget")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ viewBinding(

target(":core:theme:res"),
target(":core:network:library"),
target(":core:mvvm:library"),
target(":core:mvvm:ui-library"),
)
)
6 changes: 3 additions & 3 deletions application/feature/home/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ impl(
target(":feature:home:api"),
target(":feature:home:res"),
target(":feature:home:viewbinding"),
target(":core:di:library"),
target(":core:di:android-util"),
target(":core:theme:android-util"),
target(":core:mvvm:library"),
target(":core:navigation:library"),
target(":core:mvvm:ui-library"),
target(":core:navigation:res"),
target(":common:extensions:android-util"),
)
)
4 changes: 2 additions & 2 deletions application/feature/home/viewbinding/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ viewBinding(
androidx.appcompat,
androidx.constraintlayout
) + deps(
target(":core:navigation:library"),
target(":core:mvvm:library"),
target(":core:navigation:res"),
target(":core:mvvm:ui-library"),
target(":core:theme:res"),
target(":feature:home:res"),
)
Expand Down
4 changes: 2 additions & 2 deletions application/root-app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ androidApp(
target(":feature:characters:core:impl"),
target(":feature:characters:favorite:api"),
target(":feature:characters:favorite:impl"),
target(":core:di:library"),
target(":core:navigation:library"),
target(":core:di:android-util"),
target(":core:navigation:res"),
target(":core:theme:android-util"),
target(":core:network:library"),
target(":common:util")
Expand Down
59 changes: 59 additions & 0 deletions docs/ANDROID-LIBRARY-DEPRECATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Deprecating `androidLibrary` (F-060+)

## Why

`androidLibrary` is a **generic AGP library** target. It was a temporary way to
host “shared Android code that isn’t a feature `impl`.” That works against
Forma’s goal: **keep structure flat and role-typed**.

Problems:

1. **Suffix collision** with JVM `library` (`library` name suffix / historical
`LibraryTargetTemplate`) — validators resolve deps by suffix and cannot tell
JVM vs Android library modules apart reliably.
2. **Mixed concerns** — sample historically stuffed DI scopes, MVVM/UI bases, and
navigation XML into the same target kind.
3. **Weak teaching signal** — newcomers reach for `androidLibrary` instead of the
target that matches the module’s job.

## Replacement map

| If the module is… | Use instead | Suffix |
|-------------------|-------------|--------|
| Pure JVM shared code | `library` | `library` |
| Small Android helpers, **no** `res/` | `androidUtil` | `android-util` |
| Shared UI bases for `impl` / `widget` | `uiLibrary` | `ui-library` |
| Resources only (`res/` tree) | `androidRes` | `res` |
| Layout XML only | `viewBinding` | `viewbinding` |
| Custom View | `widget` | `widget` |
| Compose UI component | `composeWidget` | `compose-widget` |
| Feature implementation | `impl` | `impl` |
| Feature contracts | `api` | `api` |

## Sample migration (F-061)

| Old path | Old DSL | New path | New DSL |
|----------|---------|----------|---------|
| `core/di/library` | `androidLibrary` | `core/di/android-util` | `androidUtil` |
| `core/mvvm/library` | `androidLibrary` | `core/mvvm/ui-library` | `uiLibrary` |
| `core/navigation/library` | `androidLibrary` | `core/navigation/res` | `androidRes` |

Kotlin **packages** may keep historical `.library` segments to limit churn;
**project path / suffix** is what Forma validates.

## Matrix tweaks paired with migration

- `viewBinding` may depend on `ui-library` (shared UI bases without generic library).
- `androidUtil` may depend on JVM `library` (Android helpers wrapping pure JVM code).

## Lifecycle

| Ticket | Intent |
|--------|--------|
| **F-060** | `@Deprecated` on DSL + docs/vision |
| **F-061** | `application/` sample migrated |
| **F-062** | Progressive examples + agent skills teach replacements |
| **F-063** | Hard-remove target type / registry entry when nothing in-repo calls it |

Until F-063, `androidLibrary { }` still configures modules but should not appear
in new code or tutorials.
18 changes: 9 additions & 9 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ Defined in `plugins/android/.../AndroidTargets.kt`:
|--------|--------|---------------------|
| `BinaryTargetTemplate` | `binary` | `androidBinary` |
| `ApplicationTargetTemplate` | `app` | `androidApp` |
| `LibraryTargetTemplate` | `library` | `androidLibrary` **and** `library` (JVM) |
| `LibraryTargetTemplate` | `library` | `library` (JVM); deprecated `androidLibrary` shares suffix |
| `UiLibraryTargetTemplate` | `ui-library` | `uiLibrary` |
| `NativeTarget` | `native` | `androidNative` |
| `UtilTargetTemplate` | `util` | `util` |
Expand All @@ -158,25 +158,25 @@ when validators change.
| `api` | `api`, `library` | no `res/` under `src/main` |
| `impl` | `api`, `android-util`, `test-util`, `util`, `library`, `ui-library`, `res`, `viewbinding`, `widget`, `compose-widget` | — |
| `library` (JVM) | `util`, `test-util` | — |
| `androidLibrary` | `library`, `util`, `android-util`, `test-util`, `res`, `api` | — |
| `androidLibrary` (deprecated) | `library`, `util`, `android-util`, `test-util`, `res`, `api` | — |
| `uiLibrary` | `widget`, `compose-widget`, `util`, `android-util`, `res` | — |
| `util` | `util`, `library` | no `res/` |
| `androidUtil` | `android-util`, `test-util`, `res` | no `res/` |
| `androidUtil` | `android-util`, `test-util`, `res`, `library` | no `res/` |
| `testUtil` | `test-util`, `util` | no `res/` |
| `androidTestUtil` | `android-test-util`, `test-util` | — |
| `androidRes` | `res`, `widget`, `compose-widget` | **only** `res/` under `src/main` |
| `widget` | `ui-library`, `widget`, `compose-widget`, `util`, `android-util`, `res` | — |
| `composeWidget` | `ui-library`, `compose-widget`, `widget`, `util`, `android-util`, `res` | always Compose |
| `viewBinding` | `api`, `widget`, `compose-widget`, `res`, `library`, `android-util` | only `layout*` under `src/main/res` |
| `viewBinding` | `api`, `widget`, `compose-widget`, `res`, `library`, `android-util`, `ui-library` | only `layout*` under `src/main/res` |
| `androidApp` | `api`, `impl`, `library`, `util`, `android-util`, `test-util`, `res`, `viewbinding`, `widget`, `compose-widget`, `ui-library` | no `res/` |
| `androidBinary` | `app`, `api`, `impl`, `library`, `util`, `android-util`, `test-util`, `res`, `viewbinding`, `widget`, `compose-widget`, `ui-library` | no `res/` |
| `androidNative` | (no `applyDependencies` in current code) | no `res/` |

Notes for later tickets:

- `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).
- `androidApp` / `androidBinary` use restricted composition-root lists — F-011 done.
- `androidLibrary` **deprecated** (F-060+); JVM `library` keeps the `library` suffix — see [`ANDROID-LIBRARY-DEPRECATION.md`](ANDROID-LIBRARY-DEPRECATION.md).

### 2.3 Feature stack (Android module)

Expand Down Expand Up @@ -225,9 +225,9 @@ application/
├── root-res/ androidRes
├── toggle-widget/ widget
├── core/
│ ├── di/library androidLibrary
│ ├── mvvm/library androidLibrary
│ ├── navigation/library androidLibrary
│ ├── di/android-util androidUtil
│ ├── mvvm/ui-library uiLibrary
│ ├── navigation/res androidRes
│ ├── network/library library (JVM)
│ └── theme/{android-util,res}
├── common/
Expand Down
Loading
Loading