Skip to content

Conversation

@simolus3
Copy link
Contributor

@simolus3 simolus3 commented Nov 22, 2025

To ensure build hooks emit code assets that are compatible with the main app, Flutter tools pass a CCompilerConfig toolchain configuration object to hooks. This is generally what we want, hooks on macOS should use the same clang from XCode as the one used to compile the app and native Flutter plugins for instance.

In some cases however, we need to run hooks without necessarily compiling a full Flutter app with native sources. A good example for this is flutter test, which runs unit / widget tests in a regular Dart VM without embedding it in a Flutter application. So since flutter test wouldn't invoke a native compiler, running build hooks shouldn't fail if the expected toolchain is missing.

Currently however, flutter test tries to resolve a compiler toolchain for the host platform. Doing that on Windows already allows not passing a CCompilerConfig if VSCode wasn't found, but on macOS and Linux, this crashes. This fixes the issue by allowing those methods to return null instead of throwing. They still throw by default, but for the test target they are configured to not pass a toolchain to hooks if none could be resolved. This means that hooks not invoking the provided toolchain (say because they're only downloading native artifacts instead) would now work, whereas previously flutter test would crash if no toolchian was found.

This closes #178715 (but only the part shared in the original issue description, @dcharkes suggested fixing a similar issue in the same PR but that is not done here).

@simolus3 simolus3 requested a review from a team as a code owner November 22, 2025 03:00
@github-actions github-actions bot added tool Affects the "flutter" command-line tool. See also t: labels. a: desktop Running on desktop team-ios Owned by iOS platform team labels Nov 22, 2025
Copy link
Contributor

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

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 modifies the build hooks to not require a native toolchain for unit tests, which is a great improvement for developers on macOS and Linux. The changes are logical and well-implemented by allowing toolchain discovery functions to return null instead of throwing errors when a toolchain isn't found for flutter test. The new tests are also comprehensive. I have a couple of suggestions to improve documentation and ensure consistent behavior across platforms.

Copy link
Contributor

@dcharkes dcharkes left a comment

Choose a reason for hiding this comment

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

🙏 🙏 @simolus3 ! 10/10 PR with such detailed comments and the tests! 🚀

@slaci
Copy link

slaci commented Nov 25, 2025

@simolus3 is there a way now (e.g. option) to force disable native asset compilation for flutter tests? If not, then having clang installed might have a negative impact on tests performance and they might behave differently depending on where they are run. At least if I understand correctly the native modules are compiled when the buildtools are found, aren't they?

@simolus3
Copy link
Contributor Author

is there a way now (e.g. option) to force disable native asset compilation for flutter tests?

There isn't, and (as mentioned in the issue) I don't think there should be one. If a package has a build hook, it can expect that hook to run whenever the package is used. So having an option to not run hooks would easily break packages (I know you want to mock the package anyways, but this still violates assumptions).

At least if I understand correctly the native modules are compiled when the buildtools are found, aren't they?

We still unconditionally invoke build hooks. It's just that if we don't find a compiler toolchain, we will let each hook worry about finding a compiler on its own (if it needs one). Previously, we'd immediately fail the build instead.

The sqlite3 package for instance does not do any native compilation by default, it downloads a precompiled binary instead. And since a native toolchain is not required since this PR, I imagine most other packages using hooks will adopt a similar approach.
If you want to skip the build for sqlite3 entirely, you can use something like this. But adding these escape hatches is the responsibility of a package author using hooks, not of the tool invoking them.

@slaci
Copy link

slaci commented Nov 26, 2025

There isn't, and (as mentioned in the issue) I don't think there should be one. If a package has a build hook, it can expect that hook to run whenever the package is used. So having an option to not run hooks would easily break packages (I know you want to mock the package anyways, but this still violates assumptions).

My problem is: when more packages start to use build hooks (see there are a couple of them already example projects), then running any flutter test will build all the assets no matter whether I use them or not (overhead; a benchmark would be nice to have). The assumption for testing is that no matter where/when I run tests, they must behave the same. If not, then the tests are flaky. Seems like native assets (so flutter 3.38) might make all tests flaky according to your explanation as they will compile and use native assets if they can, otherwise they just run without them (so the output depends on the runner env).

If I change the download url in your code by an invalid one (similar to when the right one is down or blocked), then running flutter test fails with Building native assets failed. See the logs for more details. even if I don't use your package, but it is added as a dependency. If the test still ran then it would have behaved differently than when the native assets were there. But currently as you can see, when the download fails, then I can't run any test in a project ⚠️

You may say caching the downloaded stuff would minimize the likelihood of this happening, but as I see outputDirectoryShared points to the project's .dart_tool directory instead of PUB_CACHE or something controllable. When running tests in CI, each job will trigger each packages' downloads everytime as caching a project directory is not that trivial.

The sqlite3 package for instance does not do any native compilation by default, it downloads a precompiled binary instead.

This looks like a very big security hole tbh. Npm has already been there. So now any package can start to download anything from the net (as I see there is no central repository for these; e.g. sqlite tries to download from Github, but this is up to the plugin authors what do they use and if they check checksums after downloading) and execute basically anything when building a flutter app or just running its tests without any visible indicator? Even transient dependencies can do this?


Sorry for being picky, but I just realized these things the hard way after upgrading a prod project to 3.38. I'm not blaming you (or anyone) for any of these. I'm not involved in the native assets/code_assets developments, so I can't really recommend good/optimal solutions for my problems, sadly all I can do is to highlight my concerns which may look rude (not intended), but I still want to make them transparent. I don't think this PR could/should fix the mentioned core issues, just saying that this PR only improves the situation if I intentionally don't install clang or other build tools, then I might be able to get the intended/recommended behavior for tests (except the non-transparent precompiled stuff downloading). In my opinion not trying to compile/download anything for tests would solve many problems. @stuartmorgan-g can I summon you here to share your thoughts on this please? I know you have a lot of experience with testing/CI and these things.

A breaking change entry is definitely missing for 3.38 regarding this native assets/hooks topic.

@dcharkes
Copy link
Contributor

There isn't, and (as mentioned in the issue) I don't think there should be one. If a package has a build hook, it can expect that hook to run whenever the package is used. So having an option to not run hooks would easily break packages (I know you want to mock the package anyways, but this still violates assumptions).

Please continue the discussion on a relevant issue:

My problem is: when more packages start to use build hooks (see there are a couple of them already example projects), then running any flutter test will build all the assets no matter whether I use them or not (overhead; a benchmark would be nice to have). The assumption for testing is that no matter where/when I run tests, they must behave the same. If not, then the tests are flaky. Seems like native assets (so flutter 3.38) might make all tests flaky according to your explanation as they will compile and use native assets if they can, otherwise they just run without them (so the output depends on the runner env).

If I change the download url in your code by an invalid one (similar to when the right one is down or blocked), then running flutter test fails with Building native assets failed. See the logs for more details. even if I don't use your package, but it is added as a dependency. If the test still ran then it would have behaved differently than when the native assets were there. But currently as you can see, when the download fails, then I can't run any test in a project ⚠️

At the risk of repeating myself. This was already the situation with plugins when doing flutter build and flutter run. This merely makes flutter test consistent with the other commands relieving the need for flutter drive and relieving the need to even stub dependencies out when they can run on the host.

You may say caching the downloaded stuff would minimize the likelihood of this happening, but as I see outputDirectoryShared points to the project's .dart_tool directory instead of PUB_CACHE or something controllable. When running tests in CI, each job will trigger each packages' downloads everytime as caching a project directory is not that trivial.

The final goal is to have the assets hosted on pub as well:

In the meantime, package authors can use the approaches listed in:

The sqlite3 package for instance does not do any native compilation by default, it downloads a precompiled binary instead.

This looks like a very big security hole tbh. Npm has already been there. So now any package can start to download anything from the net (as I see there is no central repository for these; e.g. sqlite tries to download from Github, but this is up to the plugin authors what do they use and if they check checksums after downloading) and execute basically anything when building a flutter app or just running its tests without any visible indicator? Even transient dependencies can do this?

This was already the case with Flutter plugins, you can shell out to arbitrary scripts from the Gradle/CocoaPods/SPM build scripts. (And various packages were doing this to run shell scripts to download prebuilt dylibs.) Build hooks equalize the flutter test to be consistent with flutter build and flutter run.

If you have specific feature requests please file them on dart-lang/native where most of the shared code (between Dart and Flutter) for build hooks lives.

Let's keep the discussion on this issue to the issue that this PR is solving, we are unanimous that this PR is an improvement over the pre-PR situation!

@stuartmorgan-g
Copy link
Contributor

@stuartmorgan-g can I summon you here to share your thoughts on this please?

This PR seems reasonable to me; unconditionally failing due to lack of a tool we are likely not to need is clearly not ideal behavior.

I'm happy to discuss the general topic further on a relevant issue.

A breaking change entry is definitely missing for 3.38 regarding this native assets/hooks topic.

That's also not related to this PR, but to save the trouble of filing a new issue that will be closed: adding new, opt-in functionality is by definition not a breaking change. There might need to be more guidance to package authors about how to adopt it without affecting clients, but that's not the same thing.

@auto-submit auto-submit bot added this pull request to the merge queue Nov 26, 2025
Merged via the queue into flutter:master with commit 43d438f Nov 27, 2025
140 of 141 checks passed
@flutter-dashboard flutter-dashboard bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Nov 27, 2025
@simolus3 simolus3 deleted the native-assets-test branch November 27, 2025 04:20
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 27, 2025
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Nov 27, 2025
flutter/flutter@7b98d50...022b155

2025-11-27 [email protected] Make tree green again by fixing lints (flutter/flutter#179186)
2025-11-26 [email protected] Add shared Darwin implementation for plugins (flutter/flutter#176495)
2025-11-26 [email protected] Fixed changing supportedLocales fails to update the locale (flutter/flutter#178526)
2025-11-26 [email protected] [ Tool ] Remove --no-sandbox when launching web apps on Chrome device (flutter/flutter#178670)
2025-11-26 [email protected] Build hooks: Don't require toolchain for unit tests (flutter/flutter#178954)
2025-11-26 [email protected] Fix a small typo in `HandlerCompat.java` docs (flutter/flutter#178595)
2025-11-26 [email protected] Roll Fuchsia Linux SDK from nzuAxCJGeJbkZCTkr... to _e9MNK4nfBOrERVP_... (flutter/flutter#179130)
2025-11-26 [email protected] Make sure that a CupertinoActivityIndicator doesn't crash in 0x0 envi… (flutter/flutter#178565)
2025-11-26 [email protected] [native assets] Bump minimum iOS version from 12 to 13 (flutter/flutter#179079)
2025-11-26 [email protected] Documents and fixes behavior when clipping background filter fragment shader (flutter/flutter#178940)
2025-11-26 [email protected] Replace the hardcoded link with an actual link tag in `PlatformChannel.java‎` docs (flutter/flutter#178588)
2025-11-26 [email protected] Roll Packages from cc3dca6 to 5d8d954 (5 revisions) (flutter/flutter#179140)
2025-11-26 [email protected] Add more templates that the UIScene migrator can match against (flutter/flutter#179044)
2025-11-26 [email protected] Remove deprecated activeColor in `dynamic_content_color.0.dart`‎ example (flutter/flutter#178961)
2025-11-26 [email protected] Remove deprecated `activeColor` in `decorated_sliver.1.dart‎` example (flutter/flutter#178959)
2025-11-26 [email protected] Small refactor in `PlayStoreDeferredComponentManager.java‎` (flutter/flutter#178586)
2025-11-26 [email protected] Roll Dart SDK from 9e605f706cdf to 1d8dc04bd1d7 (4 revisions) (flutter/flutter#179080)
2025-11-26 [email protected] Fix error when generating pt_BR localizations (flutter/flutter#175832)
2025-11-26 [email protected] Roll Fuchsia Test Scripts from JUeFbA8y0E-_pj-bg... to MHF-UAfO6sVKqSEYk... (flutter/flutter#179151)
2025-11-26 [email protected] Add flutter_lints to samples (flutter/flutter#179091)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
@dcharkes dcharkes added the cp: merge-to-stable Cherry-picks that should be merged to stable label Nov 28, 2025
@dcharkes dcharkes added cp: merge-to-beta Cherry-picks that should be merged to beta cp: beta cherry pick this pull request to beta release candidate branch cp: stable cherry pick this pull request to stable release candidate branch and removed cp: merge-to-stable Cherry-picks that should be merged to stable cp: merge-to-beta Cherry-picks that should be merged to beta labels Nov 28, 2025
@flutteractionsbot
Copy link

Failed to create CP due to merge conflicts.
You will need to create the PR manually. See the cherrypick wiki for more info.

flutteractionsbot pushed a commit to flutteractionsbot/flutter that referenced this pull request Nov 28, 2025
To ensure build hooks emit code assets that are compatible with the main
app, Flutter tools pass a `CCompilerConfig` toolchain configuration
object to hooks. This is generally what we want, hooks on macOS should
use the same `clang` from XCode as the one used to compile the app and
native Flutter plugins for instance.

In some cases however, we need to run hooks without necessarily
compiling a full Flutter app with native sources. A good example for
this is `flutter test`, which runs unit / widget tests in a regular Dart
VM without embedding it in a Flutter application. So since `flutter
test` wouldn't invoke a native compiler, running build hooks shouldn't
fail if the expected toolchain is missing.

Currently however, `flutter test` tries to resolve a compiler toolchain
for the host platform. Doing that on Windows already allows not passing
a `CCompilerConfig` if VSCode wasn't found, but on macOS and Linux, this
crashes. This fixes the issue by allowing those methods to return `null`
instead of throwing. They still throw by default, but for the test
target they are configured to not pass a toolchain to hooks if none
could be resolved. This means that hooks not invoking the provided
toolchain (say because they're only downloading native artifacts
instead) would now work, whereas previously `flutter test` would crash
if no toolchian was found.

This closes flutter#178715 (but only
the part shared in the original issue description, @dcharkes suggested
fixing a similar issue in the same PR but that is _not_ done here).
mboetger pushed a commit to mboetger/flutter that referenced this pull request Dec 2, 2025
To ensure build hooks emit code assets that are compatible with the main
app, Flutter tools pass a `CCompilerConfig` toolchain configuration
object to hooks. This is generally what we want, hooks on macOS should
use the same `clang` from XCode as the one used to compile the app and
native Flutter plugins for instance.

In some cases however, we need to run hooks without necessarily
compiling a full Flutter app with native sources. A good example for
this is `flutter test`, which runs unit / widget tests in a regular Dart
VM without embedding it in a Flutter application. So since `flutter
test` wouldn't invoke a native compiler, running build hooks shouldn't
fail if the expected toolchain is missing.

Currently however, `flutter test` tries to resolve a compiler toolchain
for the host platform. Doing that on Windows already allows not passing
a `CCompilerConfig` if VSCode wasn't found, but on macOS and Linux, this
crashes. This fixes the issue by allowing those methods to return `null`
instead of throwing. They still throw by default, but for the test
target they are configured to not pass a toolchain to hooks if none
could be resolved. This means that hooks not invoking the provided
toolchain (say because they're only downloading native artifacts
instead) would now work, whereas previously `flutter test` would crash
if no toolchian was found.

This closes flutter#178715 (but only
the part shared in the original issue description, @dcharkes suggested
fixing a similar issue in the same PR but that is _not_ done here).
auto-submit bot pushed a commit that referenced this pull request Dec 2, 2025
)

### Issue Link:

#178954

(beta CP: #179211)

### Impact Description:

Running `flutter test` on Linux  or MacOS which does not have a `clang` starts failing on projects that target only Android otherwise (and do not require a Linux or MacOS toolchain) when there are build hooks anywhere in the dependencies.

This PR changes the behavior for `flutter test` to only pass `clang` and friends if they are installed. This means that _if_ a Linux/MacOS toolchain is installed on the system, it will be used for both `flutter run -d Linux`/`MacOS` and `flutter test` (so that the same native compiler is used), but if it's not installed on the system both `flutter run -d AndroidDevice` and `flutter test` run successfully.

This means end users don't have to go modify their systems/CIs to install Linux/MacOS toolchains for Android projects.

### Changelog Description:

[flutter/178954] Fixes running `flutter test` on Linux/MacOS for Android projects with build hooks without the desktop native tooling installed.

### Workaround:

All end users have to install `clang` and `ldd` on Linux/MacOS hosts and CI bots.

### Risk:
What is the risk level of this cherry-pick?

It changes the flow in Flutter tools from throwing an error to returning a nullable value.

### Test Coverage:
Are you confident that your fix is well-tested by automated tests?

### Validation Steps:

On a Linux  and MacOS system, make a Flutter app that only targets Android. Add a build hook. And ensure there's no `clang` on the `PATH` (Can be verified by `flutter doctor -v` finding no tooling for Flutter desktop). Run `flutter test` and it shouldn't fail due to `clang` not being installed. For MacOS, XCode needs to not be installed.
reidbaker pushed a commit to AbdeMohlbi/flutter that referenced this pull request Dec 10, 2025
To ensure build hooks emit code assets that are compatible with the main
app, Flutter tools pass a `CCompilerConfig` toolchain configuration
object to hooks. This is generally what we want, hooks on macOS should
use the same `clang` from XCode as the one used to compile the app and
native Flutter plugins for instance.

In some cases however, we need to run hooks without necessarily
compiling a full Flutter app with native sources. A good example for
this is `flutter test`, which runs unit / widget tests in a regular Dart
VM without embedding it in a Flutter application. So since `flutter
test` wouldn't invoke a native compiler, running build hooks shouldn't
fail if the expected toolchain is missing.

Currently however, `flutter test` tries to resolve a compiler toolchain
for the host platform. Doing that on Windows already allows not passing
a `CCompilerConfig` if VSCode wasn't found, but on macOS and Linux, this
crashes. This fixes the issue by allowing those methods to return `null`
instead of throwing. They still throw by default, but for the test
target they are configured to not pass a toolchain to hooks if none
could be resolved. This means that hooks not invoking the provided
toolchain (say because they're only downloading native artifacts
instead) would now work, whereas previously `flutter test` would crash
if no toolchian was found.

This closes flutter#178715 (but only
the part shared in the original issue description, @dcharkes suggested
fixing a similar issue in the same PR but that is _not_ done here).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: desktop Running on desktop cp: beta cherry pick this pull request to beta release candidate branch cp: stable cherry pick this pull request to stable release candidate branch team-ios Owned by iOS 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.

Running flutter tests on linux might require clang and lld even if the app does not target linux since 3.38

6 participants