Repository navigation
Move aar_init_script off legacy AGP APIs - #9
Closed
reidbaker-agent wants to merge 1 commit into
Closed
reidbaker-agent wants to merge 1 commit into
reidbaker-agent wants to merge 1 commit into
Conversation
reidbaker-agent
force-pushed
the
agp-aar-script
branch
from
July 22, 2026 15:54
27bf63d to
4b3f4e5
Compare
reidbaker-agent
force-pushed
the
agp-add-to-app
branch
from
July 28, 2026 13:36
d384683 to
190cb3b
Compare
reidbaker-agent
force-pushed
the
agp-aar-script
branch
from
July 28, 2026 13:36
4b3f4e5 to
594f593
Compare
reidbaker-agent
force-pushed
the
agp-add-to-app
branch
from
July 28, 2026 13:37
190cb3b to
f861ab5
Compare
reidbaker-agent
force-pushed
the
agp-aar-script
branch
from
July 28, 2026 13:37
594f593 to
2e64d7a
Compare
reidbaker-agent
force-pushed
the
agp-add-to-app
branch
from
July 29, 2026 20:22
f861ab5 to
82d9979
Compare
reidbaker-agent
force-pushed
the
agp-aar-script
branch
from
July 29, 2026 20:22
2e64d7a to
bb22914
Compare
reidbaker-agent
force-pushed
the
agp-add-to-app
branch
from
July 29, 2026 20:44
82d9979 to
49bc8bd
Compare
reidbaker-agent
force-pushed
the
agp-aar-script
branch
2 times, most recently
from
July 30, 2026 21:58
65bafb0 to
39f0c33
Compare
reidbaker-agent
force-pushed
the
agp-add-to-app
branch
from
July 31, 2026 16:16
645fe6f to
a4a3dbd
Compare
reidbaker-agent
force-pushed
the
agp-aar-script
branch
from
July 31, 2026 16:16
39f0c33 to
89849d6
Compare
reidbaker
force-pushed
the
agp-add-to-app
branch
from
July 31, 2026 18:07
a4a3dbd to
447a829
Compare
reidbaker
force-pushed
the
agp-aar-script
branch
from
July 31, 2026 18:07
89849d6 to
4a8e3a6
Compare
Phase P8 of the AGP public-API migration (flutter#180137, flutter#166550): - The library-project check no longer probes for the legacy android.libraryVariants property (absent under the new DSL); it checks for the com.android.library plugin. Error message unchanged. - The plugin-to-module assembleAar task wiring enumerates published variants through the public software components (which the script already used for task creation) instead of libraryVariants. - The singleVariant dedup guard no longer reads the internal publishing.singleVariants collection. Flutter now marks projects it configured with an ext property and declares each variant's publishing in a try-catch: when the project's own build file already declared publishing for a variant, the user's declaration wins and a warning explains the situation and the fix. (Previously any user-declared variant silently disabled Flutter's publishing setup for ALL variants, which broke partially-declared projects at publication time with no explanation.) Verification (CI): flutter build aar with and without flavors for module and plugin projects; AAR-host add-to-app flow; a scratch module with a partial user singleVariant declaration; newDsl=true aar flow. Revert-safe even after P10, but not after P9.
reidbaker
force-pushed
the
agp-aar-script
branch
from
August 6, 2026 14:37
4a8e3a6 to
7aa69dc
Compare
reidbaker
force-pushed
the
agp-add-to-app
branch
from
August 6, 2026 14:37
447a829 to
1ae9c41
Compare
This was referenced Aug 10, 2026
10 tasks done
pull Bot
pushed a commit
to Little-Star888/flutter
that referenced
this pull request
Aug 12, 2026
…on documentation (flutter#190842) There are 2 files in this pr. One is a document the ai used to keep track of work. More importantly it acts kind of like issues so it references the items in future prs. The second is user facing website documentation. I do not know if we will use it verbatim but for the set of prs lets treat that md doc as human understandable documentation that we must understand before landing the next pr. Reviewers: When the pr is out of draft and your comments are fully addressed please prioritize this pr over other work. The review bar is higher, the number of reviews has more people and the work for the next pr is already done. - @reidbaker --- Standard review context for this pr stack This is PR is part of an 11 pr stack to migrate the "newdsl" `gradle-api` specifically in agp 9.1.0. The complete stack has pass pre submits, post submits and customer tests. https://flutter-dashboard.appspot.com/#/build?repo=flutter&branch=experimental/agp-gradle-api All of the code was LLM authored. A mix of manual prompting, automatic prompting, several models and adversarial review. The combined sessions are enough that I cannot include relevant prompts like I have been doing on other prs. If you want to review the pr stack you can find it here. These prs will be abandoned/closed as prs land into flutter/flutter. 1. reidbaker-agent#1 (branch: agp-api-doc) 2. reidbaker-agent#2 (branch: agp-internal-utils) 3. reidbaker-agent#3 (branch: agp-buildmode-deps) 4. reidbaker-agent#4 (branch: agp-plugin-buildtypes) 5. reidbaker-agent#5 (branch: agp-ndk-fallback) 6. reidbaker-agent#6 (branch: agp-assets-onvariants) 7. reidbaker-agent#7 (branch: agp-apk-copy-versioncode) 8. reidbaker-agent#8 (branch: agp-add-to-app) 9. reidbaker-agent#9 (branch: agp-aar-script) 10. reidbaker-agent#10 (branch: agp-newdsl-flip) 11. reidbaker-agent#11 (branch: agp-gradle-api) Follow up work is tracked in flutter#190964 This work is urgent in the sense that we are worried that android will publish agp 10 with no opt out but not so urgent that we are willing to break flutter users because we didn't review or understand the code because we were in a rush. Breaking changes are expected as part of this work. There are patterns the android team explicitly does not want apps to use and apis that have no equivalent. As part of the effort to ensure this work does not slip into ai slop, prs from this stack will be reviewed by me (@reidbaker) before asking for review. Then we will have 2 android expert reviewers also review the every pr. --- Agent authored pr description This is PR 1 of 11 in the AGP 9.1.0 / public `gradle-api` migration stack. It adds the contributor-facing and website draft documentation for the Flutter Gradle Plugin's migration to the Android Gradle Plugin public Variant API, which unblocks building Flutter Android apps with AGP's `newDsl=true` enabled. Part of flutter#180137 and flutter#166550. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. --------- Co-authored-by: reidbaker-agent <[email protected]>
10 tasks done
okorohelijah
pushed a commit
to okorohelijah/flutter
that referenced
this pull request
Aug 17, 2026
…90957) This is PR 2 of 11 in the AGP 9.1.0 / public `gradle-api`/ newdls migration stack. This PR extracts some common utilities used in the Flutter Gradle Plugin to internal functions, to be used in upcoming PRs in this stack. It also introduces a typesafe CompileSdkVersion that handles comparisons between api versions and preview versions which are strings. First attempt was here flutter#190949 this pr includes my feedback from that first review. The first attempt did not follow the pattern of having the agent account open the pr because of rebase shenanigans that ended up touching freeze.yml. --- Standard review context for this pr stack This is PR is part of an 11 pr stack to migrate the "newdsl" `gradle-api` specifically in agp 9.1.0. The complete stack has pass pre submits, post submits and customer tests. https://flutter-dashboard.appspot.com/#/build?repo=flutter&branch=experimental/agp-gradle-api All of the code was LLM authored. A mix of manual prompting, automatic prompting, several models and adversarial review. The combined sessions are enough that I cannot include relevant prompts like I have been doing on other prs. If you want to review the pr stack you can find it here. These prs will be abandoned/closed as prs land into flutter/flutter. 1. reidbaker-agent#1 (branch: agp-api-doc) 2. reidbaker-agent#2 (branch: agp-internal-utils) 3. reidbaker-agent#3 (branch: agp-buildmode-deps) 4. reidbaker-agent#4 (branch: agp-plugin-buildtypes) 5. reidbaker-agent#5 (branch: agp-ndk-fallback) 6. reidbaker-agent#6 (branch: agp-assets-onvariants) 7. reidbaker-agent#7 (branch: agp-apk-copy-versioncode) 8. reidbaker-agent#8 (branch: agp-add-to-app) 9. reidbaker-agent#9 (branch: agp-aar-script) 10. reidbaker-agent#10 (branch: agp-newdsl-flip) 11. reidbaker-agent#11 (branch: agp-gradle-api) This work is urgent in the sense that we are worried that android will publish agp 10 with no opt out but not so urgent that we are willing to break flutter users because we didn't review or understand the code because we were in a rush. Breaking changes are expected as part of this work. There are patterns the android team explicitly does not want apps to use and apis that have no equivalent. As part of the effort to ensure this work does not slip into ai slop, prs from this stack will be reviewed by me (@reidbaker) before asking for review. Then we will have 2 android expert reviewers also review the every pr. --- Agent authored description. This is PR 2 of 11 in the AGP 9.1.0 / public `gradle-api` migration stack. This PR extracts some common utilities used in the Flutter Gradle Plugin to internal functions, to be used in upcoming PRs in this stack. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. --------- Co-authored-by: reidbaker-agent <[email protected]> Co-authored-by: Reid Baker <[email protected]>
10 tasks done
10 tasks done
pull Bot
pushed a commit
to TheRakeshPurohit/flutter
that referenced
this pull request
Aug 24, 2026
…dependencies through the new DSL (flutter#191218) This is PR 3 of 11 in the AGP 9.1.0 / public `gradle-api`/ newdsl migration stack. * It migrates many (but not all) usages of getLegacyAndroidExtension. * Introduces consistency in renaming of imports. * Adds a test to ensure we are not adding internal apis (thanks @mboetger from pr1) * builds the ability to run our gradle tests with multiple AGP versions (see packages/flutter_tools/gradle/build.gradle.kts) Between PR 3 and PR 8, an Add-to-app host app embedding a Flutter module with a custom build type (for example "staging") gets release engine artifacts. That means hot reload, debugger attach, and DevTools do not operate in that build. We can't move the work in PR 8 up but I would not want to cut a release between this pr and 8 landing. If we keep reviewing one pr a day then that is not a risk. Apps impacted by this change can use matchingFallbacks to avoid this problem (see code below). In pr 8 we change when we look for "isDebuggable" to much later in the gradle lifeycle when all the variants have been created which then lets us use a new api to understand if the variant is intended to be debuggable. Kotlin ```kotlin // host app build.gradle.kts android { buildTypes { create("staging") { isDebuggable = true applicationIdSuffix = ".staging" matchingFallbacks += "debug" /// This line. } } } ``` Groovy ```groovy // host app build.gradle android { buildTypes { staging { debuggable true applicationIdSuffix ".staging" matchingFallbacks = ['debug'] /// This line. } } } ``` Depends on flutter#190957 (PR 2). - @reidbaker --- Standard review context for this pr stack This is PR is part of an 11 pr stack to migrate the "newdsl" `gradle-api` specifically in agp 9.1.0. The complete stack has passed presubmits, postsubmits, and customer tests: https://flutter-dashboard.appspot.com/#/build?repo=flutter&branch=experimental/agp-gradle-api All of the code was LLM authored. A mix of manual prompting, automatic prompting, several models and adversarial review. The combined sessions are enough that I cannot include relevant prompts like I have been doing on other prs. If you want to review the pr stack you can find it here. These prs will be abandoned/closed as prs land into flutter/flutter. 1. reidbaker-agent#1 (branch: agp-api-doc) 2. reidbaker-agent#2 (branch: agp-internal-utils) 3. reidbaker-agent#3 (branch: agp-buildmode-deps) 4. reidbaker-agent#4 (branch: agp-plugin-buildtypes) 5. reidbaker-agent#5 (branch: agp-ndk-fallback) 6. reidbaker-agent#6 (branch: agp-assets-onvariants) 7. reidbaker-agent#7 (branch: agp-apk-copy-versioncode) 8. reidbaker-agent#8 (branch: agp-add-to-app) 9. reidbaker-agent#9 (branch: agp-aar-script) 10. reidbaker-agent#10 (branch: agp-newdsl-flip) 11. reidbaker-agent#11 (branch: agp-gradle-api) This work is urgent in the sense that we are worried that android will publish agp 10 with no opt out but not so urgent that we are willing to break flutter users because we didn't review or understand the code because we were in a rush. Breaking changes are expected as part of this work. There are patterns the android team explicitly does not want apps to use and apis that have no equivalent. As part of the effort to ensure this work does not slip into ai slop, prs from this stack will be reviewed by me (@reidbaker) before asking for review. Then we will have 2 android expert reviewers also review every pr. --- Agent authored description. This is PR 3 of 11 in the AGP 9.1.0 / public `gradle-api` migration stack (flutter#180137, flutter#166550). ### Key Changes - **DSL Build Mode Overloads**: Adds `buildModeFor` overloads accepting `ApplicationBuildType`, `DynamicFeatureBuildType`, and `LibraryBuildType` DSL types alongside the `(buildTypeName, isDebuggable)` core overload. - **Robust CompileSdkVersion Domain Model**: Extracts `CompileSdkVersion` with constructor invariant validation (`init { require(...) }`) enforcing mutual exclusivity between `apiLevel` and `previewCodename`. - **Namespaced Multi-AGP Build Property**: Namespaces the AGP version property in `packages/flutter_tools/gradle/build.gradle.kts` as `flutter.internal.agpVersion` (defaulting to `8.11.1`) to prevent user app properties from leaking into the included build during app compilation. - **Standardized Type Aliasing**: Applies non-temporal type aliasing (`import com.android.build.api.dsl.BuildType as DslBuildType`) across `FlutterPlugin.kt` and `PluginHandler.kt`. - **Public DSL Extension Access**: Updates `addFlutterDependencies` and `PluginHandler` to iterate `AgpCommonExtensionWrapper.buildTypes`. - **Build & Bytecode Validation**: Adds the `:validateNoCommonExtensionInBytecode` task in `build.gradle.kts` to prevent compiled main classes from referencing binary-incompatible `CommonExtension`, and adds `BytecodeValidatorTest.kt` verifying the binary pattern-matching and class scanning logic. - **Comprehensive Unit Tests**: Adds full 4-way dispatch test coverage in `AgpCommonExtensionWrapperTest`, dependency wiring tests in `FlutterPluginTest`, and decomposes `PluginHandlerTest` mock fixtures into focused helpers with positive assertions. ### Add-to-App & DSL Scope Context `LibraryBuildType` does not expose `isDebuggable` at DSL scope. In PR 3, custom build types on library projects (e.g. host app 'staging') fall back to `"release"` at DSL scope. In PR 8 ("Unify add-to-app module wiring on the variant API"), module wiring will be unified on `Component.debuggable` at variant scope. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. --------- Co-authored-by: reidbaker-agent <[email protected]> Co-authored-by: Reid Baker <[email protected]>
NikhilKukreja26
pushed a commit
to NikhilKukreja26/flutter
that referenced
this pull request
Aug 28, 2026
…nitWith on public DSL (flutter#191606) This is PR 4 of 11 in the AGP 9.1.0 / public `gradle-api` / newdsl migration stack. There are no breaking changes expected in this pr. Only internal logic is impacted. I will be honest I also found the tests hard to review. I had the agent pull out shared mocking logic but I am not sure that actually made review easier. Depends on flutter#191218 (PR 3). - @reidbaker --- Standard review context for this pr stack This is PR is part of an 11 pr stack to migrate the "newdsl" `gradle-api` specifically in agp 9.1.0. All of the code was LLM authored. A mix of manual prompting, automatic prompting, several models and adversarial review. The combined sessions are enough that I cannot include relevant prompts like I have been doing on other prs. If you want to review the pr stack you can find it here. These prs will be abandoned/closed as prs land into flutter/flutter. 1. reidbaker-agent#1 (branch: agp-api-doc) 2. reidbaker-agent#2 (branch: agp-internal-utils) 3. reidbaker-agent#3 (branch: agp-buildmode-deps) 4. reidbaker-agent#4 (branch: agp-plugin-buildtypes) 5. reidbaker-agent#5 (branch: agp-ndk-fallback) 6. reidbaker-agent#6 (branch: agp-assets-onvariants) 7. reidbaker-agent#7 (branch: agp-apk-copy-versioncode) 8. reidbaker-agent#8 (branch: agp-add-to-app) 9. reidbaker-agent#9 (branch: agp-aar-script) 10. reidbaker-agent#10 (branch: agp-newdsl-flip) 11. reidbaker-agent#11 (branch: agp-gradle-api) This work is urgent in the sense that we are worried that android will publish agp 10 with no opt out but not so urgent that we are willing to break flutter users because we didn't review or understand the code because we were in a rush. Breaking changes are expected as part of this work. There are patterns the android team explicitly does not want apps to use and apis that have no equivalent. As part of the effort to ensure this work does not slip into ai slop, prs from this stack will be reviewed by me (@reidbaker) before asking for review. Then we will have 2 android expert reviewers also review every pr. --- Agent authored description. This is PR 4 of 11 in the AGP 9.1.0 / public `gradle-api` migration stack (flutter#180137, flutter#166550). ### Key Changes - **Public DSL `initWith` Copy**: Replaces the legacy `getLegacyAndroidExtension` build-type copy in `PluginHandler` with `initWith` on the public DSL (`AgpCommonExtensionWrapper.buildTypes`). Missing build types on the plugin project are created with `initWith(appBuildType)`, and `isDebuggable` is copied when both sides are `ApplicationBuildType`. - **Zero AGP Internals in Production Sources**: Removes the last remaining `com.android.build.gradle.internal` imports from production code (`src/main`). - **Internal AGP Import Guard**: Adds `InternalAgpApiImportTest` to continuously enforce that production sources do not introduce `com.android.build.gradle.internal.*` imports. - **Decomposed & Robust Unit Tests**: Replaces legacy mock-only tests in `PluginHandlerTest` with tests that execute `configurePlugins` and verify `initWith` copying for both library and application plugin projects, mapping custom debuggable build types to debug engine artifacts, and verifying that pre-existing plugin build types are skipped. - **Migration Documentation Update**: Adds details for the P3 pre-spike and `finalizeDsl` fallback in `Migrating-Flutter-Gradle-Plugin-to-AGP-public-API.md`. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing.
10 tasks done
bkonyi
pushed a commit
to bkonyi/flutter
that referenced
this pull request
Sep 4, 2026
… NDK fallback to public DSL (flutter#192116) This is PR 5 of 11 in the AGP 9.1.0 / public `gradle-api` / newdsl migration stack. Add externalNativeBuild to the shared type. Extracted some shared mocking logic to a utility. No external visible changes expected. Also improved the README.md so that the test file for multiple agp versions was a markdown link. - @reidbaker --- Standard review context for this pr stack This is PR is part of an 11 pr stack to migrate the "newdsl" `gradle-api` specifically in agp 9.1.0. All of the code was LLM authored. A mix of manual prompting, automatic prompting, several models and adversarial review. The combined sessions are enough that I cannot include relevant prompts like I have been doing on other prs. If you want to review the pr stack you can find it here. These prs will be abandoned/closed as prs land into flutter/flutter. 1. reidbaker-agent#1 (branch: agp-api-doc) 2. reidbaker-agent#2 (branch: agp-internal-utils) 3. reidbaker-agent#3 (branch: agp-buildmode-deps) 4. reidbaker-agent#4 (branch: agp-plugin-buildtypes) 5. reidbaker-agent#5 (branch: agp-ndk-fallback) 6. reidbaker-agent#6 (branch: agp-assets-onvariants) 7. reidbaker-agent#7 (branch: agp-apk-copy-versioncode) 8. reidbaker-agent#8 (branch: agp-add-to-app) 9. reidbaker-agent#9 (branch: agp-aar-script) 10. reidbaker-agent#10 (branch: agp-newdsl-flip) 11. reidbaker-agent#11 (branch: agp-gradle-api) This work is urgent in the sense that we are worried that android will publish agp 10 with no opt out but not so urgent that we are willing to break flutter users because we didn't review or understand the code because we were in a rush. Breaking changes are expected as part of this work. There are patterns the android team explicitly does not want apps to use and apis that have no equivalent. As part of the effort to ensure this work does not slip into ai slop, prs from this stack will be reviewed by me (@reidbaker) before asking for review. Then we will have 2 android expert reviewers also review every pr. --- Agent authored description. This is PR 5 of 11 in the AGP 9.1.0 / public `gradle-api` migration stack (flutter#180137, flutter#166550). ### Key Changes - **Public DSL Wrapper `externalNativeBuild`**: Exposes `val externalNativeBuild: ExternalNativeBuild` in `AgpCommonExtensionWrapper.kt` dispatching across `ApplicationExtension`, `LibraryExtension`, `DynamicFeatureExtension`, and `TestExtension` without referencing `CommonExtension` (bypassing AGP binary incompatibility). - **Zero Legacy `BaseExtension` in Production Sources**: Deletes `getLegacyAndroidExtension(project: Project): BaseExtension` and removes the `com.android.build.gradle.BaseExtension` import from `FlutterPluginUtils.kt`. - **Public DSL NDK Fallback Configuration**: Migrates `forceNdkDownload` and `configureSyntheticExternalNativeBuildFallback` in `FlutterPluginUtils.kt` to `getAndroidExtension(gradleProject)` using `externalNativeBuild.cmake` / `externalNativeBuild.ndkBuild`. Sets `cmake.path = File(...)`, `cmake.buildStagingDirectory = ...`, and `buildType.externalNativeBuild.cmake.arguments += ...` through the public DSL while preserving the upstream PR flutter#187201 `ndkBuild.path` check. - **Unit Test Public DSL Mocking & Cleanup**: Migrates unit tests in `FlutterPluginUtilsTest.kt` to mock public DSL types (`ApplicationExtension`, `Cmake`, `NdkBuild`), asserts CMake arguments by list content, deletes obsolete tests asserting `ApplicationExtension`-vs-`BaseExtension` `ndkVersion` preference, and removes unused `import io.mockk.called` as well as all legacy `com.android.build.gradle.internal.*` imports. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing.
11 tasks done
Owner
Author
Stack Audit Findings & Guidance for Updating Agent
Issues to resolve when re-authoring this PR:
|
8 of 10 tasks
11 tasks done
gmackall
pushed a commit
to gmackall/flutter
that referenced
this pull request
Oct 20, 2026
…Is (flutter#193962) PR 7 (flutter#193693) merged to master as `ecd1ab06865` and PR 8 (flutter#193717) as `4249ccbc18a`. This branch merges master at `4521d8d6634`, so the Files tab shows only PR 9's changes. PR 9 commits: - `86523c549fc` Move aar_init_script to public AGP APIs - `3edc95d1fa0` Check requested AAR variants in the module and each plugin - `da9646aad53` Address review: derive requested variants from components, register AAR tasks lazily - `94a29907065` Clarify aar_init_script comments from the adversarial review - `ea08280d886` Merge commit '23ddd6b74e6' into agp-aar-init-script (PR 8's head with master at `ecd1ab06865`; no conflicts, no change to PR 9's files) - `a17fafcc35e` Merge commit '5ba8961b517' into agp-aar-init-script (master with PR 8 at `4249ccbc18a`; no conflicts, no change to PR 9's files) - `77ba449ea4a` Filter requested AAR components before adding tasks - `adbfb9cd640` Drop the AGP version list from the duplicate-variant comment - `f2b7111c51e` Docs: state the AAR variant requirement in plain words - `cd10f766b5b` Register an AAR task for every variant, publish only requested ones - `b9493299916` Merge commit '4521d8d6634' into agp-aar-init-script (master, including flutter#193649; no conflicts, no change to PR 9's files) ## Description This is PR 9 of 11 in the AGP 9.1.0 / public `gradle-api` migration stack ([flutter#180137](flutter#180137), [flutter#166550](flutter#166550)). `flutter build aar` passes `packages/flutter_tools/gradle/aar_init_script.gradle` to Gradle with `-I`. The script read `android.libraryVariants` and AGP's internal `publishing.singleVariants`. With AGP 9's `android.newDsl=true`, `flutter build aar` on a module fails on `5ba8961b517` with `Can't generate AAR on a non Android library project.` (script line 13), and then at line 178 (`Could not get unknown property 'libraryVariants'`). With this PR the script uses only `pluginManager.hasPlugin`, `Publishing.singleVariant`, software `components` and `tasks.register`, and the build succeeds with `newDsl` on and off, on AGP 9.3.1 and on master's minimum AGP 9.1.1, and on AGP 8.11.1 when FGP's version check is skipped. **Breaking:** no. Builds that succeed on `5ba8961b517` succeed with the same artifacts. Two failing cases change: a module or plugin build file that declares `singleVariant` for only some variants builds, and a variant mismatch between the module and a plugin fails with a Flutter error instead of `Task with name … not found`. **Base:** `master` at `4521d8d6634`. `aar_init_script.gradle`, `build_aar_module_test.dart` and both docs are the same at `4521d8d6634` and `5ba8961b517`, so the comparisons below against `5ba8961b517` hold for `4521d8d6634`. <details> <summary>Agent authored details</summary> ### Changes All in `aar_init_script.gradle` unless noted. Line numbers are at `b9493299916`. 1. **Library check** (`:13`): `project.pluginManager.hasPlugin("com.android.library")` replaces `android.hasProperty("libraryVariants")`. The error text is unchanged. 2. **Publishing declarations** (`afterProject`, `:185-219`): Flutter calls `android.publishing.singleVariant(<variant>) { withSourcesJar(); withJavadocJar() }` for each variant, one variant at a time. If the project's build file already declared that variant, AGP throws, and `isDuplicateSingleVariantError` (`:125-134`) matches AGP's exact message, so the build file's declaration is kept. Its comment links the AGP source of the message ([`AbstractPublishing.kt` at `studio-2025.3.2`, AGP 9.1.0](https://android.googlesource.com/platform/tools/base/+/refs/tags/studio-2025.3.2/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/dsl/AbstractPublishing.kt#38)). Any other exception is rethrown (`:201-203`). The guard that read `publishing.singleVariants` and skipped every Flutter declaration once the build file declared any variant is deleted. 3. **No module-to-plugin `dependsOn`** (`projectsEvaluated`): the loop over `moduleProject.android.libraryVariants` that made each module `assembleAar<Variant>` depend on the plugin's task (with eager `tasks.named(...).get()`) is deleted. The tool requests `assembleAar<Variant>` without a project path (`gradle.dart` `buildGradleAar`, `command.add(aarTask)`), and Gradle runs a task selector that is only a task name in every project that has the task ([Gradle docs](https://docs.gradle.org/current/userguide/command_line_interface.html#executing_tasks_in_multi_project_builds): "You can also run a task for all subprojects using a task selector that consists of only the task name."). Each plugin publishes from its own task. The comment at `:247-248` says this. 4. **Tasks, publications and task name** (`configureProject` `:27-37`; `aarTaskName` `:93-97`; `requestedAarComponents` `:99-103`): AGP adds one software component for each `singleVariant` declaration, with the variant's name. `registerAarTask` (`:105-110`) registers `assembleAar<Variant>` for every component, so a misspelled `--flavor` gets Gradle's candidate list (`Some candidates are: 'assembleAarDebug', ...`). `requestedAarComponents` returns the components whose `assembleAar<Variant>` name is in `startParameter.taskNames`; only those get a publication (`addAarPublication`, `:112-123`), because `publish` publishes every publication in the project. The check uses the same function. `aarTaskName` is the only place that builds `assembleAar<Variant>`; its comment points to the tool's `getAarTaskFor` (`gradle.dart`). Nothing parses a task name back into a variant. The `component.name != "all"` filter from `5ba8961b517` is deleted (see "Why removing the deleted code is safe"). 5. **Lazy task registration** (`registerAarTask`): `project.tasks.register` replaces `project.tasks.create(name:)`. The configuration action only adds `finalizedBy "publish"`. `publications.create` runs in `addAarPublication`, not in the action, because maven-publish registers tasks for each publication and Gradle rejects that while it realizes a task (`DefaultTaskContainer#register(String, Class, Action) on task set cannot be executed in the current context.`, seen when `publications.create` was inside the action). 6. **Requested-variant check** (`checkProjectsHaveRequestedVariants`, `:136-175`, called at `:252`): Gradle skips projects that lack the requested task, so the script checks the module and each Android plugin. The requested variants are the names of the components that `requestedAarComponents` returns for those projects. A project without a requested variant fails the build with a `GradleException` that names the variant and the project: - module: `Flutter can't build the AAR for variant 'debug' because module project ':flutter' has no variant with that name. Its variants are: demoDebug, demoProfile, demoRelease, fullDebug, fullProfile, fullRelease. Pass the module's product flavor with --flavor, or omit --flavor if the module has no product flavors.` - plugin: `Flutter can't build the AAR for variant 'demoDebug' because plugin project ':plugin_a' has no variant with that name, so the plugin AAR that the module depends on can't be published. `flutter build aar` requires the module and each of its Android plugins to declare the same product flavors.` - A requested task that matches no project's variant (for example `--flavor foo`) is left to Gradle: `Task 'assembleAarFooDebug' not found in root project 'android_generated' and its subprojects. Some candidates are: 'assembleAarDebug', 'assembleAarDemoDebug', 'assembleAarFullDebug'.` That is the same error as on `5ba8961b517`. The check's doc says so. (`3edc95d1fa0` parsed the task-name prefix and reported a Flutter module error in this case; `da9646aad53` drops the parsing per review.) 7. **Docs.** Both docs describe behavior only. - `Migrating-Flutter-Gradle-Plugin-to-AGP-public-API.md`: the P8 row of the phase table and "Features that must break" item 8 are rewritten. The "Why" starting-state list (L38-39) is unchanged, as in other PRs of the stack. - [`website-page-draft.md`](https://github.com/flutter/flutter/blob/master/docs/platforms/android/website-page-draft.md) (publishing tracked in [flutter#193713](flutter#193713)): the `flutter build aar` section is rewritten. It said Flutter "detects the overlap and reports it with an actionable error". The code keeps the build file's declaration instead, so the docs were changed to match the code. It says (L234-237) that when a build file declares `singleVariant` for a variant, Flutter keeps that declaration and declares the other variants, and (L238-241) that the module and each Android plugin need a variant with the built name. 8. **Tests.** devicelab `build_aar_module_test` gets two final sections: - With `android.newDsl=true` and `android.builtInKotlin=true` in `.android/gradle.properties`, and `android.publishing.singleVariant("release") { withSourcesJar() }` in `.android/Flutter/build.gradle`, it runs `flutter build aar --no-profile`. It checks that the edits survive, that release has the AAR and sources jar and no javadoc jar, that debug has the javadoc jar, and that the plugin AARs exist for release and debug. - It replaces that declaration with a `demo` product flavor and checks both errors: `flutter build aar --no-profile --no-release` fails with the module error, and the same with `--flavor demo` fails with the plugin error. `da9646aad53`'s commit message says the check's doc "says when it returns and when it throws". That doc sentence was added in `94a29907065`. ### Bug history (moved out of the docs) The docs describe behavior only, so the history that `3edc95d1fa0` put in them is here: - On `5ba8961b517`, the `afterProject` guard skipped all of Flutter's `singleVariant` declarations for a project whose build file declared any variant. For a module with at least one Android plugin that declared only `release`, every `flutter build aar` failed with `Task with name 'assembleAarDebug' not found in project ':flutter'`. With this PR that build succeeds (table 1 below). - On `5ba8961b517`, the `dependsOn` loop failed with `Task with name 'assembleAar<Variant>' not found in project ':<plugin>'` whenever a plugin lacked any module variant, requested or not, and nothing checked that the module had the requested variant. With this PR only requested variants are checked, in the module and in each plugin (table 2 below). ### Audit resolution [PR 9 draft audit](reidbaker-agent#9 (comment)), and stack audit items M4 and L8: | Item | Resolution | | --- | --- | | 1. Eager `.get()` and cross-project task lookup by name | The wiring is deleted, not made lazy: it was redundant (Changes 3). No `.get()` or `tasks.named` in the script. The check reads software components, and `registerAarTask` uses `tasks.register`. | | 2. Broad `catch (Exception)`; overstated `multipleVariants` warning | Only AGP's exact duplicate message is ignored (`:130-134`); everything else is rethrown (`:201-203`). No warning is logged, so there is no `multipleVariants` claim. | | 3. Ext property without `flutter.internal.` | No ext property. `afterProject` fires once per project (instrumented: count 1 for `:`, `:app`, `:flutter`, `:plugin_a` and the included FGP build), and a second call would only hit ignored duplicates. The "fires more than once in composite settings" claim is dropped. | Why the message match and not the type: AGP reports the duplicate from `AbstractPublishing.checkSingleVariantUniqueness` through `SyncIssueReporterImpl`, which throws `com.android.builder.errors.EvalIssueException` in STANDARD mode. That class is in `com.android.tools.build:builder`, not in `gradle-api`, and is not on the init script's classpath. `javap -v` on `AbstractPublishing` shows the same text in AGP 8.11.1, 9.1.0 and 9.3.1. AGP checks before it adds the component (8.11.1 `AbstractPublishing.kt:27-35`), so the build file's declaration stays. If AGP changes the text, the exception is rethrown and the build fails loudly; devicelab `build_aar_module_test` exercises the path on the template AGP. Why not declare first or query: the public `Publishing` DSL in `gradle-api-9.1.0.jar` has only `singleVariant(String)` and `singleVariant(String, Function1)`, and `LibraryPublishing` adds only `multipleVariants(...)` (`javap`), so there is no public way to list declared variants. Declaring Flutter's variants in `beforeProject` + `pluginManager.withPlugin("com.android.library")` makes the build file fail at its own `singleVariant("release")` line with the duplicate error (probe). Why not AGP types in the init script: `import com.android.build.api.dsl.LibraryExtension` fails with `unable to resolve class`. With `gradle-api:9.3.1` on the `initscript` classpath, the init script gets its own copy of the class from a different classloader, so `instanceof` is `false` and `extensions.getByType(LibraryExtension)` throws `Extension of type 'LibraryExtension' does not exist. Currently registered extension types: [..., LibraryExtension, ...]` (probes). `multipleVariants`: `PublishingUtil.ensureComponentNameUniqueness` errors only when a `multipleVariants` component name equals a `singleVariant` name. Flutter declares the same `singleVariant`s on `5ba8961b517` when the build file declares none, so that behavior is unchanged. It is not tested here. ### Why removing the deleted code is safe - `android.libraryVariants` does not exist with `newDsl=true` (reproduced above). AGP 9.1.0 marks its class, `com.android.build.gradle.LibraryExtension`, `@Deprecated` ("This class is not used for the public extensions in AGP when android.newDsl=true, which is the default in AGP 9.0, and will be removed in AGP 10.0."), and the class is not in `gradle-api-9.1.0.jar`. `singleVariants` is an internal property (`getSingleVariants()` on `com.android.build.gradle.internal.dsl.AbstractPublishing`, `javap`). It resolves from Groovy with `newDsl=true`, so it was not a failure, only internal API. - The `dependsOn`: with it removed, the task graph for `assembleAarRelease` on case (a) has `:plugin_a:assembleAarRelease`, `:plugin_a:bundleReleaseAar`, `:plugin_a:publishReleasePublicationToMavenRepository` and `:plugin_a:publish`. `:flutter:generatePomFileForReleasePublication` depends on no `:plugin_a` task, and `:flutter:publishReleasePublicationToMavenRepository` depends only on the plugin's compile-classpath tasks, so the order between the module's and the plugin's publishing doesn't matter. The module POM refers to the plugin by coordinates (`plugin_a_release`). devicelab `build_aar_module_test`, `build_android_host_app_with_module_aar` and `android_java17_build_android_host_app_with_module_aar` pass (they check the plugin AAR/POM and that the module POM depends on `plugin_with_android_release`). - The `all` filter (`component.name != "all"`, added in flutter#101891 `606e91ad63d`): AGP 7.x created an automatic multi-variant component named `all` when a project used no publishing DSL ([`PublishingUtil.kt` at `studio-2021.3.1`](https://android.googlesource.com/platform/tools/base/+/refs/tags/studio-2021.3.1/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/utils/PublishingUtil.kt), `createPublishingInfoForLibrary` and `publishingFeatureOptIn`; same in the AGP 7.3.1 jar). flutter#101891 published it separately with classifiers and skipped it for the `_<variant>` publications; flutter#112149 (`8e30cc9c56b`) removed that publication and kept the skip. AGP 8.0 disabled automatic components by default ([AGP 8.0.0 release notes](https://developer.android.com/build/releases/past-releases/agp-8-0-0-release-notes)), and the AGP 8.11.1, 9.1.1 and 9.3.1 jars list `android.disableAutomaticComponentCreation` as removed, with no `all` component code. This script declares `singleVariant` for every variant, which also turns automatic components off on AGP 7.x. A probe that prints `project.components` after `projectsEvaluated` on all four matrix configurations shows only per-variant names (`[debug, profile, release]` for `:flutter`, `:app` and each plugin, `[demoDebug, …, fullRelease]` for the flavored module, `[freeDebug, freeProfile, freeRelease]` for `plugin_flavored`, `[debug, release]` for the root), never `all`. - `tasks.register`: the publication is created at configuration time only for a requested task name, as with `tasks.create`, whose configuration closure ran immediately. Only the task object is created lazily. Table rows below and the devicelab runs show the same artifacts. ### Removed comments, assertions and code | Removed | Reason | | --- | --- | | `afterProject` comment item 2, "The project has already defined which variants to publish…", and the `singleVariants.size()` guard it described | The guard read internal API and skipped all of Flutter's declarations once a build file declared any. Item 1 of that comment became "Exit early if the project doesn't have the Android Gradle plugin applied." | | The `projectsEvaluated` comment "Configure the `assembleAar<variantName>` task for each plugin's projects and make the module's equivalent task depend on the plugin's task.", with the `libraryVariants.all` loop, both `tasks.named(...).get()` calls, both `assert(... != null)` and the `dependsOn` | The dependency is redundant (see above). `tasks.named(...).get()` throws before the asserts could fail, so they never checked anything. | | `addAarTask` and its `variantName` local | Split into `registerAarTask` (every component) and `addAarPublication` (requested components). The task name comes from `aarTaskName`, which the check also uses. | | `addAarTask` comment "This check is required to be able to configure the archives before `publish` runs.", with the check | The check is `requestedAarComponents`, applied by the caller before `addAarPublication`. Its reason is the comment on `addAarPublication`: "`publish` publishes every publication in the project, so only requested components get one." | | The `component.name != "all"` filter in `configureProject`, and the fragment "(except "all" since that is used to publish artifacts in the new way)" from "Create a default MavenPublication for the variant" | Nothing creates an `all` component where this script runs (see "Why removing the deleted code is safe"). | | From the migration doc item 8 and the website page draft: the sentences about the deleted guard, the `Task with name … not found` failures, and what the deleted code did and did not check | The docs describe behavior only. The history is in "Bug history" above. | ### Behavioral and compatibility notes Matrix projects (local scratch): (a) module + Android plugin `plugin_a`; (b) module with flavors `demo`/`full` + `plugin_a`; (c) module + `plugin_a` + `plugin_flavored` (flavor `free`); (d) module declaring `singleVariant("release") { withSourcesJar() }` + `plugin_a`. 1. **A build file that declares `singleVariant` for some variants builds.** Case (d), `flutter build aar`: | Script | Result | | --- | --- | | `5ba8961b517` (AGP 9.3.1 `newDsl=false`) | exit 1: `Task with name 'assembleAarDebug' not found in project ':flutter'.` | | This PR (all four configurations below) | exit 0. Release has the AAR, POM and sources jar, and no javadoc jar (the build file's declaration). Debug and profile have sources and javadoc jars. Plugin AAR/POM for all three. | 2. **Variant mismatches fail with a Flutter error that names the project.** Only requested variants are checked. The `5ba8961b517` column is AGP 9.3.1 with `newDsl=false`. | Case | `5ba8961b517` | This PR (all four configurations) | | --- | --- | --- | | (b) `--flavor demo` | `Task with name 'assembleAarDemoDebug' not found in project ':plugin_a'.` | plugin error for `demoDebug` and `:plugin_a` | | (c) default | `Task with name 'assembleAarDebug' not found in project ':plugin_flavored'.` | plugin error for `debug` and `:plugin_flavored` | | (b) no `--flavor`, `--no-profile --no-release` | `Task with name 'assembleAarDemoDebug' not found in project ':plugin_a'.` | module error for `debug`, listing the module's variants | | (c) `--flavor free`, `--no-profile --no-release` | `Task with name 'assembleAarDebug' not found in project ':plugin_flavored'.` | module error for `freeDebug` | | (b) `--flavor foo` | Gradle `Task 'assembleAarFooDebug' not found in root project …` | same Gradle error | Without the module check (`86523c549fc`), the module-error cases exit 0 with "✓ Built" and a repo that has only the plugin AAR. 3. **Path-qualified requests.** `./gradlew -I aar_init_script.gradle … :flutter:assembleAarRelease` exits 0 and publishes nothing, on `5ba8961b517` and on this PR: a publication is created only when `startParameter.taskNames` contains the bare name. The tool sends only the bare name (`gradle.dart` `command.add(aarTask)`). Not changed here. 4. **No warnings.** `flutter build aar` hides Gradle output on success unless `-v` (`ProcessUtils.run` buffers it; `gradle.dart` prints it only on failure, and verified with a `logger.error` probe). Keeping a build file's declaration needs no action, so nothing is logged. Errors are shown. 5. **Configuration cache:** with `org.gradle.configuration-cache=true` in case (a)'s `.android/gradle.properties` (AGP 9.3.1 / Gradle 9.5.0), `flutter build aar` reports the same single problem with the `5ba8961b517` script and with this PR: ``Task `:flutter:compileFlutterBuildDebug` of type `com.flutter.gradle.tasks.FlutterTask`: invocation of 'Task.project' at execution time is unsupported with the configuration cache.`` None comes from the init script. 6. `newDsl=true` with `builtInKotlin=false` fails before the script with any Kotlin plugin (`The 'org.jetbrains.kotlin.android' plugin is not compatible with AGP's 9.0 new DSL`). The devicelab section and matrix set `builtInKotlin=true`. 7. Related open work: no overlap with [flutter#191681](flutter#191681) (`gradle.dart`, `gradle_errors.dart` and tests). No open PR touches `aar_init_script.gradle`. ### Follow-up candidates (pre-existing, not changed here) - `allprojects { apply plugin: "maven-publish" }` applies maven-publish to every project, including ones that never publish. - The `afterProject` and `projectsEvaluated` hooks reach across projects, which Gradle Isolated Projects does not allow. - The `-SNAPSHOT` rewrite of `project.version` in `configureProject`. - The `engine.realm` and `engine.stamp` file reads at configuration time in `configureProject`. - The `is-plugin` branch of `projectsEvaluated` looks unreachable from the tool. - The configuration cache problem in FGP `FlutterTask` (note 5). - `newDsl=true` with KGP (`builtInKotlin=false`), for PR 10. - Mapping between flavored module variants and plugin variants (for example, plugin `release` for module `demoRelease`). - `singleVariant` is declared on the root project and `:app`, which never publish. - Gradle output is hidden on success, so nothing the script logs at info or warn level reaches the user without `-v`. ### Integration test coverage of the changed paths Searched `dev/devicelab/bin/tasks` and `packages/flutter_tools/test/integration.shard` for `build aar` and `assembleAar`. | Path | Tests | | --- | --- | | Library check, publishing declarations, lazy task registration, plugin publishing without `dependsOn` (`newDsl=false`) | devicelab `build_aar_module_test`, `build_android_host_app_with_module_aar`, `android_java17_build_android_host_app_with_module_aar`; `android_obfuscate_test` | | `newDsl=true`, a build file's `singleVariant` declaration kept | devicelab `build_aar_module_test`, `newDsl=true` section | | Module and plugin variant mismatch errors | devicelab `build_aar_module_test`, product flavor section | | AGP 9.1.1 and 8.11.1; a variant that no project has | Manual matrix only | `Linux build_aar_module_test` runs in presubmit for this PR: in `.ci.yaml` its `runIf` includes `dev/**` and `packages/flutter_tools/**`, and it sets neither `presubmit: false` nor `bringup`. It also ran locally on the head (below) with `cd dev/devicelab && ../../bin/dart bin/test_runner.dart test -t build_aar_module_test --exit`. ### Tests run locally (macOS, JDK 17) On the head (`b9493299916`; master merged at `4521d8d6634`): - `gradle test --rerun-tasks` in `packages/flutter_tools/gradle`, with the engine's `third_party/gradle` binary that the README names: 33 suites, 277 tests, 0 failures. - `dart format` and `dart analyze --fatal-infos` on `build_aar_module_test.dart`: clean (unchanged since `3edc95d1fa0`). - Tool tests: `test/commands.shard/hermetic/build_aar_test.dart`, `test/commands.shard/permeable/build_aar_test.dart` and `test/general.shard/android/android_gradle_builder_test.dart`: 56 tests, all pass. - `test/integration.shard/android_obfuscate_test.dart`: 2 tests pass. - devicelab via `bin/test_runner.dart test --exit`, one at a time: `build_aar_module_test` (including both added sections), `build_android_host_app_with_module_aar`, `android_java17_build_android_host_app_with_module_aar`, `build_android_host_app_with_module_source`, `module_host_with_custom_build_test`, all `success`. - The configuration cache comparison (note 5). Manual matrix on `b9493299916`, four configurations: - **9.3.1 off:** AGP 9.3.1 / Gradle 9.5.0, `newDsl=false`, `builtInKotlin=false`. - **9.3.1 on:** AGP 9.3.1 / Gradle 9.5.0, `newDsl=true`, `builtInKotlin=true`. - **9.1.1 on:** AGP 9.1.1 / Gradle 9.3.1 (FGP's minimums on master since flutter#193813), `newDsl=true`, `builtInKotlin=true`. - **8.11.1:** AGP 8.11.1 / Gradle 8.14.3, `newDsl=false`, with `skipDependencyChecks=true` in `.android/gradle.properties`. Without it, FGP stops before the init script runs: `Your project's Gradle version (8.14.3) is lower than Flutter's minimum supported version of 9.3.1.` | Case | 9.3.1 off | 9.3.1 on | 9.1.1 on | 8.11.1 | | --- | --- | --- | --- | --- | | (a) | exit 0, AAR, POM, sources and javadoc jars for `flutter_{debug,profile,release}` and `plugin_a_{debug,profile,release}` | same | same | same | | (b) `--flavor demo` | exit 1, plugin error | same | same | same | | (b) no `--flavor` | exit 1, module error | same | same | same | | (b) `--flavor foo` | exit 1, Gradle `Task 'assembleAarFooDebug' not found in root project` | same | same | same | | (c) | exit 1, plugin error | same | same | same | | (c) `--flavor free` | exit 1, module error | same | same | same | | (d) | exit 0, as (a), except module release has the sources jar and no javadoc jar | same | same | same | Case (a)'s `flutter_release-1.0.pom` depends on `dev.pr9.plugin_a:plugin_a_release:1.0` (checked on `b9493299916`, AGP 9.1.1). Negative control, the master script (same at `5ba8961b517` and `4521d8d6634`), on `b9493299916`: with `newDsl=true` (AGP 9.3.1 and AGP 9.1.1), (a)-(d) all fail with `Can't generate AAR on a non Android library project.`; with `newDsl=false` on AGP 9.3.1, (a) passes and (b)-(d) fail with Gradle's `Task with name 'assembleAar<V>' not found in project ':<p>'.` On `a17fafcc35e`, with only line 13 changed to `hasPlugin`, `newDsl=true` fails at line 178 with `Could not get unknown property 'libraryVariants' for object of type com.android.build.gradle.internal.dsl.LibraryExtensionImpl$AgpDecorated.` devicelab `build_aar_module_test` with that script fails at the `newDsl=true` section with the same error. </details> --- ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant in-code documentation (doc comments with `///`). - [x] If this PR introduces a new feature or capability, I created and linked a website documentation issue or PR in [flutter/website] (or verified none is needed). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [AI contribution guidelines]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [flutter/website]: https://github.com/flutter/website [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
suryaKommana2662
pushed a commit
to suryaKommana2662/flutter
that referenced
this pull request
Oct 29, 2026
…Is (flutter#193962) PR 7 (flutter#193693) merged to master as `ecd1ab06865` and PR 8 (flutter#193717) as `4249ccbc18a`. This branch merges master at `4521d8d6634`, so the Files tab shows only PR 9's changes. PR 9 commits: - `86523c549fc` Move aar_init_script to public AGP APIs - `3edc95d1fa0` Check requested AAR variants in the module and each plugin - `da9646aad53` Address review: derive requested variants from components, register AAR tasks lazily - `94a29907065` Clarify aar_init_script comments from the adversarial review - `ea08280d886` Merge commit '23ddd6b74e6' into agp-aar-init-script (PR 8's head with master at `ecd1ab06865`; no conflicts, no change to PR 9's files) - `a17fafcc35e` Merge commit '5ba8961b517' into agp-aar-init-script (master with PR 8 at `4249ccbc18a`; no conflicts, no change to PR 9's files) - `77ba449ea4a` Filter requested AAR components before adding tasks - `adbfb9cd640` Drop the AGP version list from the duplicate-variant comment - `f2b7111c51e` Docs: state the AAR variant requirement in plain words - `cd10f766b5b` Register an AAR task for every variant, publish only requested ones - `b9493299916` Merge commit '4521d8d6634' into agp-aar-init-script (master, including flutter#193649; no conflicts, no change to PR 9's files) ## Description This is PR 9 of 11 in the AGP 9.1.0 / public `gradle-api` migration stack ([flutter#180137](flutter#180137), [flutter#166550](flutter#166550)). `flutter build aar` passes `packages/flutter_tools/gradle/aar_init_script.gradle` to Gradle with `-I`. The script read `android.libraryVariants` and AGP's internal `publishing.singleVariants`. With AGP 9's `android.newDsl=true`, `flutter build aar` on a module fails on `5ba8961b517` with `Can't generate AAR on a non Android library project.` (script line 13), and then at line 178 (`Could not get unknown property 'libraryVariants'`). With this PR the script uses only `pluginManager.hasPlugin`, `Publishing.singleVariant`, software `components` and `tasks.register`, and the build succeeds with `newDsl` on and off, on AGP 9.3.1 and on master's minimum AGP 9.1.1, and on AGP 8.11.1 when FGP's version check is skipped. **Breaking:** no. Builds that succeed on `5ba8961b517` succeed with the same artifacts. Two failing cases change: a module or plugin build file that declares `singleVariant` for only some variants builds, and a variant mismatch between the module and a plugin fails with a Flutter error instead of `Task with name … not found`. **Base:** `master` at `4521d8d6634`. `aar_init_script.gradle`, `build_aar_module_test.dart` and both docs are the same at `4521d8d6634` and `5ba8961b517`, so the comparisons below against `5ba8961b517` hold for `4521d8d6634`. <details> <summary>Agent authored details</summary> ### Changes All in `aar_init_script.gradle` unless noted. Line numbers are at `b9493299916`. 1. **Library check** (`:13`): `project.pluginManager.hasPlugin("com.android.library")` replaces `android.hasProperty("libraryVariants")`. The error text is unchanged. 2. **Publishing declarations** (`afterProject`, `:185-219`): Flutter calls `android.publishing.singleVariant(<variant>) { withSourcesJar(); withJavadocJar() }` for each variant, one variant at a time. If the project's build file already declared that variant, AGP throws, and `isDuplicateSingleVariantError` (`:125-134`) matches AGP's exact message, so the build file's declaration is kept. Its comment links the AGP source of the message ([`AbstractPublishing.kt` at `studio-2025.3.2`, AGP 9.1.0](https://android.googlesource.com/platform/tools/base/+/refs/tags/studio-2025.3.2/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/dsl/AbstractPublishing.kt#38)). Any other exception is rethrown (`:201-203`). The guard that read `publishing.singleVariants` and skipped every Flutter declaration once the build file declared any variant is deleted. 3. **No module-to-plugin `dependsOn`** (`projectsEvaluated`): the loop over `moduleProject.android.libraryVariants` that made each module `assembleAar<Variant>` depend on the plugin's task (with eager `tasks.named(...).get()`) is deleted. The tool requests `assembleAar<Variant>` without a project path (`gradle.dart` `buildGradleAar`, `command.add(aarTask)`), and Gradle runs a task selector that is only a task name in every project that has the task ([Gradle docs](https://docs.gradle.org/current/userguide/command_line_interface.html#executing_tasks_in_multi_project_builds): "You can also run a task for all subprojects using a task selector that consists of only the task name."). Each plugin publishes from its own task. The comment at `:247-248` says this. 4. **Tasks, publications and task name** (`configureProject` `:27-37`; `aarTaskName` `:93-97`; `requestedAarComponents` `:99-103`): AGP adds one software component for each `singleVariant` declaration, with the variant's name. `registerAarTask` (`:105-110`) registers `assembleAar<Variant>` for every component, so a misspelled `--flavor` gets Gradle's candidate list (`Some candidates are: 'assembleAarDebug', ...`). `requestedAarComponents` returns the components whose `assembleAar<Variant>` name is in `startParameter.taskNames`; only those get a publication (`addAarPublication`, `:112-123`), because `publish` publishes every publication in the project. The check uses the same function. `aarTaskName` is the only place that builds `assembleAar<Variant>`; its comment points to the tool's `getAarTaskFor` (`gradle.dart`). Nothing parses a task name back into a variant. The `component.name != "all"` filter from `5ba8961b517` is deleted (see "Why removing the deleted code is safe"). 5. **Lazy task registration** (`registerAarTask`): `project.tasks.register` replaces `project.tasks.create(name:)`. The configuration action only adds `finalizedBy "publish"`. `publications.create` runs in `addAarPublication`, not in the action, because maven-publish registers tasks for each publication and Gradle rejects that while it realizes a task (`DefaultTaskContainer#register(String, Class, Action) on task set cannot be executed in the current context.`, seen when `publications.create` was inside the action). 6. **Requested-variant check** (`checkProjectsHaveRequestedVariants`, `:136-175`, called at `:252`): Gradle skips projects that lack the requested task, so the script checks the module and each Android plugin. The requested variants are the names of the components that `requestedAarComponents` returns for those projects. A project without a requested variant fails the build with a `GradleException` that names the variant and the project: - module: `Flutter can't build the AAR for variant 'debug' because module project ':flutter' has no variant with that name. Its variants are: demoDebug, demoProfile, demoRelease, fullDebug, fullProfile, fullRelease. Pass the module's product flavor with --flavor, or omit --flavor if the module has no product flavors.` - plugin: `Flutter can't build the AAR for variant 'demoDebug' because plugin project ':plugin_a' has no variant with that name, so the plugin AAR that the module depends on can't be published. `flutter build aar` requires the module and each of its Android plugins to declare the same product flavors.` - A requested task that matches no project's variant (for example `--flavor foo`) is left to Gradle: `Task 'assembleAarFooDebug' not found in root project 'android_generated' and its subprojects. Some candidates are: 'assembleAarDebug', 'assembleAarDemoDebug', 'assembleAarFullDebug'.` That is the same error as on `5ba8961b517`. The check's doc says so. (`3edc95d1fa0` parsed the task-name prefix and reported a Flutter module error in this case; `da9646aad53` drops the parsing per review.) 7. **Docs.** Both docs describe behavior only. - `Migrating-Flutter-Gradle-Plugin-to-AGP-public-API.md`: the P8 row of the phase table and "Features that must break" item 8 are rewritten. The "Why" starting-state list (L38-39) is unchanged, as in other PRs of the stack. - [`website-page-draft.md`](https://github.com/flutter/flutter/blob/master/docs/platforms/android/website-page-draft.md) (publishing tracked in [flutter#193713](flutter#193713)): the `flutter build aar` section is rewritten. It said Flutter "detects the overlap and reports it with an actionable error". The code keeps the build file's declaration instead, so the docs were changed to match the code. It says (L234-237) that when a build file declares `singleVariant` for a variant, Flutter keeps that declaration and declares the other variants, and (L238-241) that the module and each Android plugin need a variant with the built name. 8. **Tests.** devicelab `build_aar_module_test` gets two final sections: - With `android.newDsl=true` and `android.builtInKotlin=true` in `.android/gradle.properties`, and `android.publishing.singleVariant("release") { withSourcesJar() }` in `.android/Flutter/build.gradle`, it runs `flutter build aar --no-profile`. It checks that the edits survive, that release has the AAR and sources jar and no javadoc jar, that debug has the javadoc jar, and that the plugin AARs exist for release and debug. - It replaces that declaration with a `demo` product flavor and checks both errors: `flutter build aar --no-profile --no-release` fails with the module error, and the same with `--flavor demo` fails with the plugin error. `da9646aad53`'s commit message says the check's doc "says when it returns and when it throws". That doc sentence was added in `94a29907065`. ### Bug history (moved out of the docs) The docs describe behavior only, so the history that `3edc95d1fa0` put in them is here: - On `5ba8961b517`, the `afterProject` guard skipped all of Flutter's `singleVariant` declarations for a project whose build file declared any variant. For a module with at least one Android plugin that declared only `release`, every `flutter build aar` failed with `Task with name 'assembleAarDebug' not found in project ':flutter'`. With this PR that build succeeds (table 1 below). - On `5ba8961b517`, the `dependsOn` loop failed with `Task with name 'assembleAar<Variant>' not found in project ':<plugin>'` whenever a plugin lacked any module variant, requested or not, and nothing checked that the module had the requested variant. With this PR only requested variants are checked, in the module and in each plugin (table 2 below). ### Audit resolution [PR 9 draft audit](reidbaker-agent#9 (comment)), and stack audit items M4 and L8: | Item | Resolution | | --- | --- | | 1. Eager `.get()` and cross-project task lookup by name | The wiring is deleted, not made lazy: it was redundant (Changes 3). No `.get()` or `tasks.named` in the script. The check reads software components, and `registerAarTask` uses `tasks.register`. | | 2. Broad `catch (Exception)`; overstated `multipleVariants` warning | Only AGP's exact duplicate message is ignored (`:130-134`); everything else is rethrown (`:201-203`). No warning is logged, so there is no `multipleVariants` claim. | | 3. Ext property without `flutter.internal.` | No ext property. `afterProject` fires once per project (instrumented: count 1 for `:`, `:app`, `:flutter`, `:plugin_a` and the included FGP build), and a second call would only hit ignored duplicates. The "fires more than once in composite settings" claim is dropped. | Why the message match and not the type: AGP reports the duplicate from `AbstractPublishing.checkSingleVariantUniqueness` through `SyncIssueReporterImpl`, which throws `com.android.builder.errors.EvalIssueException` in STANDARD mode. That class is in `com.android.tools.build:builder`, not in `gradle-api`, and is not on the init script's classpath. `javap -v` on `AbstractPublishing` shows the same text in AGP 8.11.1, 9.1.0 and 9.3.1. AGP checks before it adds the component (8.11.1 `AbstractPublishing.kt:27-35`), so the build file's declaration stays. If AGP changes the text, the exception is rethrown and the build fails loudly; devicelab `build_aar_module_test` exercises the path on the template AGP. Why not declare first or query: the public `Publishing` DSL in `gradle-api-9.1.0.jar` has only `singleVariant(String)` and `singleVariant(String, Function1)`, and `LibraryPublishing` adds only `multipleVariants(...)` (`javap`), so there is no public way to list declared variants. Declaring Flutter's variants in `beforeProject` + `pluginManager.withPlugin("com.android.library")` makes the build file fail at its own `singleVariant("release")` line with the duplicate error (probe). Why not AGP types in the init script: `import com.android.build.api.dsl.LibraryExtension` fails with `unable to resolve class`. With `gradle-api:9.3.1` on the `initscript` classpath, the init script gets its own copy of the class from a different classloader, so `instanceof` is `false` and `extensions.getByType(LibraryExtension)` throws `Extension of type 'LibraryExtension' does not exist. Currently registered extension types: [..., LibraryExtension, ...]` (probes). `multipleVariants`: `PublishingUtil.ensureComponentNameUniqueness` errors only when a `multipleVariants` component name equals a `singleVariant` name. Flutter declares the same `singleVariant`s on `5ba8961b517` when the build file declares none, so that behavior is unchanged. It is not tested here. ### Why removing the deleted code is safe - `android.libraryVariants` does not exist with `newDsl=true` (reproduced above). AGP 9.1.0 marks its class, `com.android.build.gradle.LibraryExtension`, `@Deprecated` ("This class is not used for the public extensions in AGP when android.newDsl=true, which is the default in AGP 9.0, and will be removed in AGP 10.0."), and the class is not in `gradle-api-9.1.0.jar`. `singleVariants` is an internal property (`getSingleVariants()` on `com.android.build.gradle.internal.dsl.AbstractPublishing`, `javap`). It resolves from Groovy with `newDsl=true`, so it was not a failure, only internal API. - The `dependsOn`: with it removed, the task graph for `assembleAarRelease` on case (a) has `:plugin_a:assembleAarRelease`, `:plugin_a:bundleReleaseAar`, `:plugin_a:publishReleasePublicationToMavenRepository` and `:plugin_a:publish`. `:flutter:generatePomFileForReleasePublication` depends on no `:plugin_a` task, and `:flutter:publishReleasePublicationToMavenRepository` depends only on the plugin's compile-classpath tasks, so the order between the module's and the plugin's publishing doesn't matter. The module POM refers to the plugin by coordinates (`plugin_a_release`). devicelab `build_aar_module_test`, `build_android_host_app_with_module_aar` and `android_java17_build_android_host_app_with_module_aar` pass (they check the plugin AAR/POM and that the module POM depends on `plugin_with_android_release`). - The `all` filter (`component.name != "all"`, added in flutter#101891 `606e91ad63d`): AGP 7.x created an automatic multi-variant component named `all` when a project used no publishing DSL ([`PublishingUtil.kt` at `studio-2021.3.1`](https://android.googlesource.com/platform/tools/base/+/refs/tags/studio-2021.3.1/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/utils/PublishingUtil.kt), `createPublishingInfoForLibrary` and `publishingFeatureOptIn`; same in the AGP 7.3.1 jar). flutter#101891 published it separately with classifiers and skipped it for the `_<variant>` publications; flutter#112149 (`8e30cc9c56b`) removed that publication and kept the skip. AGP 8.0 disabled automatic components by default ([AGP 8.0.0 release notes](https://developer.android.com/build/releases/past-releases/agp-8-0-0-release-notes)), and the AGP 8.11.1, 9.1.1 and 9.3.1 jars list `android.disableAutomaticComponentCreation` as removed, with no `all` component code. This script declares `singleVariant` for every variant, which also turns automatic components off on AGP 7.x. A probe that prints `project.components` after `projectsEvaluated` on all four matrix configurations shows only per-variant names (`[debug, profile, release]` for `:flutter`, `:app` and each plugin, `[demoDebug, …, fullRelease]` for the flavored module, `[freeDebug, freeProfile, freeRelease]` for `plugin_flavored`, `[debug, release]` for the root), never `all`. - `tasks.register`: the publication is created at configuration time only for a requested task name, as with `tasks.create`, whose configuration closure ran immediately. Only the task object is created lazily. Table rows below and the devicelab runs show the same artifacts. ### Removed comments, assertions and code | Removed | Reason | | --- | --- | | `afterProject` comment item 2, "The project has already defined which variants to publish…", and the `singleVariants.size()` guard it described | The guard read internal API and skipped all of Flutter's declarations once a build file declared any. Item 1 of that comment became "Exit early if the project doesn't have the Android Gradle plugin applied." | | The `projectsEvaluated` comment "Configure the `assembleAar<variantName>` task for each plugin's projects and make the module's equivalent task depend on the plugin's task.", with the `libraryVariants.all` loop, both `tasks.named(...).get()` calls, both `assert(... != null)` and the `dependsOn` | The dependency is redundant (see above). `tasks.named(...).get()` throws before the asserts could fail, so they never checked anything. | | `addAarTask` and its `variantName` local | Split into `registerAarTask` (every component) and `addAarPublication` (requested components). The task name comes from `aarTaskName`, which the check also uses. | | `addAarTask` comment "This check is required to be able to configure the archives before `publish` runs.", with the check | The check is `requestedAarComponents`, applied by the caller before `addAarPublication`. Its reason is the comment on `addAarPublication`: "`publish` publishes every publication in the project, so only requested components get one." | | The `component.name != "all"` filter in `configureProject`, and the fragment "(except "all" since that is used to publish artifacts in the new way)" from "Create a default MavenPublication for the variant" | Nothing creates an `all` component where this script runs (see "Why removing the deleted code is safe"). | | From the migration doc item 8 and the website page draft: the sentences about the deleted guard, the `Task with name … not found` failures, and what the deleted code did and did not check | The docs describe behavior only. The history is in "Bug history" above. | ### Behavioral and compatibility notes Matrix projects (local scratch): (a) module + Android plugin `plugin_a`; (b) module with flavors `demo`/`full` + `plugin_a`; (c) module + `plugin_a` + `plugin_flavored` (flavor `free`); (d) module declaring `singleVariant("release") { withSourcesJar() }` + `plugin_a`. 1. **A build file that declares `singleVariant` for some variants builds.** Case (d), `flutter build aar`: | Script | Result | | --- | --- | | `5ba8961b517` (AGP 9.3.1 `newDsl=false`) | exit 1: `Task with name 'assembleAarDebug' not found in project ':flutter'.` | | This PR (all four configurations below) | exit 0. Release has the AAR, POM and sources jar, and no javadoc jar (the build file's declaration). Debug and profile have sources and javadoc jars. Plugin AAR/POM for all three. | 2. **Variant mismatches fail with a Flutter error that names the project.** Only requested variants are checked. The `5ba8961b517` column is AGP 9.3.1 with `newDsl=false`. | Case | `5ba8961b517` | This PR (all four configurations) | | --- | --- | --- | | (b) `--flavor demo` | `Task with name 'assembleAarDemoDebug' not found in project ':plugin_a'.` | plugin error for `demoDebug` and `:plugin_a` | | (c) default | `Task with name 'assembleAarDebug' not found in project ':plugin_flavored'.` | plugin error for `debug` and `:plugin_flavored` | | (b) no `--flavor`, `--no-profile --no-release` | `Task with name 'assembleAarDemoDebug' not found in project ':plugin_a'.` | module error for `debug`, listing the module's variants | | (c) `--flavor free`, `--no-profile --no-release` | `Task with name 'assembleAarDebug' not found in project ':plugin_flavored'.` | module error for `freeDebug` | | (b) `--flavor foo` | Gradle `Task 'assembleAarFooDebug' not found in root project …` | same Gradle error | Without the module check (`86523c549fc`), the module-error cases exit 0 with "✓ Built" and a repo that has only the plugin AAR. 3. **Path-qualified requests.** `./gradlew -I aar_init_script.gradle … :flutter:assembleAarRelease` exits 0 and publishes nothing, on `5ba8961b517` and on this PR: a publication is created only when `startParameter.taskNames` contains the bare name. The tool sends only the bare name (`gradle.dart` `command.add(aarTask)`). Not changed here. 4. **No warnings.** `flutter build aar` hides Gradle output on success unless `-v` (`ProcessUtils.run` buffers it; `gradle.dart` prints it only on failure, and verified with a `logger.error` probe). Keeping a build file's declaration needs no action, so nothing is logged. Errors are shown. 5. **Configuration cache:** with `org.gradle.configuration-cache=true` in case (a)'s `.android/gradle.properties` (AGP 9.3.1 / Gradle 9.5.0), `flutter build aar` reports the same single problem with the `5ba8961b517` script and with this PR: ``Task `:flutter:compileFlutterBuildDebug` of type `com.flutter.gradle.tasks.FlutterTask`: invocation of 'Task.project' at execution time is unsupported with the configuration cache.`` None comes from the init script. 6. `newDsl=true` with `builtInKotlin=false` fails before the script with any Kotlin plugin (`The 'org.jetbrains.kotlin.android' plugin is not compatible with AGP's 9.0 new DSL`). The devicelab section and matrix set `builtInKotlin=true`. 7. Related open work: no overlap with [flutter#191681](flutter#191681) (`gradle.dart`, `gradle_errors.dart` and tests). No open PR touches `aar_init_script.gradle`. ### Follow-up candidates (pre-existing, not changed here) - `allprojects { apply plugin: "maven-publish" }` applies maven-publish to every project, including ones that never publish. - The `afterProject` and `projectsEvaluated` hooks reach across projects, which Gradle Isolated Projects does not allow. - The `-SNAPSHOT` rewrite of `project.version` in `configureProject`. - The `engine.realm` and `engine.stamp` file reads at configuration time in `configureProject`. - The `is-plugin` branch of `projectsEvaluated` looks unreachable from the tool. - The configuration cache problem in FGP `FlutterTask` (note 5). - `newDsl=true` with KGP (`builtInKotlin=false`), for PR 10. - Mapping between flavored module variants and plugin variants (for example, plugin `release` for module `demoRelease`). - `singleVariant` is declared on the root project and `:app`, which never publish. - Gradle output is hidden on success, so nothing the script logs at info or warn level reaches the user without `-v`. ### Integration test coverage of the changed paths Searched `dev/devicelab/bin/tasks` and `packages/flutter_tools/test/integration.shard` for `build aar` and `assembleAar`. | Path | Tests | | --- | --- | | Library check, publishing declarations, lazy task registration, plugin publishing without `dependsOn` (`newDsl=false`) | devicelab `build_aar_module_test`, `build_android_host_app_with_module_aar`, `android_java17_build_android_host_app_with_module_aar`; `android_obfuscate_test` | | `newDsl=true`, a build file's `singleVariant` declaration kept | devicelab `build_aar_module_test`, `newDsl=true` section | | Module and plugin variant mismatch errors | devicelab `build_aar_module_test`, product flavor section | | AGP 9.1.1 and 8.11.1; a variant that no project has | Manual matrix only | `Linux build_aar_module_test` runs in presubmit for this PR: in `.ci.yaml` its `runIf` includes `dev/**` and `packages/flutter_tools/**`, and it sets neither `presubmit: false` nor `bringup`. It also ran locally on the head (below) with `cd dev/devicelab && ../../bin/dart bin/test_runner.dart test -t build_aar_module_test --exit`. ### Tests run locally (macOS, JDK 17) On the head (`b9493299916`; master merged at `4521d8d6634`): - `gradle test --rerun-tasks` in `packages/flutter_tools/gradle`, with the engine's `third_party/gradle` binary that the README names: 33 suites, 277 tests, 0 failures. - `dart format` and `dart analyze --fatal-infos` on `build_aar_module_test.dart`: clean (unchanged since `3edc95d1fa0`). - Tool tests: `test/commands.shard/hermetic/build_aar_test.dart`, `test/commands.shard/permeable/build_aar_test.dart` and `test/general.shard/android/android_gradle_builder_test.dart`: 56 tests, all pass. - `test/integration.shard/android_obfuscate_test.dart`: 2 tests pass. - devicelab via `bin/test_runner.dart test --exit`, one at a time: `build_aar_module_test` (including both added sections), `build_android_host_app_with_module_aar`, `android_java17_build_android_host_app_with_module_aar`, `build_android_host_app_with_module_source`, `module_host_with_custom_build_test`, all `success`. - The configuration cache comparison (note 5). Manual matrix on `b9493299916`, four configurations: - **9.3.1 off:** AGP 9.3.1 / Gradle 9.5.0, `newDsl=false`, `builtInKotlin=false`. - **9.3.1 on:** AGP 9.3.1 / Gradle 9.5.0, `newDsl=true`, `builtInKotlin=true`. - **9.1.1 on:** AGP 9.1.1 / Gradle 9.3.1 (FGP's minimums on master since flutter#193813), `newDsl=true`, `builtInKotlin=true`. - **8.11.1:** AGP 8.11.1 / Gradle 8.14.3, `newDsl=false`, with `skipDependencyChecks=true` in `.android/gradle.properties`. Without it, FGP stops before the init script runs: `Your project's Gradle version (8.14.3) is lower than Flutter's minimum supported version of 9.3.1.` | Case | 9.3.1 off | 9.3.1 on | 9.1.1 on | 8.11.1 | | --- | --- | --- | --- | --- | | (a) | exit 0, AAR, POM, sources and javadoc jars for `flutter_{debug,profile,release}` and `plugin_a_{debug,profile,release}` | same | same | same | | (b) `--flavor demo` | exit 1, plugin error | same | same | same | | (b) no `--flavor` | exit 1, module error | same | same | same | | (b) `--flavor foo` | exit 1, Gradle `Task 'assembleAarFooDebug' not found in root project` | same | same | same | | (c) | exit 1, plugin error | same | same | same | | (c) `--flavor free` | exit 1, module error | same | same | same | | (d) | exit 0, as (a), except module release has the sources jar and no javadoc jar | same | same | same | Case (a)'s `flutter_release-1.0.pom` depends on `dev.pr9.plugin_a:plugin_a_release:1.0` (checked on `b9493299916`, AGP 9.1.1). Negative control, the master script (same at `5ba8961b517` and `4521d8d6634`), on `b9493299916`: with `newDsl=true` (AGP 9.3.1 and AGP 9.1.1), (a)-(d) all fail with `Can't generate AAR on a non Android library project.`; with `newDsl=false` on AGP 9.3.1, (a) passes and (b)-(d) fail with Gradle's `Task with name 'assembleAar<V>' not found in project ':<p>'.` On `a17fafcc35e`, with only line 13 changed to `hasPlugin`, `newDsl=true` fails at line 178 with `Could not get unknown property 'libraryVariants' for object of type com.android.build.gradle.internal.dsl.LibraryExtensionImpl$AgpDecorated.` devicelab `build_aar_module_test` with that script fails at the `newDsl=true` section with the same error. </details> --- ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant in-code documentation (doc comments with `///`). - [x] If this PR introduces a new feature or capability, I created and linked a website documentation issue or PR in [flutter/website] (or verified none is needed). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [AI contribution guidelines]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [flutter/website]: https://github.com/flutter/website [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
reidbaker-agent
pushed a commit
that referenced
this pull request
Oct 30, 2026
…lutter#194980) Within an RTL `BidiRun`, logical spans progress from right to left, so `TextLayout.addLine` must iterate `paragraph.spans` in reverse logical order to append visual blocks from left to right. Additionally, `TextSpan._getMetrics` previously measured each span in isolation using the paragraph's default direction, causing leading or isolated neutral characters in a span following an RTL span to be measured as LTR (`level = 0`) instead of RTL (`level = 1` per UAX #9 N1) and embedded LTR spans inside an RTL paragraph to be measured as RTL. `WebParagraph._resolveSpans` now keeps the builder-supplied `_styleSpans` private, splits text spans at paragraph-level `bidiRegions` boundaries, and exposes only the BiDi-resolved `WebParagraph.spans` with each sub-span's resolved region direction so `extractTextClusters` and `Canvas2D` metrics match paragraph-level BiDi runs. ### Reviewer & AI Audit Context - **Core Logic Hunks**: `+75 / -9` production lines in `engine/src/flutter/lib/web_ui/lib/src/engine/web_paragraph/paragraph.dart` and `engine/src/flutter/lib/web_ui/lib/src/engine/web_paragraph/layout.dart` (`WebParagraph._resolveSpans()`, `TextLayout.extractBidiRuns()`, `TextLayout.addLine()`) and `+182 / -0` test lines in `engine/src/flutter/lib/web_ui/test/webparagraph/paragraph_bidi_test.dart`. - **C++ `SkParagraph` Parity Anchor**: Matches `OneLineShaper::iterateThroughShapingRegions` (`engine/src/flutter/third_party/skia/modules/skparagraph/src/OneLineShaper.cpp:556-578, 697`), which intersects paragraph-level `fBidiRegions` with style `Block`s so each shaped run has a single UAX #9 level, and `TextLine::iterateThroughSingleRunByStyles` (`engine/src/flutter/third_party/skia/modules/skparagraph/src/TextLine.cpp:1034-1035`), which traverses style blocks in reverse logical order (`run->leftToRight() ? fBlockRange.start + index : fBlockRange.end - index - 1`) within RTL runs. - **Callers & Precondition Proofs**: - Exposing only the BiDi-resolved `WebParagraph.spans` (while keeping builder-supplied `_styleSpans` private) guarantees that `layout.dart`, `wrapper.dart`, and `painter.dart` cannot accidentally read unsplit spans. - Within each UAX #9 `BidiRegion` sub-span, `Canvas2D` `getTextClusters()` emits clusters in monotonic logical offset order (`engine/src/flutter/lib/web_ui/lib/src/engine/web_paragraph/paragraph.dart:783`, Ledger `L007`), and `TextBlock` / `TextPainter` whole-block and per-cluster rendering translate by `spanShiftFromLineStart = shiftFromLineStart - shiftFromSpanStart` (`engine/src/flutter/lib/web_ui/lib/src/engine/web_paragraph/layout.dart:381, 1306`, `engine/src/flutter/lib/web_ui/lib/src/engine/web_paragraph/painter.dart:356-360`), which handles RTL sub-spans measured in `[-spanWidth, 0]`. - **Cross-Phase Consumer Check (`Placeholders` / `Backgrounds` / `Queries`)**: - `PlaceholderSpan`s are preserved without `Canvas2D` text measurement in `_resolveSpans()` and positioned in visual left-to-right order alongside `TextBlock`s in `addLine()`. - `TextPainter` backgrounds/decorations (`engine/src/flutter/lib/web_ui/lib/src/engine/web_paragraph/painter.dart:214, 346`), `getBoxesForRange` (`engine/src/flutter/lib/web_ui/lib/src/engine/web_paragraph/layout.dart:587`), `getPositionForOffset` (`engine/src/flutter/lib/web_ui/lib/src/engine/web_paragraph/layout.dart:774`), and `getGlyphInfoAt` (`engine/src/flutter/lib/web_ui/lib/src/engine/web_paragraph/layout.dart:888`) all consume `line.visualBlocks` and `spanShiftFromLineStart`, so visual left-to-right block ordering and sub-span direction propagate across painting, selection boxes, and hit-testing. ### Tests - `engine/src/flutter/lib/web_ui/test/webparagraph/paragraph_bidi_test.dart`: - `Paragraph BiDi order and layout within multi-span RTL bidi run` — verifies left-to-right visual box ordering (`"BiDi "` -> `"שלום"` -> `" & "` -> `"مرحبا بالعالم"` -> `" · Emoji ..."`), `GlyphInfo.writingDirection` and cluster bounds, and `getPositionForOffset` hit-testing across a span that crosses an RTL->LTR boundary. - `Paragraph BiDi isolated neutral span between RTL spans in LTR paragraph` — verifies that an isolated neutral span (`" + "`) between two RTL spans resolves to RTL (`level = 1`) and is positioned visually between `"שלום"` and `"مرحبا"`. - `Paragraph BiDi multi-span order in RTL paragraph` — verifies that two RTL spans in an RTL paragraph (`"שלום "` followed by bold `"עולם"`) are ordered visually from right to left (`second.right == first.left`). - `Embedded multi-span LTR run inside RTL paragraph` — verifies that `_resolveSpans` overrides the RTL paragraph direction to `TextDirection.ltr` for an embedded multi-span LTR run (`"Hello"`, `" + "`, `"World"`). - `Multi-span RTL run with inline placeholder` — verifies that an inline `PlaceholderSpan` between two RTL text spans is preserved by `_resolveSpans` and positioned visually between them (`second` -> placeholder -> `first`). - **Full WebParagraph corpus (`--full`, `preferWebParagraph` + `forceTestFonts` on `40c36fffd2ec`)**: - Part 1 (`engine/src/flutter/lib/web_ui`): `17/17` `engine/src/flutter/lib/web_ui/test/webparagraph` suites (`+214 ~5 -1`, all 8 BiDi tests passing) + `62/62` `engine/src/flutter/lib/web_ui/test/ui` suites (`+196 ~0 -44`). - Part 2 (`packages/flutter`, `896/896` suites across `8/8` shards): `+16478 ~1231 -812` (`0` compile errors or unfinished shards). ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. Assisted-by: Gemini 4 Argon (Medium)
Owner
Author
|
Closing fork PR — landed upstream on flutter/flutter (flutter#193962, CP flutter#195640). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 9 of 11 of the AGP public-API migration; stacked on #8.
Phase P8 of the AGP public-API migration (flutter#180137,
flutter#166550):
android.libraryVariants property (absent under the new DSL); it
checks for the com.android.library plugin. Error message unchanged.
variants through the public software components (which the script
already used for task creation) instead of libraryVariants.
publishing.singleVariants collection. Flutter now marks projects it
configured with an ext property and declares each variant's
publishing in a try-catch: when the project's own build file already
declared publishing for a variant, the user's declaration wins and a
warning explains the situation and the fix. (Previously any
user-declared variant silently disabled Flutter's publishing setup
for ALL variants, which broke partially-declared projects at
publication time with no explanation.)
Verification (CI): flutter build aar with and without flavors for
module and plugin projects; AAR-host add-to-app flow; a scratch module
with a partial user singleVariant declaration; newDsl=true aar flow.
Revert-safe even after P10, but not after P9.
CI must run the FGP unit tests (the Kotlin suite in packages/flutter_tools/gradle could not run in the delivery sandbox: dl.google.com returns 403, so AGP artifacts do not resolve).