Skip to content

[flutter_tools] Provision Android NDK in the main Gradle invocation#186337

Merged
auto-submit[bot] merged 19 commits into
flutter:masterfrom
kyungilcho:draft/android-ndk-single-invocation
Jul 7, 2026
Merged

[flutter_tools] Provision Android NDK in the main Gradle invocation#186337
auto-submit[bot] merged 19 commits into
flutter:masterfrom
kyungilcho:draft/android-ndk-single-invocation

Conversation

@kyungilcho

Copy link
Copy Markdown
Contributor

Summary

  • move Android NDK provisioning into the main Gradle invocation
  • pass the sdkmanager path, Android SDK root, and validated installed NDK versions from flutter_tools to the Flutter Gradle Plugin
  • only fall back to the synthetic externalNativeBuild/CMake path when the main invocation cannot satisfy provisioning

Context

This is a draft to explore the single-invocation direction discussed after #183555 and #185439.

The goal here is to keep the cleaner shape of doing the NDK check where AGP has already resolved the actual ndkVersion, while also tightening the behavior around installation and fallback.

Design

  • AndroidGradleBuilder now receives AndroidSdk explicitly instead of reading it from globals
  • flutter_tools passes:
    • flutter.androidSdkRoot
    • flutter.installedNdkVersions
    • flutter.sdkManagerPath when installation is actually possible
  • the plugin now:
    • returns early when the resolved NDK is already installed
    • installs the resolved NDK with sdkmanager --sdk_root=... when possible
    • verifies source.properties after installation
    • falls back to the synthetic CMake path only when the tool-provided path cannot handle the build

Validation

  • packages/flutter_tools/test/general.shard/android/android_gradle_builder_test.dart
  • packages/flutter_tools/gradle: FlutterPluginUtilsTest
  • local benchmarking still shows the same structural improvement as the earlier prototype:
    • no extra printNdkVersion Gradle invocation
    • no unexpected .cxx output in the fast path

Notes

This is still a draft because I want feedback on the shape before turning it into the replacement for the narrower flavored-only reland.

@github-actions github-actions Bot added platform-android Android applications specifically tool Affects the "flutter" command-line tool. See also t: labels. team-android Owned by Android platform team labels May 11, 2026
@kyungilcho

Copy link
Copy Markdown
Contributor Author

@gmackall I pushed an updated draft in the direction you suggested.

The current shape keeps the single-invocation approach, passes the sdkmanager / SDK root / installed-NDK information through the main Gradle invocation, and tightens a few of the rougher edges from my first pass.

I reran analyze, android_gradle_builder_test.dart, FlutterPluginUtilsTest, and a source-level plain release build locally. On that plain build, the extra metadata invocation is gone, build/.cxx no longer gets created, and ./gradlew -q printNdkVersion is working again.

At this point I think the main build path is in a pretty good place, but there are still two scope questions I would like your take on before I keep pushing it further:

  1. Should the same provisioning contract also be applied to non-build Gradle invocations like getBuildVariants / outputsAppLinkSettings, so they do not still have a path back to synthetic .cxx?

  2. Is it acceptable to keep the synthetic externalNativeBuild path as a compatibility fallback when tool-driven provisioning cannot handle the build, or were you aiming for something stricter than that?

If the bar is "main build path is fixed and fallback remains for compatibility", I think this draft is getting close. If the bar is broader than that, I think there is still another step.

@gmackall gmackall added the CICD Run CI/CD label May 15, 2026
@flutter-dashboard

Copy link
Copy Markdown

This pull request is not mergeable in its current state, likely because of a merge conflict. Pre-submit CI jobs were not triggered. Pushing a new commit to this branch that resolves the issue will result in pre-submit jobs being scheduled.

@gmackall

Copy link
Copy Markdown
Member
  1. Should the same provisioning contract also be applied to non-build Gradle invocations like getBuildVariants / outputsAppLinkSettings, so they do not still have a path back to synthetic .cxx?

Hmm I would just follow the current pattern here. From glancing at it my understanding is that they simply don't rely on the ndk being present, and so are independent. Is that also your understanding? I believe we only use the ndk for the previously mentioned purpose of allowing AGP to take care of stripping debug symbols.

  1. Is it acceptable to keep the synthetic externalNativeBuild path as a compatibility fallback when tool-driven provisioning cannot handle the build, or were you aiming for something stricter than that?

I was thinking of leaving the synthetic path as a fallback for now. I think abstractly we should be able to move to the strict path in the future, based on what we can ensure we have access to from the doctor step, but I also would want to do that in a follow up pr once I have a sense that people aren't hitting issues with the new main path.

@github-actions github-actions Bot removed the CICD Run CI/CD label May 18, 2026
@kyungilcho
kyungilcho marked this pull request as ready for review May 19, 2026 06:13
@kyungilcho
kyungilcho requested a review from a team as a code owner May 19, 2026 06:13
@kyungilcho
kyungilcho requested review from reidbaker and removed request for a team May 19, 2026 06:13

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces tool-side Android NDK provisioning, enabling the Flutter tool to detect and install required NDK versions using sdkmanager prior to Gradle's native build execution. The changes include a new printNdkVersion task, refactored NDK resolution logic, and the passing of SDK metadata through Gradle properties. Feedback recommends ensuring NDK installation respects Gradle's offline mode, refactoring file path construction for clarity, and utilizing the new NDK version helper to ensure compatibility across various Android Gradle Plugin versions.

Comment on lines +780 to +781
val sdkManagerPath = toolNdkProvisioningProperties.sdkManagerPath ?: return false
val execOps = gradleProject.serviceOf<ExecOperations>()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Running sdkmanager to install the NDK involves network access. This should respect Gradle's offline mode to avoid build failures when the user explicitly requests an offline build.

        val sdkManagerPath = toolNdkProvisioningProperties.sdkManagerPath ?: return false
        if (gradleProject.gradle.startParameter.isOffline) {
            return false
        }
        val execOps = gradleProject.serviceOf<ExecOperations>()

Comment on lines +793 to +797
val installedNdkMarker =
File(
File(File(toolNdkProvisioningProperties.androidSdkRoot, "ndk"), configuredNdkVersion),
"source.properties"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The nested File constructor calls can be simplified for better readability by using the File(parent, child) constructor with a combined path string.

Suggested change
val installedNdkMarker =
File(
File(File(toolNdkProvisioningProperties.androidSdkRoot, "ndk"), configuredNdkVersion),
"source.properties"
)
val installedNdkMarker = File(toolNdkProvisioningProperties.androidSdkRoot, "ndk/$configuredNdkVersion/source.properties")

Comment on lines +994 to +1001
val androidExtension = getAndroidApplicationExtension(project)
project.tasks.register(TASK_PRINT_NDK_VERSION) {
description = "Prints out the configured ndkVersion for this Android project"
doLast {
val configuredNdkVersion = androidExtension.ndkVersion
println("$NDK_VERSION_OUTPUT_PREFIX$configuredNdkVersion")
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using getConfiguredNdkVersion(project) is safer and more consistent than accessing androidExtension.ndkVersion directly. It handles both the modern ApplicationExtension and the legacy BaseExtension while using findByType to avoid potential exceptions on older AGP versions where the new extension might not be available.

        project.tasks.register(TASK_PRINT_NDK_VERSION) {
            description = "Prints out the configured ndkVersion for this Android project"
            doLast {
                val configuredNdkVersion = getConfiguredNdkVersion(project)
                println("$NDK_VERSION_OUTPUT_PREFIX$configuredNdkVersion")
            }
        }

@gmackall gmackall added the CICD Run CI/CD label May 19, 2026
@github-actions github-actions Bot removed the CICD Run CI/CD label May 21, 2026
@reidbaker reidbaker added the CICD Run CI/CD label May 21, 2026
@reidbaker
reidbaker requested a review from gmackall May 21, 2026 20:13
@github-actions github-actions Bot removed the CICD Run CI/CD label May 25, 2026
reidbaker
reidbaker previously approved these changes May 26, 2026
@gmackall gmackall added the autosubmit Merge PR when tree becomes green via auto submit App label Jul 6, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Jul 6, 2026
@auto-submit

auto-submit Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/186337, because This PR has not met approval requirements for merging. The PR author is not a member of flutter-hackers and needs 1 more review(s) in order to merge this PR.

  • Merge guidelines: A PR needs at least one approved review if the author is already part of flutter-hackers or two member reviews if the author is not a member of flutter-hackers before re-applying the autosubmit label. Reviewers: If you left a comment approving, please use the "approve" review action instead.

@gmackall
gmackall requested a review from reidbaker July 6, 2026 20:51
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Jul 6, 2026
@gmackall gmackall added the CICD Run CI/CD label Jul 6, 2026
@camsim99 camsim99 added the autosubmit Merge PR when tree becomes green via auto submit App label Jul 7, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Jul 7, 2026
Merged via the queue into flutter:master with commit 29be279 Jul 7, 2026
178 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Jul 7, 2026
auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Jul 10, 2026
…12169)

Manual roll Flutter from 91939cc4db78 to dc2a8703e12b (50 revisions)

Manual roll requested by [email protected]

flutter/flutter@91939cc...dc2a870

2026-07-09 [email protected] [ios,macos] Update swiftc.py flags to match swiftc (flutter/flutter#189174)
2026-07-09 [email protected] [AGP 9] Update Warn Version to AGP 9+ (flutter/flutter#189109)
2026-07-09 [email protected] Sync CHANGELOG.md from stable (flutter/flutter#189203)
2026-07-09 [email protected] [web] Roll Chrome to 145 (framework) (flutter/flutter#182861)
2026-07-09 [email protected] Roll Packages from 52d84d6 to 20928d5 (6 revisions) (flutter/flutter#189194)
2026-07-09 [email protected] [web] Avoid absolute positioning for base CanvasKit canvas (flutter/flutter#188337)
2026-07-09 [email protected] Roll Dart SDK from cdb7217e65aa to a11fb7ed40a5 (6 revisions) (flutter/flutter#189195)
2026-07-09 [email protected] Fix dereference of nullptr in the moved-to-rect signal in the Linux embedder (flutter/flutter#189152)
2026-07-09 [email protected] Fix data for design packages (flutter/flutter#189140)
2026-07-09 [email protected] Roll Skia from 7b42d1251d54 to ab3a7b98c94d (2 revisions) (flutter/flutter#189181)
2026-07-09 [email protected] Roll Skia from 05d9d214e0b7 to 7b42d1251d54 (2 revisions) (flutter/flutter#189175)
2026-07-09 [email protected] Roll Skia from 542c8bdd7f4f to 05d9d214e0b7 (4 revisions) (flutter/flutter#189169)
2026-07-09 [email protected] UberSDF rect handling for thin (line-like) rectangles (flutter/flutter#188821)
2026-07-09 [email protected] Roll Skia from dd572c07f63c to 542c8bdd7f4f (4 revisions) (flutter/flutter#189160)
2026-07-09 [email protected] [flutter_tools] Fix hot restart for WASM web builds (flutter/flutter#187898)
2026-07-08 [email protected] Split FlViewRenderer into OpenGL and software backends (flutter/flutter#188824)
2026-07-08 [email protected] Promote android_hardware_smoke_tests out of bringup in CI (flutter/flutter#189081)
2026-07-08 [email protected] Roll Skia from 8df24be66531 to dd572c07f63c (4 revisions) (flutter/flutter#189150)
2026-07-08 [email protected] Expose LinuxWindowRegistrar on _window_linux.dart in order to better support out of tree LinuxWindowingOwners (flutter/flutter#188917)
2026-07-08 [email protected] Roll pub packages (flutter/flutter#189149)
2026-07-08 [email protected] fix(ci): harden some workflows (flutter/flutter#189087)
2026-07-08 [email protected] Roll Skia from 51a62da33da0 to 8df24be66531 (1 revision) (flutter/flutter#189139)
2026-07-08 [email protected] Roll Dart SDK to Dart 3.13 beta3 (flutter/flutter#189122)
2026-07-08 [email protected] [flutter_tools] Don't crash on non-UTF-8 plugin pubspec.yaml (flutter/flutter#188976)
2026-07-08 [email protected] [flutter_tools] Watch transitive #include headers for FragmentProgram hot reload (flutter/flutter#187945)
2026-07-08 [email protected] Roll Skia from 040d9f55de00 to 51a62da33da0 (1 revision) (flutter/flutter#189135)
2026-07-08 [email protected] Roll Packages from 92525f5 to 52d84d6 (7 revisions) (flutter/flutter#189134)
2026-07-08 [email protected] [flutter_tools] Forcefully kill hung subprocesses 5 seconds after timeout (flutter/flutter#187178)
2026-07-08 [email protected] Expose the app's build name and number as compile-time constants (flutter/flutter#187935)
2026-07-08 [email protected] Roll Skia from 1ff92f879815 to 040d9f55de00 (1 revision) (flutter/flutter#189131)
2026-07-08 [email protected] Roll Skia from 6137414bef5c to 1ff92f879815 (6 revisions) (flutter/flutter#189126)
2026-07-08 [email protected] [test cross imports] More test/rendering + flutter_test/test fixes (flutter/flutter#188954)
2026-07-08 [email protected] engine: explain why each candidate build was skipped in Flutter web loader (flutter/flutter#186254)
2026-07-08 [email protected] vscode: add missing unicode.h (flutter/flutter#189102)
2026-07-08 [email protected] Roll Fuchsia Linux SDK from 7RjQJBW3m-3Jl-7jr... to QcRFUtvCw2EobfJ8s... (flutter/flutter#189104)
2026-07-08 [email protected] Roll Skia from 075fbe4778d9 to 6137414bef5c (10 revisions) (flutter/flutter#189106)
2026-07-08 [email protected] engine: warn on WASM load failure when not crossOriginIsolated (flutter/flutter#186252)
2026-07-08 [email protected] Roll Dart SDK from c9bccc09e733 to db2155f56bf3 (2 revisions) (flutter/flutter#189105)
2026-07-08 [email protected] [flutter_tools] Prevent interactive device selection in machine mode (flutter/flutter#188267)
2026-07-08 [email protected] [flutter_tools] Fix wireless ADB device discovery when serial contains spaces (flutter/flutter#187943)
2026-07-07 [email protected] [web] Fix grouped autofill on iOS Chrome (flutter/flutter#187459)
2026-07-07 [email protected] Fix TextSelectionOverlay crash when layout is degenerate (flutter/flutter#188672)
2026-07-07 [email protected] [flutter_tools] Provision Android NDK in the main Gradle invocation (flutter/flutter#186337)
2026-07-07 [email protected] Android_hardware_smoke_test: Migrate to AGP 9 (flutter/flutter#189082)
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD platform-android Android applications specifically team-android Owned by Android platform team tool Affects the "flutter" command-line tool. See also t: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants