Skip to content

Unify add-to-app module wiring on the variant API and drop host-project cross-wiring - #8

Open
reidbaker-agent wants to merge 9 commits into
agp-apk-copy-versioncodefrom
agp-add-to-app
Open

reidbaker-agent wants to merge 9 commits into
agp-apk-copy-versioncodefrom
agp-add-to-app

Conversation

@reidbaker-agent

@reidbaker-agent reidbaker-agent commented Jul 22, 2026 •

Copy link
Copy Markdown
Owner

Phase 8 of 11 of the AGP public-API migration; stacked on #7.

Phase P7 of the AGP public-API migration (flutter#180137,
flutter#166550), the last Kotlin consumer of the legacy variant
API:

  • Add-to-app module (library) projects now use the same consolidated
    onVariants block as application projects: the Flutter compile task
    and CopyFlutterAssetsTask are registered lazily per library variant,
    and flutter_assets are wired through
    variant.sources.assets.addGeneratedSourceDirectory. The host
    application consumes them through AGP's normal library packaging and
    variant matching, with build modes resolved from the public
    Component.debuggable flag (so a custom debuggable host build type
    such as 'staging' still maps to the debug engine artifacts via the
    module's matched variant). Module variants skip the CLI task-name
    gating: which module variant a host build consumes is AGP's variant
    matching decision, and a module has at most three variants to
    configure lazily.
  • The host-project lookup, the appProject.afterEvaluate
    libraryVariants.all x applicationVariants.all cross-product, the
    explicit merge<HostVariant>Assets.dependsOn edge, and the
    addFlutterDepsForModule legacy fork from P5 are deleted.
  • flutter.hostAppProjectName is now a no-op: it only fed the host
    lookup. A warning explains that it has no effect and will be removed
    in a future Flutter release.
  • The legacy com.android.builder.model.BuildType buildModeFor overload
    lost its last caller and is deleted along with its duplicate tests
    (the P2 name/debuggable and DSL overload tests cover the semantics).
    FlutterPlugin.kt and FlutterPluginUtils.kt no longer import any
    legacy (non com.android.build.api) AGP types.

Verification (CI): build_android_host_app_with_module_source,
module_host_with_custom_build, module_custom_host_app_name scenarios;
module-root flutter build apk; add-to-app newDsl=true test; gradle unit
tests on both AGP axes.

Revert-safe until P10 lands (mutually tolerant with P6).

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).

@reidbaker-agent
reidbaker-agent force-pushed the agp-apk-copy-versioncode branch from c051ed0 to 1d44353 Compare July 22, 2026 15:54
@reidbaker-agent
reidbaker-agent force-pushed the agp-apk-copy-versioncode branch from 1d44353 to f3d2a31 Compare July 28, 2026 13:36
@reidbaker-agent
reidbaker-agent force-pushed the agp-apk-copy-versioncode branch from f3d2a31 to 91f0814 Compare July 28, 2026 13:37
@reidbaker-agent
reidbaker-agent force-pushed the agp-apk-copy-versioncode branch from 91f0814 to 45e5dea Compare July 29, 2026 20:21
@reidbaker-agent
reidbaker-agent force-pushed the agp-apk-copy-versioncode branch from 45e5dea to 9ce1623 Compare July 29, 2026 20:43
@reidbaker-agent
reidbaker-agent force-pushed the agp-apk-copy-versioncode branch from 9ce1623 to e34a92a Compare July 30, 2026 21:59
@reidbaker-agent
reidbaker-agent force-pushed the agp-apk-copy-versioncode branch from e34a92a to b7a23f7 Compare July 31, 2026 16:16
…page draft)

Phase P0 of the Flutter Gradle Plugin migration to the AGP public API
surface (flutter#180137, flutter#166550): the contributor
migration record (replacement map, decision records, phase map,
revert-window table) and the draft of the user-facing breaking-change
page to be published to docs.flutter.dev before the newDsl flip reaches
beta.

Revert-safe: always.
…k/ndk reads to the public DSL

Phase P1 of the AGP public-API migration (flutter#180137,
flutter#166550):

- VersionFetcher no longer calls the internal
  com.android.build.gradle.internal.utils.getKotlinAndroidPluginVersion.
  getKGPVersion now relies on the existing public fallback chain
  (kotlin_version property -> KotlinAndroidPluginWrapper.pluginVersion ->
  reflection) and documents that null is the expected result when KGP is
  absent (e.g. under AGP built-in Kotlin). DependencyVersionChecker
  already treats null as "KGP not required".
- AgpCommonExtensionWrapper gains compileSdkPreview.
- getCompileSdkFromProject now reads compileSdk/compileSdkPreview from
  the new DSL via the wrapper and returns a structured CompileSdkVersion
  instead of parsing the legacy "android-NN" string. The PluginHandler
  compileSdk mismatch warning uses CompileSdkVersion.isHigherThan, which
  defines the preview semantics the old string comparison got wrong
  (resolves the PluginHandler TODO): preview > numeric, distinct preview
  codenames incomparable (no warning), numeric compared numerically.
  Warning message text is unchanged.
- getConfiguredNdkVersion reads through the wrapper instead of the
  legacy BaseExtension fallback.
- Deletes the setAgpKotlinVersionToNull test helper (existed only to
  stub the internal AGP call) and updates unit tests accordingly; adds
  tests for both preview-vs-numeric directions and the codename reset
  case.

Verification note: the FGP unit test suite could not be executed in this
sandbox (the network policy blocks dl.google.com, so AGP artifacts do
not resolve); run 'gradle test' in packages/flutter_tools/gradle in CI.

Revert-safe until P2 lands.
@reidbaker
reidbaker force-pushed the agp-apk-copy-versioncode branch from b7a23f7 to 53bcc7f Compare July 31, 2026 18:07
…gh the new DSL

Phase P2 of the AGP public-API migration (flutter#180137,
flutter#166550):

- buildModeFor gains a (buildTypeName, isDebuggable) core overload and a
  new-DSL BuildType overload. Application and dynamic-feature build
  types use their public isDebuggable flag; library build types have no
  public debuggable signal at DSL scope, so the conventional "debug"
  name is used for them. The legacy com.android.builder.model.BuildType
  overload remains for the variant-scope call sites that migrate in
  later phases.
- addFlutterDependencies (engine/embedding deps) now takes a new-DSL
  BuildType, and FlutterPlugin registers it via the wrapper's buildTypes
  container instead of the legacy BaseExtension.
- PluginHandler's three dependency-wiring loops (per-build-type Api
  wiring, embedding deps, plugin-to-plugin deps) iterate the wrapper
  container. The build-type copy block still uses the legacy container
  and internal.dsl.BuildType; that is phase P3.
- build.gradle.kts accepts -PagpVersion= so CI can compile and test the
  plugin against the AGP 9 line in addition to the default (the public
  DSL is not binary-compatible between AGP 8 and 9 everywhere), and a
  new validateNoCommonExtensionInBytecode task fails the build if any
  compiled main class references CommonExtension (the known-broken
  type that AgpCommonExtensionWrapper exists to avoid).
- android_run_flutter_gradle_plugin_tests_test.dart gains a second test
  running the suite with -PagpVersion=<templateAndroidGradlePluginVersion>.
- PluginHandlerTest no longer depends on exhausted-iterator mock
  behavior; the wrapper container returns a fresh iterator per call.

Verification note: FGP unit tests could not be executed in this sandbox
(network policy blocks dl.google.com); run 'gradle test' and
'gradle -PagpVersion=9.1.0 test' in packages/flutter_tools/gradle in CI.

Revert-safe until P3 lands.
Phase P3 of the AGP public-API migration (flutter#180137,
flutter#166550):

- PluginHandler no longer imports com.android.build.gradle.internal.dsl.BuildType.
  The build-type copy block that shared live legacy BuildType instances
  (addAll) for app-type plugin projects and hand-copied two properties
  for library plugin projects is replaced by a single initWith-based
  copy on the new-DSL containers: missing build types are created on
  the plugin project with initWith(appBuildType) (which carries
  matchingFallbacks), and isDebuggable is additionally copied when both
  sides are application build types. Library build types cannot receive
  app-specific properties through the public DSL - this is a documented
  behavior change of the migration (BuildConfig.DEBUG / JNI
  debuggability of plugins built for custom debuggable build types).
- Production sources are now free of com.android.build.gradle.internal
  imports; InternalAgpApiImportTest locks that in (test sources may
  still use internals until the gradle-api dependency swap).
- PluginHandlerTest: the two mock-only copy tests (which never invoked
  configurePlugins) are replaced with tests that run configurePlugins
  and assert the initWith copy for both a library plugin project and an
  app plugin project, including the custom-debuggable-build-type ->
  debug engine artifact mapping.
- The planned pre-spike (afterEvaluate DSL mutation under newDsl=true)
  could not run in this sandbox; recorded in the migration doc with the
  finalizeDsl fallback. android_plugin_example_app_build and a custom
  build-type scratch build must confirm in CI.

Verification note: FGP unit tests could not be executed in this sandbox
(network policy blocks dl.google.com); run 'gradle test' (both AGP
axes) in packages/flutter_tools/gradle in CI.

Revert-safe until P4 lands.
…public DSL

Phase P4 of the AGP public-API migration (flutter#180137,
flutter#166550):

- AgpCommonExtensionWrapper gains externalNativeBuild, and both the
  already-configuring-a-native-build check in forceNdkDownload and the
  synthetic-cmake fallback now read/write cmake.path,
  cmake.buildStagingDirectory and the per-build-type
  externalNativeBuild.cmake.arguments through the public DSL (property
  assignment with File values instead of the legacy Any-taking
  CmakeOptions methods; arguments via the public MutableList).
- getLegacyAndroidExtension and the BaseExtension import are deleted
  from FlutterPluginUtils; nothing in production sources references
  BaseExtension anymore.
- forceNdkDownload tests move their mocks from BaseExtension/
  internal CmakeOptions to the wrapper path (findByName("android") +
  public Cmake), assert cmake arguments by list content, and drop the
  defaultConfig-untouched assertions that only existed to guard the
  legacy extension. The two tests that asserted the old
  ApplicationExtension-vs-BaseExtension ndkVersion preference are
  deleted: getConfiguredNdkVersion has had a single source since P1.
  Also restores the findByType(ApplicationExtension) mocks (needed by
  isFlutterAppProject) that P1's test edit dropped in three tests.
- FlutterPluginUtilsTest no longer imports internal.dsl.CmakeOptions or
  internal.dsl.DefaultConfig.

Verification note: FGP unit tests could not be executed in this sandbox
(network policy blocks dl.google.com); run 'gradle test' (both AGP
axes) in packages/flutter_tools/gradle in CI, plus an NDK-absent
scratch build exercising the synthetic-cmake fallback.

Revert-safe until P5 lands.
… projects

Phase P5, commit 1 of 2 (P5a), of the AGP public-API migration
(flutter#180137, flutter#166550):

- For application projects, compileFlutterBuild<Variant> (FlutterTask)
  is now registered inside the consolidated androidComponents.onVariants
  block as a lazy TaskProvider, configured entirely from the public
  variant API: minSdk from Variant.minSdk.apiLevel, flavor from
  Variant.flavorName, and the Flutter build mode from
  buildModeFor(variant.buildType, variant.debuggable) so custom
  debuggable build types keep mapping to debug engine artifacts.
  Registration is gated by shouldConfigureFlutterTask on the computed
  assemble task name (new name-based overload), mirroring the legacy
  callback's gating.
- addFlutterDeps is split: addFlutterDepsForApp (per-ABI versionCode,
  legacy assets copy into the merged-assets dir, processResources hook)
  looks the compile task up by name instead of registering it;
  addFlutterDepsForModule keeps the full legacy path for add-to-app
  module projects until that path migrates. The always-null
  packageAssets/isUsedAsSubproject dead code and the duplicated
  processResources hook in the application variant callback are
  removed.

No behavior change intended for what gets built; the assets delivery
mechanism changes in the next commit (P5b).

Verification note: FGP unit tests could not run in this sandbox (network
policy blocks dl.google.com); run 'gradle test' (both AGP axes) in CI.

Revert-safe (with P5b) until P6 lands.
…app path

Phase P5, commit 2 of 2 (P5b), of the AGP public-API migration
(flutter#180137, flutter#166550):

- New CopyFlutterAssetsTask stages flutter_assets/** from the Flutter
  build output into its own output directory (modeled on
  CopyFlutterJniLibsTask, including the overlapping-outputs rationale),
  applying the user read+write file permissions the old Copy task set.
- For application projects, copyFlutterAssets<Variant> is registered in
  the consolidated onVariants block as a lazy TaskProvider and wired via
  variant.sources.assets.addGeneratedSourceDirectory. AGP now merges
  Flutter's assets like any other assets source; collisions with user
  assets resolve by source-set priority instead of the old post-merge
  overwrite (documented behavior change). A missing assets source set
  fails loudly instead of silently building an APK without Flutter
  assets.
- The legacy app-path assets copy (into mergeAssets.outputDir), its
  processResources/cleanMergeAssets task-graph surgery, and the manual
  compress<V>Assets dependsOn wiring are deleted for app projects; AGP
  owns those edges now. The application variant callback is reduced to
  the per-ABI versionCode override and the flutter-apk copy, both of
  which migrate in the next phase. The add-to-app module path is
  unchanged (still the full legacy copy) until it migrates.
- copyFlutterAssets<Variant> changes type from org.gradle.api.tasks.Copy
  to CopyFlutterAssetsTask and is now registered lazily (documented
  breaking change for build scripts that referenced it by type).
- Tests: new CopyFlutterAssetsTaskTest executes the task against real
  files (staging layout, permission bits, non-asset exclusion, stale
  output cleanup). The FlutterPluginTest filePermissions test built on
  capturing the legacy Copy registration is superseded by it.

Verification (CI): gradle unit tests both AGP axes; integration
builddir/obfuscate/jni/print_build_variants/deferred_components_assets;
add-to-app source smoke + flutter build aar; asset-staleness rebuild
check; config-cache per baseline.

Revert-safe until P6 lands.
Phase P6 of the AGP public-API migration (flutter#180137,
flutter#166550). The application path no longer uses the legacy
variant API at all:

- New CopyFlutterApksTask copies the variant's SingleArtifact.APK
  directory contents (via BuiltArtifactsLoader) into
  build/outputs/flutter-apk under the unchanged names
  app[-abi][-flavor]-<build-mode>.apk. It is attached as a finalizer of
  assemble<Variant> (matched by name, with a projectsEvaluated
  assertion that fails loudly if the assemble task was never created,
  instead of silently leaving flutter run/build without APKs). The task
  declares individual predictable @OutputFiles - computed from target
  platforms, flavor, and build mode - rather than the shared
  flutter-apk directory, so it is UP-TO-DATE-capable without
  overlapping outputs between variants; it replaces the old
  assemble.doLast copy.
- Per-ABI versionCode for --split-per-abi builds is now a read-then-set
  on VariantOutput.versionCode inside onVariants (the output is seeded
  with AGP's merged value, which covers flavor-defined versionCodes),
  replacing versionCodeOverride on the legacy ApkVariantOutput. When
  the built APK's versionCode differs from what Flutter configured
  (e.g. an afterEvaluate mutation), CopyFlutterApksTask logs a warning
  pointing at androidComponents.onVariants.
  Note on onVariants FIFO callback ordering: Because FGP is applied at
  line 25 of build.gradle.kts, FGP's callback executes before app-level
  onVariants callbacks at line 70. Any custom app-level block reading
  output.versionCode.get() will observe the ABI-offset value. For
  standard apps, no change is needed; monotonic ABI ordering and Play
  Store uniqueness are preserved even if an app transforms versionCode.
- The entire legacy applicationVariants.configureEach block and its
  helpers are deleted; AbstractAppExtension remains only in the
  add-to-app module path, which migrates next.

Verification (CI): split-per-abi + apkanalyzer per-ABI versionCode
assertions including the flavor-defined-versionCode case; flavor
filename check; flutter run / hot restart / attach; Windows-runner
smoke for the copy tasks; android_e2e_api_test; gradle unit tests on
both AGP axes.

Revert-safe until P7 lands (mutually tolerant with P7 until P10).
…ct cross-wiring

Phase P7 of the AGP public-API migration (flutter#180137,
flutter#166550), the last Kotlin consumer of the legacy variant
API:

- Add-to-app module (library) projects now use the same consolidated
  onVariants block as application projects: the Flutter compile task
  and CopyFlutterAssetsTask are registered lazily per library variant,
  and flutter_assets are wired through
  variant.sources.assets.addGeneratedSourceDirectory. The host
  application consumes them through AGP's normal library packaging and
  variant matching, with build modes resolved from the public
  Component.debuggable flag (so a custom debuggable host build type
  such as 'staging' still maps to the debug engine artifacts via the
  module's matched variant). Module variants skip the CLI task-name
  gating: which module variant a host build consumes is AGP's variant
  matching decision, and a module has at most three variants to
  configure lazily.
- The host-project lookup, the appProject.afterEvaluate
  libraryVariants.all x applicationVariants.all cross-product, the
  explicit merge<HostVariant>Assets.dependsOn edge, and the
  addFlutterDepsForModule legacy fork from P5 are deleted.
- flutter.hostAppProjectName is now a no-op: it only fed the host
  lookup. A warning explains that it has no effect and will be removed
  in a future Flutter release.
- The legacy com.android.builder.model.BuildType buildModeFor overload
  lost its last caller and is deleted along with its duplicate tests
  (the P2 name/debuggable and DSL overload tests cover the semantics).
  FlutterPlugin.kt and FlutterPluginUtils.kt no longer import any
  legacy (non com.android.build.api) AGP types.

Verification (CI): build_android_host_app_with_module_source,
module_host_with_custom_build, module_custom_host_app_name scenarios;
module-root flutter build apk; add-to-app newDsl=true test; gradle unit
tests on both AGP axes.

Revert-safe until P10 lands (mutually tolerant with P6).
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]>
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]>
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.
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.
@reidbaker-agent

Copy link
Copy Markdown
Owner Author

Stack Audit Findings & Guidance for Updating Agent

Context: This pull request (Phase 8 of the AGP 9.1.0 migration) was audited as part of review on flutter/flutter#192488 (Phase 6).
Action Required: Re-author / rebase this branch from a recent cut of the upstream flutter/flutter master branch + the updated Phase 6 (flutter/flutter#192488) and Phase 7 (#7).

Issues to resolve when re-authoring this PR:

  1. Eliminate String-Based Compile Task Lookup in CopyFlutterJniLibsTask (Mis-architected wiring):

    • Evidence: FlutterPlugin.kt:319-340. The task lookup still relies on:
      dependsOn(projectToAddTasksTo.tasks.matching { it.name == compileTaskName })
      intermediateDir.set(
          projectToAddTasksTo.layout.dir(
              projectToAddTasksTo.provider {
                  val compileTask = projectToAddTasksTo.tasks.findByName(compileTaskName) as? FlutterTask
                  compileTask?.outputDirectory
              }
          )
      )
    • Why it's a problem: In Phase 6, looking up the compile task by name was a necessary compromise because add-to-app module compile tasks were registered in legacy libraryVariants. In Phase 8, however, both app and module compile tasks are registered in the same onVariants callback (compileTaskProvider at L359). String-based lookup is no longer needed. Furthermore, tasks.matching { ... } causes Gradle to eagerly realize and configure every registered task in the container.
    • Direction: Wire CopyFlutterJniLibsTask directly from compileTaskProvider (e.g. intermediateDir.set(compileTaskProvider.map { it.outputDirectory })), matching the pattern used for assets in Phase 6.
  2. Avoid Control-Flow via Mid-Block return@onVariants:

    • Evidence: FlutterPlugin.kt:396-401 has if (!isAppProject) { return@onVariants } in the middle of the onVariants lambda.
    • Why it's a problem: Deciding whether task registration applies to modules based solely on whether it precedes or follows a mid-block return is error-prone. Any shared wiring added at the bottom of the lambda will silently skip modules. It also compressed compilation, asset delivery, and APK copying into a single ~100-line inline block, undoing the clean separation of concerns established in Phase 6.
    • Direction: Modularize variant configuration into explicit helper functions (e.g. configureAppVariantOutputs and configureModuleVariantOutputs) rather than early returns.
  3. Remove Redundant dependsOn:

    • Evidence: In copyFlutterAssets registration, both dependsOn(compileTaskProvider) and intermediateDir.set(compileTaskProvider.map { ... }) are present.
    • Direction: Drop the explicit dependsOn(compileTaskProvider); the Gradle Provider mapping already establishes the task dependency edge.
  4. Cleanup Dead Overload and Cross-Project Property Read:

    • Evidence: FlutterPluginUtils.shouldConfigureFlutterTask(Project, Task) has no production callers. Remove it.
    • Evidence: FlutterPlugin.kt:474 reads projectToAddTasksTo.rootProject.hasProperty("flutter.hostAppProjectName"). Reading rootProject breaks Gradle Isolated Projects. Use projectToAddTasksTo.providers.gradleProperty(...) instead. Also update dev/integration_tests/pure_android_host_apps/android_custom_host_app/gradle.properties to stop setting this deprecated property so the fixture doesn't emit deprecation warnings.

@reidbaker
reidbaker force-pushed the agp-apk-copy-versioncode branch 2 times, most recently from 79ef973 to e15544b Compare October 2, 2026 01:38
EricApostal pushed a commit to EricApostal/flutter that referenced this pull request Oct 12, 2026
… variant API (flutter#193717)

PR 7 ([flutter#193693](flutter#193693)) merged
to `master` as `ecd1ab06865`, and `23ddd6b74e6` merges `master` into
this branch, so the [Files
changed](https://github.com/flutter/flutter/pull/193717/files) tab shows
only PR 8's changes.

PR 8 commits:
- `8c9a54d8297` Unify add-to-app module wiring on the variant API
- `2c28d8f8245` Merge PR 7 head `f668780fd47` (conflicts resolved to PR
7's wording where PR 8 does not change the code)
- `a6be16034c8` Trim comments added by PR 8
- `b8cd39bcf81` Address review: clarify the variant gate and task
registration comments
- `2efbd345505` Address review: check the add-to-app module in
module_host_with_custom_build_test
- `d8f869ec468` Address review: share task registration stubs in
FlutterPluginTest
- `798ac295725` Address review: take ApplicationVariant in
configureApplicationOutputs
- `1366874099e` Address review: drop
android.compatibility.enableLegacyApi from the qa section
- `eb20bbd43de` Merge PR 7 head `25550b3fc49` (no conflicts)
- `23ddd6b74e6` Merge `master` at `ecd1ab06865` (PR 7 squash-merged).
`FlutterPlugin.kt`, `FlutterPluginUtils.kt` and `FlutterPluginTest.kt`
conflicted only because the squash commit is not an ancestor; `master`'s
copies are identical to `25550b3fc49`, already merged by `eb20bbd43de`,
so this branch's side was kept.

## Description

This is PR 8 of 11 in the AGP 9.1.0 / public `gradle-api` migration
stack ([flutter#180137](flutter#180137),
[flutter#166550](flutter#166550)).

An add-to-app module (an Android library) is wired through the same
`androidComponents.onVariants` path as an app. The module adds Flutter's
assets and native libraries to its own variants, and the host app
consumes them like any Android library. Flutter does not look up or
configure the host project. With this PR, a host app with AGP 9's
`android.newDsl=true` builds a Flutter module from source; on
`33f39876b92` it fails with `Check failed.` while configuring `:app`.

**Breaking:** `flutter.hostAppProjectName` has no effect. Setting it
logs a warning.

**Base:** `master`. PR 7 (flutter#193693) merged as `ecd1ab06865`.

<details>
<summary>Agent authored details</summary>

### Changes

1. **Module variants on `onVariants`** (`FlutterPlugin.kt`
`addFlutterTasks`). For every variant, app or library, `onVariants`
calls `registerFlutterCompileTask`, `registerFlutterAssetTasks` and
`registerFlutterJniLibsTask`. Only application variants then call the
new `configureApplicationOutputs` (per-ABI versionCodes and the
flutter-apk copy), which takes an `ApplicationVariant`. There is no
mid-lambda `return@onVariants`.
2. **Every module variant is configured.**
`shouldCompileFlutterForVariant` returns true for a `LibraryVariant`.
The command-line check (`shouldConfigureFlutterTask`, flutter#109560) compares
the command line with the project's own `assemble<Variant>` names, and a
host task such as `:app:assembleDemoStaging` never matches the module
variant it consumes (`debug`, via `matchingFallbacks`). A module has
three variants: the template's `debug` and `release`, plus the `profile`
build type the plugin creates. It declares no flavors
(`templates/module/android/library_new_embedding/Flutter.tmpl/build.gradle.tmpl`).
The tasks are registered lazily, so a build runs only the module variant
the host consumes. `--dry-run` on a host with flavor `demo` and build
types `staging` (falls back to `debug`) and `prod` (falls back to
`release`):

   | Host task | Module Flutter tasks in the task graph |
   | --- | --- |
| `app:assembleDemoDebug`, `app:assembleDemoStaging` |
`compileFlutterBuildDebug`, `copyFlutterAssetsDebug`,
`copyJniLibsflutterBuildDebug` |
| `app:assembleDemoRelease`, `app:assembleDemoProd` |
`compileFlutterBuildRelease`, `copyFlutterAssetsRelease`,
`copyJniLibsflutterBuildRelease` |

3. **Build mode from the module variant.** `registerFlutterCompileTask`
reads the library variant's `buildType` and public
`Component.debuggable`, so the mode follows the module variant the host
consumes.
4. **jniLibs wired from the compile task provider.**
`registerFlutterJniLibsTask` takes `compileTaskProvider` and sets
`intermediateDir` from `compileTaskProvider.map { it.outputDirectory }`.
The `tasks.matching { it.name == … }` dependency and the `findByName`
provider are gone. Both copy tasks are registered only together with
their compile task, so `intermediateDir` is required (not `@Optional`)
in `CopyFlutterJniLibsTask` and `CopyFlutterAssetsTask`, and their
`isPresent` branches are removed.
5. **`flutter.hostAppProjectName`.** `warnIfHostAppProjectNameIsSet`
reads `project.providers.gradleProperty(…)` (not
`rootProject.hasProperty`) and logs `Warning: The Gradle property
'flutter.hostAppProjectName' has no effect. Remove it from
gradle.properties.` The `flutter.hostAppProjectName=SampleApp` line is
removed from
`dev/integration_tests/pure_android_host_apps/android_custom_host_app/gradle.properties`,
so devicelab `module_custom_host_app_name_test` (host project
`:SampleApp`) checks that a host not named `app` builds without the
property.
6. **Deleted:**
- From `FlutterPlugin.kt`: the host-project lookup, the
`appProject.afterEvaluate` × `libraryVariants.all` ×
`applicationVariants.all` loop, the `merge<HostVariant>Assets.dependsOn`
edge, `addFlutterDepsForModule` and `addCopyFlutterAssetsDependency`.
- From `FlutterPluginUtils.kt`: `shouldConfigureFlutterTask(Project,
Task)` and `buildModeFor(com.android.builder.model.BuildType)`. Both
lost their last caller.
7. **Docs.**
- `Migrating-Flutter-Gradle-Plugin-to-AGP-public-API.md`: decision
record 5 and "Features that must break" item 6 are rewritten.
-
[`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
add-to-app section is rewritten.
- The text these replace said that the warning names a removal milestone
(it does not), and that a host `staging` debuggable build type gets
debug artifacts (the module variant decides). It also said that
`tasks.getByPath(":flutter:copyFlutterAssetsDebug")` fails with "Task
with path … not found". That is false: in the scratch host below it
returns a `CopyFlutterAssetsTask`.
8. **Tests.**
   - New `FlutterPluginTest` cases:
- jniLibs wiring from the compile task provider, with no name lookups;
- a module variant configured for a single unrelated command-line task,
with no APK wiring;
     - the module build mode for `debug`, `profile` and `release`;
- the `hostAppProjectName` warning, with no host lookup, and no warning
when it is unset.
   - The skip test also checks that no jniLibs task is registered.
- The new tests share stub helpers (`stubTaskRegistration`,
`captureTaskConfiguration`, `setCommandLineTasks`).
- devicelab `module_host_with_custom_build_test` gets a final section.
The host fixture adds a build type `qa` (debuggable, falls back to
`release`). The section runs `app:assembleDemoQa` with
`android.newDsl=true` and `flutter.hostAppProjectName=app`. It checks
that the output has the warning, and that `app-demo-qa.apk` has the
Flutter assets and `libapp.so` for arm64-v8a and armeabi-v7a, and no
debug assets. The existing sections already check
`app:assembleDemoStaging` (debug artifacts).

### Audit resolution

[PR 8 draft
audit](reidbaker-agent#8 (comment)):

| Item | Resolution |
| --- | --- |
| 1. String-based compile task lookup for jniLibs | Provider wiring,
`FlutterPlugin.kt:549-553`. Unit test `onVariants wires the jniLibs copy
to the output of the variant's compile task`. |
| 2. Mid-block `return@onVariants` | None. `onVariants` (`:329-350`)
calls helpers; app-only wiring is in `configureApplicationOutputs`
(`:647`). |
| 3. Redundant `dependsOn` | None on the assets copy (`:497-530`) or the
jniLibs copy (`:536-560`). |
| 4. Dead overload; `rootProject` read; fixture property |
`shouldConfigureFlutterTask(Project, Task)` deleted.
`providers.gradleProperty` at `:665`. Fixture line removed. |

[PR 7 draft
audit](reidbaker-agent#7 (comment)):
its four items were resolved in flutter#193693. The stale "registered in
`applicationVariants`" KDoc it named has no counterpart here; the
`registerFlutterCompileTask` KDoc names no project type.

### Why removing the deleted code is safe

- The deleted module path used
`com.android.build.gradle.LibraryExtension.libraryVariants`,
`AbstractAppExtension.applicationVariants`,
`BaseVariant.mergeAssetsProvider` and
`BaseVariantOutput.processResourcesProvider`. AGP does not register
those extensions with `android.newDsl=true`, which is why `33f39876b92`
fails with `Check failed.` in the host `afterEvaluate` block that looks
up `LibraryExtension`. Its replacements (`LibraryVariant`,
`Component.debuggable`,
`Sources.assets`/`jniLibs.addGeneratedSourceDirectory`) are public
`com.android.build.api` APIs. FGP compiles against AGP 9.1.1, the
minimum on `master` (flutter#193813), and `Component.getDebuggable` also exists
in gradle-api 8.11.1 (checked with `javap`).
- Remaining `hostAppProjectName` users: none. `git grep
hostAppProjectName` hits only this PR's code, tests and docs, plus an
unrelated iOS `XcodeProject.hostAppProjectName` in the tool. The
templates, `include_flutter.groovy`, `module_plugin_loader.gradle` and
devicelab do not read it. devicelab `module_custom_host_app_name_test`
(host project `:SampleApp`) passes without the fixture property.
- `com.android.builder.model.BuildType` and
`shouldConfigureFlutterTask(Project, Task)` have no remaining
references.

### Removed comments, docstrings and tests

| Removed | Reason |
| --- | --- |
| KDoc and two `TODO(gmackall)` comments (flutter#166550) on
`addFlutterDepsForModule`; KDoc on `addCopyFlutterAssetsDependency` |
Functions deleted. flutter#166550 stays open for the rest of the stack. |
| The "host app build variant → Flutter variant" table comment in the
module block | The mapping is gone; the module variant decides the mode.
|
| KDoc of `shouldConfigureFlutterTask(Project, Task)` and
`buildModeFor(ModelBuildType)` | Functions deleted. |
| `flutterCompileTaskName` KDoc | Nothing references the task by name,
and the one-line name builder needs no doc. |
| flutter#188785 comment in `registerFlutterJniLibsTask`, and the `@Optional`
KDoc and "no Flutter build" comment in `CopyFlutterJniLibsTask` | The
jniLibs task is registered only with its compile task.
`gradle_libapp_so_packaging_test` "app:assembleAndroidTest builds when
no Flutter compile task is configured" still passes. |
| `@Optional` KDoc on `CopyFlutterAssetsTask.intermediateDir` (it kept
`@Optional` for the module migration) | The migration is this PR. |
| `FlutterPluginUtilsTest`: `buildModeFor returns profile if the
BuildType has name profile`, `buildModeFor returns debug if the
BuildType is debuggable`, `buildModeFor returns release if the BuildType
is not debuggable and not named profile` | Overload deleted.
`buildModeFor with a name and debuggable flag prefers the profile name
over debuggability` covers the same cases. |
| `FlutterPluginUtilsTest`: four `shouldConfigureFlutterTask` tests used
a mocked `Task` | Kept with the same names and inputs, calling the
`String` overload. |
| `CopyFlutterAssetsTaskTest`: `clears the destination directory when
there is no flutter build for the variant` | The input is required, so
that case cannot happen. Replaced by `removes staged assets that the
flutter build does not produce`, which keeps the stale-file check. |
| Comments trimmed per review (`a6be16034c8`): the
`PROP_HOST_APP_PROJECT_NAME` KDoc; the `registerFlutterJniLibsTask` KDoc
sentence naming its compile task; the "An application project only has
application variants." comment above the `check`; restatements in the
KDoc of `shouldCompileFlutterForVariant` (PR 7's paragraph restored
verbatim), `registerFlutterCompileTask`, `configureApplicationOutputs`,
`warnIfHostAppProjectNameIsSet` and both `intermediateDir`s; test helper
KDocs and integration test comments; migration doc item 6 | Each
restated the code. What remains is why library variants are ungated, why
the inputs are required, and the test intent. Added code-comment lines
vs `f668780fd47`: 85 → 39. |
|
`packages/flutter_tools/test/integration.shard/android_add_to_app_module_test.dart`
(added by this PR, then deleted per review, `2efbd345505`) | Its `qa`
check moved into devicelab `module_host_with_custom_build_test`, which
builds the same fixture. Its `staging` check duplicated the devicelab
staging section, except for `android.newDsl=true`. The devicelab task
runs presubmit on Linux, Mac and Windows (`.ci.yaml`), like the deleted
test. |
| Rewritten per review (`b8cd39bcf81`): the library paragraph of the
`shouldCompileFlutterForVariant` KDoc, the assets `GradleException`
comment, the `registerFlutterCompileTask` and
`configureApplicationOutputs` KDocs | They explained too little ("What
does this mean?") or restated the code. |
| The `check(variant is ApplicationVariant)` that PR 7 added in the app
`onVariants` path, moved by this PR into `configureApplicationOutputs`
(`798ac295725`) | The call site branches on `variant is
ApplicationVariant`, and the function takes an `ApplicationVariant`, so
the compiler enforces the type. In an application project every variant
is an `ApplicationVariant`, so the APK wiring runs for the same
variants. |
| `-Pandroid.compatibility.enableLegacyApi=false` in the devicelab `qa`
section (`1366874099e`) | AGP 9.1.0, 9.1.1 and 9.3.1 mark the option
`Deprecated(VERSION_10_0)`. Its consumers back the variant API that
`android.newDsl=true` removes. |

### Behavioral and compatibility notes

1. **`flutter.hostAppProjectName` has no effect (breaking).** It logs a
warning and names no removal milestone. Its only use was the host
lookup.
2. **The Flutter build mode follows the module variant the host
consumes.** A host `debug`/`profile`/`release` consumes the module
variant of the same name. A custom host build type gets the module
variant its `matchingFallbacks` select. With a debuggable `qa` that
falls back to `release`, built as the only task, the APKs contain:

   | Build | `flutter_assets` | `libapp.so` | Flutter tasks run |
   | --- | --- | --- | --- |
| `33f39876b92` (`newDsl=false`) | no | no | `compileFlutterBuildDebug`
(not packaged) |
   | This PR | yes | yes | `compileFlutterBuildRelease` |

3. **A host `assemble<Variant>AndroidTest` run on its own runs the
module's `flutter assemble` for that variant.** `--dry-run` of
`:app:assembleDemoDebugAndroidTest` lists
`:flutter:compileFlutterBuildDebug` with this PR, and only
`copyJniLibsflutterBuildDebug` on `33f39876b92`. This is a cost,
accepted because the module cannot map host task names to its own
variants.
4. **Module assets are a generated assets source directory.** The
deleted code copied them into the module's merged-assets output after
`mergeAssets`, and made every build re-run `clean<MergeAssetsTask>`.
Collisions resolve by source-set priority, as for apps since PR 6.
5. **`copyFlutterAssets<V>` in a module is a `CopyFlutterAssetsTask`,
not a `Copy`.** Lookups by name still work; lookups typed as `Copy`
fail.
6. Related open work:
[flutter#191703](flutter#191703) reports an
NPE in `getLegacyAndroidExtension` with `newDsl=true`. That function was
removed earlier in the stack, and this PR removes the next failure on
that path. A module with plugins on `newDsl=true` is not tested here. No
conflict with
[flutter#193682](flutter#193682) or
[flutter#193610](flutter#193610)
(`gradle_errors.dart`); this PR changes no message that the tool
matches. [flutter#184409](flutter#184409)
asks for separate library and app logic in `addFlutterTasks`; this PR
shares `onVariants` and splits only the app outputs.

### Integration test coverage of the changed paths

| Path | Tests |
| --- | --- |
| Module from source, `newDsl=true`, debuggable custom build type on
`release`, `hostAppProjectName` warning | devicelab
`module_host_with_custom_build_test`, `app:assembleDemoQa` section |
| Module from source, flavor and custom build types, debug and release |
devicelab `module_host_with_custom_build_test` (other sections;
`app:assembleDemoStaging` needs the ungated `LibraryVariant`),
`build_android_host_app_with_module_source` |
| Host project not named `app`, without `hostAppProjectName` | devicelab
`module_custom_host_app_name_test` |
| Module AAR (`flutter build aar`) and AAR host | devicelab
`build_aar_module_test`, `build_android_host_app_with_module_aar`;
`android_obfuscate_test`, `android_plugin_new_output_dir_test` |
| App path | `gradle_libapp_so_packaging_test`,
`flutter_build_apk_split_per_abi_test`,
`android_gradle_asset_merging_test`,
`android_run_flutter_gradle_plugin_tests_test` |

### Tests run locally (macOS, JDK 17)

On the head after merging `master` (`23ddd6b74e6`; FGP compiles and
tests against AGP 9.1.1):

- `./gradlew test --rerun-tasks` in `packages/flutter_tools/gradle`: 33
suites, 277 tests, 0 failures.
- ktlint 1.5.0 with the CI `.editorconfig` and baseline: clean.
- `dart format` and `dart analyze --fatal-infos` on
`module_host_with_custom_build_test.dart` and
`gradle_libapp_so_packaging_test.dart`: clean.
- `gradle_libapp_so_packaging_test`: 4 tests pass.
- devicelab, all `success`: `module_host_with_custom_build_test`,
`module_custom_host_app_name_test`,
`build_android_host_app_with_module_source`.

On `1366874099e`:

- `./gradlew test --rerun-tasks` in `packages/flutter_tools/gradle`: 33
suites, 274 tests, 0 failures.
- ktlint 1.5.0 with the CI `.editorconfig` and baseline: clean.
- `dart format` and `dart analyze --fatal-infos` on
`module_host_with_custom_build_test.dart`: clean.
- devicelab `module_host_with_custom_build_test`: `success`. The
`app:assembleDemoQa` section ran `./gradlew app:assembleDemoQa
-Pandroid.newDsl=true -Pflutter.hostAppProjectName=app` and logged the
`flutter.hostAppProjectName` warning.

On `d8f869ec468`:

- `./gradlew test --rerun-tasks` in `packages/flutter_tools/gradle`: 33
suites, 274 tests, 0 failures.
- ktlint 1.5.0 with the CI `.editorconfig` and baseline: clean.
- `dart format` and `dart analyze --fatal-infos` on
`module_host_with_custom_build_test.dart`: clean.
- devicelab `module_host_with_custom_build_test`: `success`. The
`app:assembleDemoQa` section logged the `flutter.hostAppProjectName`
warning.

On `a6be16034c8` (the merge and the trim):

- `./gradlew test` in `packages/flutter_tools/gradle`: 33 suites, 274
tests, 0 failures.
- ktlint 1.5.0 with the CI `.editorconfig` and baseline: clean.
- `dart format` and `dart analyze --fatal-infos` on the changed Dart
files: clean.
- `android_add_to_app_module_test` (deleted in `2efbd345505`): passes.
- devicelab `module_custom_host_app_name_test` (fixture without
`flutter.hostAppProjectName`): `success`, and the log has no
`hostAppProjectName` warning.

On `8c9a54d8297` (before the merge of PR 7's review changes and the
comment trim):

- Integration tests, one at a time, all pass:
`android_add_to_app_module_test` (1), `android_obfuscate_test` (2),
`android_plugin_new_output_dir_test` (1),
`gradle_libapp_so_packaging_test` (4),
`flutter_build_apk_split_per_abi_test` (4),
`android_gradle_asset_merging_test` (4),
`android_run_flutter_gradle_plugin_tests_test` (2).
- `android_add_to_app_module_test` on `33f39876b92`: fails with `Check
failed.`. With `variant is LibraryVariant ||` removed: fails with "does
not contain 'assets/flutter_assets/AssetManifest.bin'".
- devicelab via `bin/test_runner.dart test --exit`, all `success`:
`module_host_with_custom_build_test`,
`module_custom_host_app_name_test`,
`build_android_host_app_with_module_source`, `build_aar_module_test`,
`build_android_host_app_with_module_aar`.
- Scratch module plus the devicelab host fixture (AGP 9.3.1, Gradle
9.5.0):
- Defaults: `assembleDemoStaging` has `kernel_blob.bin` and no
`libapp.so`; `assembleDemoRelease` has `libapp.so` for arm64-v8a,
armeabi-v7a and x86_64.
- Same with `android.compatibility.enableLegacyApi=false`, and with
`android.newDsl=true` added.

</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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants