blob: 058dc38d9a76508b79b8619c7e6fe0b5008b4e96 [file] [view]
# Flutter Gradle Plugin (FGP) Architecture & Development Rules
## Scope & Legacy Code Policy (The Ratchet Principle)
- **New Code & Modified Lines**: All newly introduced tasks, properties, build logic, and modified lines must strictly comply with these rules.
- **No Automatic Legacy Refactoring**: Pre-existing code that violates target architectural guidelines (such as Worker API adoption, configuration-cache compliance, or legacy mock boilerplate) should **not** be automatically refactored within an unrelated PR to prevent scope explosion, high review burden, and regression risks.
- **Surface Opportunities to the User**: When encountering adjacent legacy violations or cleanup opportunities during development, bring them to the user's attention with a brief rationale rather than silently skipping them or applying unapproved refactors. The user can then decide whether to include a localized cleanup or track it for a follow-up PR.
- **Dedicated Refactoring**: Large-scale migrations of existing tasks to modern Gradle APIs should be planned and executed in dedicated, standalone pull requests.
---
## 1. Zero Pollution of Customer-Facing Build Scripts
- **Consumer Build Isolation**: `build.gradle.kts` is evaluated directly in customer projects via composite builds (`includeBuild`). Keep build scripts minimal with only logic needed to compile and package the plugin.
- **No Contributor Tasks in Build Scripts**: Never register contributor verification tasks, custom lint tasks, or lifecycle hooks (`check.dependsOn`, `test.dependsOn`) in customer-evaluated build scripts.
- **Offline Verification**: Implement all contributor assertions, bytecode validations, and ABI checks in unit tests (`src/test/kotlin/`) or CI integration test shards (`packages/flutter_tools/test/integration.shard/`).
## 2. Type-Safe AGP & Gradle API Usage (No `@Suppress`)
- **No Unsafe Casts or Suppression**: Never use raw wildcard casts (e.g., `as NamedDomainObjectContainer<Any>`) or `@Suppress("UNCHECKED_CAST")`. Unchecked casts conceal breaking changes across AGP versions.
- **Use Idiomatic Type-Safe APIs**: Use official, type-safe AGP APIs (e.g., `pluginBuildTypes.create(name) { initWith(source) }`) to manipulate domain objects safely across AGP releases.
- **Structured Compatibility Layers**: When bridging binary- or DSL-incompatible AGP versions (e.g., AGP 8 vs 9), introduce explicit, type-safe abstraction wrappers or reflection bridges.
## 3. Lazy Configuration & Execution Avoidance
- **Never Eagerly Resolve at Configuration Time**: Never call .get() or .getOrNull() during plugin application or task configuration (afterEvaluate, task creation). Wire inputs and outputs lazily using Property, Provider, ListProperty, MapProperty, DirectoryProperty, and RegularFileProperty.
- **Wire Providers Directly**: Pass providers directly into task inputs (e.g., `task.inputDir.set(extension.path)`).
## 4. Strict Configuration Cache Compatibility
- **No Project State in Tasks**: Tasks must never hold references to `Project`, `SourceSet`, `Configuration`, or other non-serializable Gradle model objects in fields or action closures.
- **Pass Serializable Inputs**: Inject required values as primitive types, serializable data structures, or Gradle Property instances annotated with @Input, @InputFiles, or @InputDirectory.
- **Use Injected Services**: Use Gradle service injection (e.g., @Inject for FileSystemOperations, ArchiveOperations, or ExecOperations) inside tasks instead of calling Project helper methods.
## 5. Build Avoidance & Path Normalization
- **Path Normalization**: Annotate all file inputs (such as @InputFile or @InputDirectory) with @PathSensitive(PathSensitivity.RELATIVE) or @PathSensitive(PathSensitivity.NAME_ONLY) (instead of absolute paths) to ensure cache hits across different machines and CI environments.
- **Deterministic Cache Keys**: Never mark non-deterministic inputs (such as timestamps or machine-dependent environment variables) as `@Input`.
- **Explicit Outputs**: Declare `@OutputFile` or `@OutputDirectory` for every produced artifact.
## 6. Worker API for Heavy Computation
- **Thread & Classloader Isolation**: Offload heavy computation, class parsing, code generation, or external process execution to `WorkerExecutor` / `WorkQueue` (using `noIsolation()`, `classLoaderIsolation()`, or `processIsolation()`) to keep the main Gradle daemon thread non-blocking.
## 7. Namespace & Environment Hygiene
- **Namespace Internal Properties**: Prefix all internal Gradle properties, project extensions, and system properties with `flutter.internal.` (e.g., `flutter.internal.agpVersion`) to prevent collisions with customer app configurations.
- **Matrix Verification**: Verify compatibility against the full matrix of supported AGP versions (AGP 8.x through 9.x) and Gradle versions.