Skip to content

[TEST] test PR - #196005

Draft
chingjun wants to merge 593 commits into
flutter:masterfrom
chingjun:bazel-migration/phase0
Draft

chingjun wants to merge 593 commits into
flutter:masterfrom
chingjun:bazel-migration/phase0

Conversation

@chingjun

@chingjun chingjun commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@chingjun chingjun added the CICD Run CI/CD label Nov 4, 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.

@github-actions github-actions Bot added a: tests "flutter test", flutter_test, or one of our tests a: text input Entering text in a text field or keyboard related problems platform-android Android applications specifically platform-ios iOS applications specifically tool Affects the "flutter" command-line tool. See also t: labels. framework flutter/packages/flutter repository. See also f: labels. engine flutter/engine related. See also e: labels. a: animation Animation APIs p: material_ui material_ui package in flutter/packages a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) platform-macos Building on or for macOS specifically a: internationalization Supporting other languages or locales. (aka i18n) platform-windows Building on or for Windows specifically d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos f: gestures flutter/packages/flutter/gestures repository. platform-web Web applications specifically platform-linux Building on or for Linux specifically a: desktop Running on desktop f: integration_test The flutter/packages/integration_test plugin e: impeller Impeller rendering backend issues and features requests team-android Owned by Android platform team team-ios Owned by iOS platform team team-web Owned by Web platform team flutter-gpu team-macos Owned by the macOS platform team team-windows Owned by the Windows platform team team-linux Owned by the Linux platform team labels Nov 4, 2026
…the repository cache

The Mac engine and test builders' times on Bazel against GN's, the
linux_unopt drones' (the Robolectric suite's three shapes: one at a time,
six abreast, six abreast with C1 and the serial collector), the 338/338
presubmit at 531; among what a recipe change would add, the repository
cache in the named cache (every Java drone re-downloads 2.2 GB of Maven
files today) and the measured case against remote execution of the
Robolectric tests.
standalone/linux_license.json (the presubmit's `Linux linux_license`)
was the one Linux builder of its kind left on GN, for a flag the
converter did not know: --enable-minimal-linux, BUILDCONFIG.gn's
is_minimal_linux, under which GN skips its pkg-config lookups for the
Linux embedding's libraries on a bot without the desktop's development
packages. The Bazel build runs no pkg-config -- those libraries come from
its sysroot -- so the flag asks for what it has already: the converter
takes it as assumed, like --use-glfw-swiftshader.

The builder file: --bazel and download_bazelisk, --no-lto as the other
two licenses builds have (linux_unopt's ci/host_release_licenses, the
Mac's), so that the same configuration serves, and the test runner as
`run_tests.py --type binaries` instead of the executable from the build
directory (517's guard). Generated: `build:licenses` in configs.bazelrc,
the :licenses install's configuration, builds.json (126 of 126 builds
with a configuration). Checked: converter tests, check_build_configs.sh,
engine_build_configs, bazel_ci_test, Bazel analysis of
--config=licenses :licenses and :licenses_tests.
Under --config=rbe_mac the CppCompile strategy names the workers first,
which took the wasm32 toolchain's compiles along with the macOS and iOS
ones: em++.sh then ran the host's Python on a Linux worker -- a Mach-O
binary from the Mac's synced @cpython3 ("cannot execute binary file:
Exec format error", exit 1, so no local fallback) -- and 564 of the
engine wildcard's targets failed to build on the Mac tester's sweep
(T80; the 329 that built all passed). A Linux host's SDK and Python are
Linux binaries and run on the workers, which is how linux_web_engine
builds ci/wasm_release in six minutes; that stays.

emscripten_cc_toolchain_config gains host_tools_run_remotely (default
True); the toolchain's BUILD sets it to HOST_OS == "linux", and with it
false the compiler, linker and archiver actions carry no-remote-exec,
as the Apple toolchains' links do under the remote-compile mode.
Diagnosis and the fix's shape from the Mac tester's report.
…ines are bin/cache's

A tool test's SDK facsimile under --config=prebuilt_engine laid bin/cache's
engine directories but stamped them as the tree's build does: engine.stamp,
engine_stamp.json, flutter.version.json and each artifact set's stamp
with the checkout's HEAD (@engine_version). On a bot the two agree (the
bot's bin/cache holds the commit's own engine); on a developer's machine
bin/cache holds the content hash's build, stamped with the commit that
built it, and macos_content_validation_test found the framework's
FlutterEngine revision differing from engine_stamp.json's (the Mac tester's
T78/T79). The iOS engines, meanwhile, came from the tree under either
configuration: the apple shard built them -- hours the first time -- even
with `flutter precache --ios` done.

Now, under the prebuilt engine:
- @flutter_bin_cache//:stamps (new): bin/cache's *.stamp, engine_stamp.json,
  flutter.version.json and engine.realm as the tool left them, laid through
  `cache` with the engine's directories. The facsimile's bookkeeping says
  what its artifacts are, and a set the tool has not fetched has no stamp,
  so the tool fetches it as the checkout's would. The realm stays explicit
  too (the Gradle plugin reads the file unconditionally).
- The iOS engine directories on a Mac from bin/cache
  (@flutter_bin_cache//:ios_engine{,_profile,_release}, new, through
  //build_defs/engine:prebuilt_ios_engine_*_directory -- files_directory,
  empty where not precached) instead of the tree's.
- The Android gen_snapshots still come from the tree; their stamp says
  bin/cache's revision (//bin/internal:prebuilt_android_sdk_stamp, from
  @engine_version's new PREBUILT_ENGINE_VERSION = bin/cache/engine.stamp,
  watched; empty without one) so that the tool takes them as current.
- The tree's facsimile is unchanged: _TOOL_CACHE_FILES_BASE keeps the
  versioned stamps; the common files (the tool's snapshot, the realm, the
  fonts' and Gradle wrapper's pins, the iOS USB stamps) are
  _TOOL_CACHE_FILES_COMMON, which the prebuilt side takes alone.

The stamps taken are the engine's and its artifact sets' alone: the iOS
USB artifacts' stamps stay the facsimile's own, since on Linux the tool
lays no such artifacts and rewrites those stamps (a bin/cache stamp there
is a read-only file: the first run of this change), and the fonts', the
Gradle wrapper's and the Android gen_snapshots' the facsimile writes.

On the bots nothing changes in value: bin/cache's stamps there are the
commit the shards run at. Checked here with bin/cache at this commit's own
engine (`flutter precache --linux --web` under FLUTTER_PREBUILT_ENGINE_
VERSION and the archives realm): the integration shard's analyze_once,
flutter_run and hot_reload tests and the desktop shard's
build_linux_config_only pass under --config=prebuilt_engine, the
facsimile's cache carrying bin/cache's stamps; Bazel analysis of the tool
suites under both configurations. On a Mac with `flutter precache --macos
--ios` the apple shard runs from bin/cache alone, and both content
validation tests compare like with like (the Mac tester's next task).
…est targets compile on the workers too

darwin/common's test_config gives the test targets two compile flags
that name the host's Xcode by absolute path --
`-F<platform>/Developer/Library/Frameworks` (XCTest, Testing) and
`-I<platform>/Developer/usr/lib` -- so 526 marked those rules
`no-remote-exec` and their compiles stayed on the Mac under the Apple
toolchains' remote-compile mode (a worker has no Xcode: `'XCTest/XCTest.h'
file not found`).

@macos_sdk now lays each platform's Developer/Library/Frameworks (the
frameworks symlinked one by one) and Developer/usr/lib (whole) as views
beside the SDK's -- <view>.platform/Developer/... -- and a
`platform_developer` rule (platform_developer.bzl, generated into the
repository) hands a dependent's compiles the same `-F` and `-I` on the
views as exec-root paths, with the directories as inputs (source
directories, tracked as the SDK view is): `:macos_developer`,
`:ios_developer`, `:ios_simulator_developer`; off a Mac, or on an Xcode
without a platform's test support, an empty cc_library of the name, so
that the generated files load the same everywhere. The emitter turns the
two flags into a dependency on the platform's target under the flags'
own conditions (render's last pass; `_platformDeveloperTarget`), and
keeps `no-remote-exec` only where a Swift module's swiftflags still name
the Xcode (the swift rules take no CcInfo for `-F`; Swift compiles run
on the Mac in any case). The links keep their absolute `-F`/`-L`/rpath:
they run on the Mac.

Regenerated darwin/common, darwin/ios, darwin/macos (15 rules lose the
tag, 3 Swift modules keep it). Checked: the converter's tests (the
emitter test rewritten for the dependency), `--check`, Bazel analysis of
the Darwin packages on Linux through the stubs. The Mac tester verifies
the compiles on the workers (the iOS simulator unopt and the macOS
host_debug_unopt test builds under --config=rbe_mac).

Also: licenses_cpp_testrunner is `large` (bazel_mapping.dart testSizes):
the Mac tester's T81 run (891 pass / 2 fail at 756360f) had it time
out at medium's 300 s while a whole wildcard suite competed for the
machine; the suite takes ~40 s alone on Linux and a few minutes under
load, within large's 900 s.
…: ci/check_generated_bazel_files.sh

Nothing on the bots ran `gn_to_bazel --check`: a BUILD.gn or builder-file
change could land without its regeneration and the generated files would
drift until someone ran the converter. `ci/check_generated_bazel_files.sh`
runs the check -- every generated BUILD.bazel against its BUILD.gn, the
mirrored lists, ci/builders/BUILD.bazel + configs.bazelrc and
build/bazel/builds.json against the builder files, lib/web_ui/felt_config.bzl
against felt_config.yaml -- on the checkout's prebuilt Dart SDK, with the
DEPS gn binary and without buildifier, as a step of linux_unopt's first
build beside "Check build configs" (the recipe runs a script step as-is on a
Bazel builder; the converter's own BUILD.bazel lists the step among those
the Bazel build has no test for, since the check reads the whole GN tree).
About a minute on a bot (32 s of CPU here).

For the check to run without buildifier the emitter's output has to be
buildifier's: the 112 generated files and felt_config.bzl were, the builders'
BUILD.bazel was not -- its suites wrote `//engine/src/flutter/testing/dart:dart`
where buildifier writes `//engine/src/flutter/testing/dart`. `buildifierLabel`
(bazel_mapping.dart) drops a name that repeats the package's last segment (and
`@repo//:repo`'s), and the suites' labels go through it; `--check
--buildifier=none` now reports everything current.

README (converter) and ci.md name the step.
…droid as three device tests

linux_android_emulator_tests was the last Linux engine builder on GN by
design: `run_tests.py --type android` had no Bazel counterpart. Its build
(ci/android_emulator_debug_x64) had a configuration and an install (the
APK) since the Android phase; what it lacked was running the three suites
run_android_tests pushes to the emulator -- flutter_shell_native_unittests,
impeller_toolkit_android_unittests, impeller_vulkan_android_unittests --
which the converter emitted as plain cc_tests that `bazel test` would have
tried to run on the host.

flutter_android_device_test (build_defs/testing/android_device_test.bzl)
is run_android_unittest step for step: a cc_binary of the Android target
configuration (`<name>_binary`) and a host launcher that `adb push`es it to
/data/local/tmp/<name>, runs it there whole with no arguments and no
environment, takes adb's exit status as the verdict and, on a failure,
keeps `adb logcat -d` in the test's undeclared outputs (its tail in the
log). The launcher takes `adb` from PATH and starts neither emulator nor
adb server: on the bot the recipe's android_virtual_device context has
booted the device before the step, and an adb of another version would
restart the server under it. The tests are local (the server over the
host's loopback), exclusive (one device, fixed paths) and external (the
device is no input), and compatible with Android target configurations
only, so a host configuration's wildcard run skips them as it skips the
binaries. args/env/data/shard_count are errors rather than dropped: the
suite runs as run_tests.py runs it or the gap is seen.

The converter: androidDeviceTests (bazel_mapping.dart, run_android_tests's
list by GN label) makes those three the macro, size large (an unstripped
push and a whole suite on an emulator); runTestsTypeSuites gains the
`android` type (builders.dart), so the build gets the suite
//engine/src/flutter/ci/builders:android_emulator_debug_x64_tests and
builds.json its type. linux_android_emulator.json builds with Bazel as the
other Linux files do (--bazel, --no-rbe, download_bazelisk) and its test
step names --variant beside --android-variant (run_tests.py's Bazel mode
reads bazel.json from every directory named). Regenerated: the three
packages' BUILD.bazel, ci/builders/BUILD.bazel, configs.bazelrc,
builds.json. README ("Android"), ci.md (the builder off the still-GN list).

Checked: converter 284/284 and --check, engine_build_configs 84,
bazel_ci_test 23, check_build_configs.sh, buildifier; `bazel test
--config=android_emulator_debug_x64 //engine/src/flutter/ci/builders:
android_emulator_debug_x64_tests` against a fake adb on PATH builds the
three binaries (119 MB unstripped) and runs the three launchers (push,
chmod, shell, in that order; 3 pass), and with the fake failing, the status
(7) comes back and the logcat lands in the undeclared outputs with its tail
in the log. The emulator run itself is the presubmit's (the builder runs
on every PR; 7:51 under GN at 540).
…e review

Forty-six statements that the tree had moved past, found by reading the
three documents against it: counts (58 Linux builds and 55 distinct
configurations, 68 Mac builds and 60; nine Firefox suites since the roll;
1,427 engine tests after it; 2.2 GB of Maven files), paths (gn_build,
gn_action, dart_pkg and flatbuffers live in build_defs/, not build/bazel/;
the output directories carry the platform, `android_arm64_debug-opt`,
`linux_x64_debug-opt`), rules that do not exist (`dart_binary` is
`dart_vm_kernel`), and "not yet" statements about things since done: the
builder-file variants folded into the originals, `--no-prebuilt-dart-sdk`
a setting, zlib and BoringSSL as Bazel targets, the entitlements'
generated_file converted, gen_snapshot_host in cross builds, the GL wrap
on arm64 and Mac, the Xcode frameworks' test targets compiling on the
workers (541), the Mac builders on Bazel in place, `apple_universal`
stripping and dSYMs, the wasm installs' web SDK, `stripped` off in the
Android configurations rather than off by default, `use_rbe: true` kept
beside `--no-rbe`, `ci/host_release_benchmarks` as the one host build
without `--no-lto`. The file table names bazel_ci.py, bazel_ci_test.py,
builds.json and ci.md. A paragraph under the table says what the commit
numbers, Qnn/Dnn/Tnn ids and research/NN references are (the migration
branch's own record, kept outside the tree), so a reader can read past
them.
… its own action graph

linux_clang_tidy built its two variants with GN for the compile database
clang-tidy reads, archived each build directory to CAS, and five 32-core
tester drones linted shards of it. A Bazel build's database cannot travel:
its commands run from the drone's execution root (bazel-out, the external
repositories, the generated headers), so the lint runs where the build
ran -- `ci/clang_tidy.sh --variant <build> --lint-all` as the build's own
test step, on a 32-core build drone (NEED_DECISION Q44: the host variant's
four shards' work on one drone, the Android variant whole rather than its
share of the host's intersection).

The script, finding a bazel.json in the variant's directory, makes the
database with tools/gn_to_bazel/tool/compile_commands.py
--build-directory --build before the lint: `bazel aquery` of the CppCompile
actions through the directory's own commands (bazel_ci.described_command:
the same bazelisk, startup options, output base and configuration as the
build), then a build of the targets that own the commands (391 for the
host variant; `compile_commands.targets` beside the database;
--build_manual_tests for the benchmarks the query reaches,
--skip_incompatible_explicit_targets for an owner compiled under a
transition alone), with --remote_download_regex fetching the generated
sources and headers a remote build leaves on the workers -- clang-tidy
opens them -- and the database last. Entries whose compiler is not a clang
(Emscripten's, 318 wasm sources under the host variant) are left out, as the
lint tool has no clang-tidy beside em++; GN's host database had no wasm
compiles either.

tools/clang_tidy reads an entry's `arguments` when there is one (the string
`command` split on spaces mangled Bazel's quoted defines,
'-DDART_LEGACY_API=[[deprecated]]'); the string path is unchanged. Its
file matching is a set lookup now: with --lint-all the list is the whole
engine tree, and a search of it per command took longer than the lint
(the first clang-tidy ran ten minutes in). .clang-tidy's HeaderFilterRegex
gains the execution root's spelling of the engine's headers, anchored
(`^engine/src/flutter/<dir>/`), beside GN's `../../flutter/<dir>/`: the
generated headers under bazel-out/ stay out as GN's gen/ do, and a header
included relative to the main file, spelled absolute, matches under
neither, as it never did.

Three things the whole execution root does differently from the sandbox
and from GN's commands, each handled in the database: the database keeps
to the build's own configuration (configuration_ranks: a host build's
graph reaches the profile and release engines' and the Android compiles,
1,580 sources here, whose generated headers are not the build's -- they
are the other builders' databases, as under GN); the virtual include
directories are searched before the external repositories' roots for
quoted includes (from the whole root, "third_party/benchmark/include/..."
landed in BoringSSL's vendored copy; the sandbox resolves it through the
declared mapping); and the sysroot's include directories are `-isystem`,
as GN's pkg-config ones are (clang-tidy keeps quiet about a system
header's macro expanded in the engine's code: GLib's g_clear_object in 83
Linux shell files otherwise). embedder_unittests.cc's clang-analyzer
fallback defines yield to the build's: they conflicted once the build's
quoted defines reached clang-tidy whole, which GN's lint never managed.

linux_clang_tidy.json: --bazel, --no-rbe, download_bazelisk, cores=32,
cas_archive off, the lint as each build's test step, the five global tests
gone. Regenerated: ci/builders/BUILD.bazel (installs of everything for the
two builds), configs.bazelrc, builds.json.

Measured here (24 cores, the recipe's sequence by hand, a warm output
root): tools/gn --bazel --bazel-rbe; the install 4:42 (10,936 actions);
the lint step 51 min: aquery 2:36, the owners' build 4:14 (7,128 actions
the first time), 4,350 compile commands, clang-tidy over 1,252 engine
files 43:49 (715 CPU-minutes: about 24 on a 32-core drone; GN's four
host shards summed to 18), no lint problems found. The Android variant's
lint and the Mac file (mac_clang_tidy.json) follow; so do the clangd
builders (ci.md).
…the build's own configuration, the compiles alone

545's first run on the bots failed before any lint: the query's universe,
every target under //engine/src/flutter/..., took the Emscripten
toolchain's package into analysis, whose @emsdk the clang-tidy builders
do not sync (no web build; the repository rule fails on the missing
prebuilt). And the Android variant's run here failed building the
database's owners: the wildcard reaches targets no build of the platform
makes -- impellerc configured for Android (its compile under the NDK's
flags fails on nlohmann's literal operators; the real build only ever
builds it for the host, in the exec configuration) and a host-only test
that compiles under Android and does not link (-lpthread).

Three corrections, each toward what GN's database was:

- With a build directory the universe is the root group
  //engine/src/flutter:flutter, GN's default target: what `ninja` with no
  target built for the clang-tidy builders and what GN's database listed,
  the platform's unittests included (the root group names them by
  platform, as GN's did). The wildcard remains the default without a build
  directory. The host closure reaches no Emscripten target (cquery).
- The exec configuration's compiles are left out with the other
  configurations' (configuration_ranks): the host tools under the host's
  flags are the host build's database, as GN's host-toolchain compiles were
  that lint's; building their owners in a cross build's configuration is
  what failed.
- `--build` builds the owners' compilation outputs alone
  (--output_groups=compilation_outputs), not their links; the database
  has no use for a link, and a host-only test reached by an Android graph
  has none.

Measured: the Android variant end to end here -- the install 10:37 (the
Dart SDK's wrapped build for Android on the critical path), the database
in 1 min (3,082 commands of android_arm64_debug-opt; 618 sources left to
the api26 transition's and the exec configuration's databases), clang-tidy
over 686 files in 9:51 and a last file alone in 9:25 (content_context.cc),
no lint problems; the host database with the same code in 2:25 (4,105
commands of 357 owners; 35 exec-only sources left out). The pushed 545's
host lint (1,252 files of the wildcard's universe) found no problems
either; the root group's files are among them.
… a failure

mac_clang_tidy.json takes linux_clang_tidy.json's shape (545): its macOS
host and iOS simulator builds carry --bazel (--no-rbe, download_bazelisk,
cas_archive off) and each lints itself in a build-local test step --
`ci/clang_tidy.sh --variant <build> --lint-all` -- in place of the five
tester-drone tests, which re-ran tools/gn on each drone for a database of
their own and sharded GN's; the osx_sdk context comes from the builds'
properties as it does for the other Mac builders. The Apple toolchains'
remote-compile mode (--config=rbe_mac, a bot's and a developer's with
--bazel-rbe) compiles through apple_clang_wrapper.sh, which the database
would have left out as a compiler that is not a clang: compile_commands.py
resolves it to the Mac's clang from the action's environment
(FLUTTER_CLANG_HOST, the toolchain's), where clang-tidy is.

tools/clang_tidy --lint-all with no compile command naming a file of the
repository is now a failure (exit 1, the paths named) rather than "No
changed files" and a pass: a database that does not describe the checkout
-- another tree's, or every entry left out -- would otherwise lint nothing
on a bot and go green. The changed-files and regex modes keep their pass.

Regenerated: ci/builders/BUILD.bazel (installs of everything for the two
Mac builds), configs.bazelrc, builds.json. ci.md. Verified here: the
converter's --check, engine_build_configs, bazel_ci_test, the lint tool's
tests (the new one included); the Mac run is the tester's (T84) before
the push.
…the drones' arm64

linux_clangd and mac_clangd (standalone builder files) build with Bazel:
the install lays font_subset as GN's ninja did, and a step before
clangd_check makes the compile database from the build's action graph --
`compile_commands.py --build-directory=../out/<build> --build
--build-targets=//engine/src/flutter/assets:assets_fixtures`: GN's default
target's compiles in the build's configuration, and a build of that one
target's compilation outputs, which writes the generated source the check
reads (`_fl_assets_fixtures.cc`, Bazel's name for GN's gen-time
`_fl__fl_assets_fixtures.cc`; clangd_check accepts either) rather than the
engine's objects, which a no-RBE builder has no time for. `--build-targets`
is new for it. Here: the database in 59 s, clangd's check "0 errors" in 2 s.

mac_clang_tidy.json's builds pass --mac-cpu arm64 and --simulator-cpu
arm64, as the other Mac builder files do: tools/gn's x64 default had the
converter give the lint an x64 build on an arm64 drone, whose host tools
(flutter_tester_opengles, which the root group's unittests reach) are no
build of that platform's, and the database's query failed on the Mac
tester's machine (T84: incompatible target). The lint reads the drones'
own build now -- and finds what GN's x64 lint never looked at: on arm64,
clang-analyzer-security.ArrayBound reports an out-of-bounds write inside
rapidjson's AddMember on the paths through json_message_codec_unittests.cc's
calls (reproduced under GN with --mac-cpu arm64, absent under x64 on both
build systems: a false positive of the analyzer in the vendored header).
The statements carry [[clang::suppress]] with the reason; a NOLINT in the
test cannot reach a diagnostic placed in the header. The Mac tester's
(a) run had 1,076 of 1,077 files clean with this the one failure; the iOS
simulator variant (x64 then) 491 of 491.

Regenerated: builds.json, ci/builders/BUILD.bazel, configs.bazelrc. ci.md.
The format check of 546's presubmit (linux_unopt, "Check formatting")
found two lines yapf folds otherwise; this machine's python3 is 3.13,
which tools/yapf.sh refuses, and the file went up unformatted. Formatted
with depot_tools' vpython3 (3.8) and third_party/yapf.
…or shell/platform/common; ci.md's numbers

548's [[clang::suppress]] on json_message_codec_unittests.cc's AddMember
statements does not reach the report (T84c): the analyzer places it in
rapidjson's document.h, which the attribute on the caller does not cover,
and clang-tidy's header filter keeps a header report whose path starts in
the main file. shell/platform/common/.clang-tidy inherits the engine's
configuration and turns clang-analyzer-security.ArrayBound off for the
directory, with the reason (an arm64-only analysis of a vendored header; the
x64 analysis GN's lint ran reports nothing there); the attribute is gone.

ci.md: linux_clang_tidy's numbers from 546's presubmit -- the host drone
16:39 (install 0:55, the lint step 12:14), the Android drone 8:48, the
builder 17:53 against GN's 15–22 -- and the Mac tester's T84: the macOS
host variant's lint 9:07 over 1,077 files on 16 cores, the iOS simulator's
2:17 over 491, the Mac clangd check 0 errors.
As 548 did for the Mac clang-tidy file: tools/gn's x64 default gave the
clangd build an x64 configuration on an arm64 drone, whose host tools the
database's query (the root group) reaches and finds incompatible there
(T84d). Regenerated: configs.bazelrc, builds.json.
The Mac tester's run of the three root suites in one invocation (--jobs=200,
11-06) laid more than 500 runfiles trees at once -- 160 GB of APFS metadata
and links -- and emptied a 208 GB disk before the suites were half done. The
table says so: one suite at a time, or fewer jobs.
The builder is postsubmit only and uploads the engine's benchmark series
(generate_metrics.sh over nine executables of the build directory,
upload_metrics.sh to Datastore). Another toolchain configuration under
those numbers is a baseline change for the perf owners to want, and a
first run on Bazel would be a real upload with no presubmit before it;
research/28 scopes the move, NEED_DECISION Q46 asks.
…er Bazel builder files do

551's presubmit: both clangd builders failed before the database -- the
analysis of the root group reaches the GL backends' wrapped GN build,
which names reclient's configurations (@reclient_cfgs) among its inputs
whether or not it runs on RBE, and these two files synced no reclient
(use_rbe unset; the repository rule's CIPD fallback is forbidden to a bot).
use_rbe: true beside --no-rbe, as ci.md has it for every Bazel builder
file: the sync brings reclient and its cfgs, the recipe starts no proxy,
and the build runs no remote action.
At 551 the Mac host clang-tidy drone took 31 minutes, 23 of them the lint
on the Mac bot's cores, where GN's four tester shards kept the whole
builder to 25-28. The host variant is now built on two drones --
ci/host_debug_clang_tidy and ci/mac_host_debug_clang_tidy_1, the same
flags, the second's install from the remote cache -- each linting one half
of the same database: `--shard-id=0|1 --shard-variants=<the drone's own
variant>`, the lint tool's two-way split over the sorted file list, which
both drones compute identically from identical databases (checked on
Linux: 621 of 1,241 files for shard 1). The iOS simulator variant stays
on one drone (12:31 at 551). Regenerated: configs.bazelrc (the second
configuration), ci/builders/BUILD.bazel (its install), builds.json. ci.md.
…and embedding are bin/cache's

The tool's android shard wanted an engine checkout under either
configuration: its facsimile took the Android gen_snapshots from the tree
(build/archives' android_engine_*_gen_snapshot, the Android engine built
for three CPUs in two modes, hours the first time) and served the
embedding to Gradle from the build's Maven repository, even under
--config=prebuilt_engine with `flutter precache --android` done -- 538 left
it so, with a stamp of bin/cache's revision over the tree's files
(//bin/internal:prebuilt_android_sdk_stamp).

Now, under the prebuilt engine, the shard runs from bin/cache alone, as
the apple shard has since 538:
- @flutter_bin_cache//:android_engine (new): the android-sdk set as
  `flutter precache --android` lays it -- artifacts/engine/
  android-<cpu>-<mode>/<host>/ for the three CPUs in the two AOT modes,
  the gen_snapshot and its LICENSE -- laid at its paths through `cache`,
  with android-sdk.stamp among the stamps the facsimile takes
  (@flutter_bin_cache//:stamps). Not the flutter.jar beside them: the
  android-internal-build-artifacts set, which no command of the tool asks
  for. The set is checked like the desktop's and the web's (_ARTIFACT_SETS:
  optional, its stamp must equal engine.stamp when there); a cache without
  it lays nothing, and the test's tool fetches it as the checkout's would.
- android_build_tools' `maven` is optional. Without one -- the shard under
  the prebuilt engine -- there is no FLUTTER_STORAGE_BASE_URL, and the
  Gradle plugin fetches the embedding of the facsimile's engine.stamp and
  engine.realm (bin/cache's own) from the storage, as a developer's
  `flutter build apk` does; the shard has the network for Gradle's own
  downloads in any case. With the tree's engine the file: URL at the
  build's Maven repository stays.
- The shard's `cache` is _ANDROID_TOOL_CACHE, the tool's with the set
  under the prebuilt engine (a select of its own: _TOOL_CACHE is one
  already). //bin/internal:prebuilt_android_sdk_stamp goes.
- The tree's facsimile lays its gen_snapshots under the host's directory
  as the tool names it (_HOST_ENGINE: darwin-x64 on every Mac, where
  linux-x64 was written), unverified on a Mac as before.

Checked here with bin/cache at this commit's engine
(FLUTTER_PREBUILT_ENGINE_VERSION and the archives realm, `flutter precache
--android`: android-sdk.stamp equal to engine.stamp): the repository's
android_engine is the twelve files of the set; Bazel analysis of the shard
under both configurations; and under --config=prebuilt_engine
flutter_build_apk_verbose_test (the `-v` output names the gen_snapshot
run) and flutter_build_apk_split_per_abi_test (release APKs for the three
ABIs) pass, 375 s and 551 s, with no Android engine action run -- the
gen_snapshots bin/cache's, the embedding Gradle's download from the
storage. On a Mac, after `flutter precache --android`, the shard should
run the same way from darwin-x64 (the Mac tester's next task).
preview_detector_graph_test writes a project to the real file system,
watches it, and expects each step's change event in turn. Twice on a Mac
under Bazel the step that restores a file found the previous step's errors
still reported (the Mac tester's run at 458, 11-01; `Mac_arm64
tool_tests_commands` at 546's presubmit, 11-06: "Expected: Set:[] Actual:
Set:[foo.dart, main.dart]" from expectHasNoErrors): a write's second
FSEvents event, analyzed while the file was still invalid, reported under
the next step's expectation. The test's own note anticipates it ("slow on
heavily loaded machines and cause flaky failures"); dev/bots runs the
commands shard one file at a time (forceSingleCore), and the GN builder
was still 2-3% flaky on this family before flutter#191214; under
Bazel eight tests ran beside it on the Mac bot. The next run passed each
time.

The file is `flaky` in commands_shard_hermetic now (Bazel retries it up to
three times, 17 s each) rather than serialized: `exclusive` would put the
nine widget_preview watcher tests on the Mac shard's critical path for an
intermittent failure of one.
mac_clang_tidy with the host lint on two drones: 23:27 for the builder
(31 min at 551 with one; GN 25-28), each host drone 22:11 -- the build
3:38, its half of the lint 12:33. linux_clang_tidy 15:56 the same run
(17:08-17:53 before; GN 15-22). The clangd builders 6:21-6:40 (Linux) and
7:26-7:57 (Mac) at 554-555; a stale "mac_clangd stays GN" goes (548 moved
it).
…apple shard's shape

Two of the android shard's files build for macOS and iOS as well on a Mac
host: analyze_size_test's iOS and macOS cases (`flutter build ios
--no-codesign` and `flutter build macos` of hello_world, --analyze-size)
and flutter_build_with_compilation_error_test's macos and ios targets (a
created project; `skip: !platform.isMacOS` elsewhere). In the android
shard on a Mac they failed (the Mac tester's T85, 23 of 25 under the
prebuilt engine): the iOS case found no iOS engine in the facsimile and
tried to fetch one into the read-only cache ("failed to write ...
ios-sdk.stamp"); the macOS case's Xcode build phase followed the
facsimile's xcode_backend.sh link to the checkout and bootstrapped the
checkout's own tool ("update_engine_version.sh: ... engine.stamp.tmp:
Operation not permitted") -- what the apple shard arranges for
(unsandboxed xcodebuild, the iOS engine directories, the engine
directories and the two build-phase scripts copied writable) and the
android shard has no reason to.

Now:
- The android shard runs the two files' other cases on every host
  (_INTEGRATION_ANDROID_CASES, `dart test --name`: analyze_size_test but
  for "hello_world for (iOS|macOS)", the compilation-error file's apk and
  web targets). On Linux nothing changes in what runs: those cases were
  skipped.
- apple_cases_shard (new; in //:apple_tool_tests) runs the macOS and iOS
  cases (_INTEGRATION_ANDROID_APPLE_CASES) with the apple shard's
  attributes -- the Apple cache files, unsandboxed, _APPLE_WRITABLE, plus
  hello_world's project in its data and its ios/ and macos/ copied
  writable, as the gallery's are -- under targets named `<file>_apple`
  (the android shard has the files' names, as the signed shard's
  `_signed` does for its overlap with the apple shard).

Checked: Bazel analysis of both shards and //:apple_tool_tests (the Apple
one skipped as incompatible on Linux, its targets and `--name` arguments
as intended); the two android targets pass here under
--config=prebuilt_engine with the filter (analyze_size_test 4 of its 6 cases in 239 s, the compilation-error file its apk and web cases in 179 s). On a Mac the two
`_apple` targets are the Mac tester's next task (T86).
…h list

A tool test runs with a home of its own ($TEST_TMPDIR/home: the tool's
settings and analytics, Gradle's files). On a Mac, `security` -- and with
it codesign, xcodebuild and the tool's code-signing lookup -- reads the
user's keychain search list from ~/Library/Preferences/com.apple.security.plist,
so under that home it sees the System keychain alone: the login keychain
with the developer's identity is invisible. `flutter create` then writes
no DEVELOPMENT_TEAM into the project, a device build has nothing to sign
with, and a failed device build is explained by the missing team rather
than by Xcode's output. The Mac tester's T86/T87 found it through
flutter_build_with_compilation_error_test's ios case (559's
apple_cases_shard): the case passes from the checkout, where `flutter
create` found "Apple Development: Google Development" and wrote the team,
and failed in the facsimile, where the tool printed its development-team
hint in place of the Dart error the test looks for. T50/T51's "No valid
code signing certificates were found" on the same Mac was the same thing.

The launcher of an unsandboxed tool test on a Mac (the shards that build
with Xcode) now copies the developer's search list into the test's home
(`security list-keychains -d user` under the developer's home, `-s` under
the test's; the keychains themselves stay where they are; nothing when the
list is empty or `security` fails). Bazel's test environment carries no
HOME, so the developer's home is the account's (dscl's NFSHomeDirectory),
and `security` writes the list only into an existing ~/Library/Preferences,
which the launcher makes first -- both found by the Mac tester on the
first cut (T88). Sandboxed tests keep seeing no identity, as before. On a
LUCI Mac the recipe's tests run under the bot's own home, so nothing
changes there.

Checked: the shell fragment against a fake `security` and `dscl` (two
keychains, one with a space in its path; an empty list; no HOME; no dscl),
Bazel analysis of the tool shards. On the Mac (T88, with the tester's
equivalent patch): flutter_build_with_compilation_error_test_apple passes
both cases in 29 s, and lldb_init_test_signed -- a device build with
automatic signing, never runnable there before -- finds the identity
("Automatically signing iOS for device deployment using specified
development team") and fails only where that Mac's Xcode has no account
to provision with; its flavor case passes.
The apple suite whole at 560 on the tester's Mac under
--config=prebuilt_engine: //:apple_tool_tests 10 of 10 in 15:46 (no
keychain or signing prompt), swift_package_manager_test 7:14 and
ios_content_validation_test 3:46 the long ones; the android shard's 23
Android targets in 23 min at 556 and its two filtered files at 559. In
the Mac table and the prebuilt section.
The build/bazel README had grown to 2,972 lines: a design log with
measurements and references to notes kept outside the tree, and no place
to start. It is now a short entry point -- status, prerequisites, quick
starts for an engine developer and for a framework or tool contributor,
everyday tasks, what to regenerate after a change, where Bazel keeps its
state, how long a build takes, known gaps -- and its sections are moved,
by subject, into docs/:

  dependencies, toolchains, wrapped_gn_builds, engine_tests,
  framework_tests, web_tests, artifacts, builders, shaders, performance,
  verification

The moved text is unchanged except that commit-sequence numbers, tester
task ids, decision-log ids, dated measurement narrative and pointers to
out-of-tree notes are removed (about 45 places), the stale counts and
statements a documentation audit found are corrected, and
docs/performance.md gains a dated table of build timings against GN, with
and without remote execution.

build_defs/README.md, the index of the rules, now lists the 19 files and
the engine/ alias package it had missed. ci.md names the riscv64 Linux
builder among those still on GN and follows two references to the moved
sections.

Documentation only.
Upstream moved the Linux profile and release builds to ThinLTO
(flutter#196314): tools/gn --thin-lto passes GN's use_thin_lto under
enable_lto, and //build/config/compiler:optimize then has -flto=thin
where full LTO has -flto. The merge left the seven of those builds that
run under Bazel on --no-lto. This takes upstream's flag in them and
gives the Bazel build the setting.

- //engine/src/flutter/build/bazel:thin_lto, a flag beside `lto`, and
  the toolchain configuration's `thin_lto` attribute: under it the
  `lto` feature has -flto=thin on compiles and links (the Linux,
  Android, macOS and iOS toolchains share the configuration). It does
  nothing without `lto`, as in GN.
- The converter maps --thin-lto and --no-thin-lto to the setting in a
  build with LTO and to nothing otherwise; to nothing for the web too,
  for which tools/gn reads no LTO flag. Seven configurations gain
  `lto` and `thin_lto`: ci_host_release, host_profile_desktop,
  host_release_desktop, linux_profile_arm64_desktop,
  linux_release_arm64_desktop, host_profile_test, host_release_test.
- in_configuration carries the setting, and the tests' Android
  artifacts say `thin_lto = "false"`: the Android builders build with
  full LTO whatever the build around them has.
- The recipe's --no-lto, in a release builder's presubmit, turns LTO off
  in such a build as in any other (bazel_ci.py's match is unchanged; a
  test). ThinLTO therefore runs in the three release builders'
  postsubmit builds, and in every build of linux_host_engine_test.

Not done: the wrapped Dart SDK build still compiles the VM without LTO
in every build. GN compiles it with -flto=thin in these builds (gn desc
of libdart_vm_aotruntime_product), as with -flto in the Android AOT and
iOS device builds. The README's known gaps now say so.

Checked against GN (tools/gn --runtime-mode release --thin-lto, gn
desc): //flutter/fml:fml's cflags and flutter_tester's ldflags have
-flto=thin once, as Bazel's 46 compiles of fml and the link of
flutter_tester under --config=host_release_desktop do; the host tools
(the execution configuration) have it too, as GN's one toolchain does;
with --//engine/src/flutter/build/bazel:lto=false after the
configuration fml's compiles have no LTO flag.

Built and run locally: `bazelisk test --config=host_release_test
//engine/src/flutter/fml:fml_unittests
//engine/src/flutter/shell/common:shell_unittests` passes (fml's 46
objects are ThinLTO bitcode), and `bazelisk build
--config=host_release_desktop
//engine/src/flutter/ci/builders:host_release_desktop` builds; its
libflutter_linux_gtk.so exports the same 310 symbols as the debug
build's, which has no LTO (that one has four more, the debug mode's
snapshot data).

Also: gn_to_bazel's tests (284) and --check, check_build_configs.sh,
check_generated_bazel_files.sh, bazel_ci_test.py (23),
engine_build_configs (84), bazel_tools (46), buildifier, yapf; Bazel
analysis of the seven builds' install targets, of ci_host_debug and
ci_android_release_arm64 (whose compiles keep the bare -flto), and of
//:tool_tests //:dart_tests.

Not built: the arm64 cross builds, and the profile mode. Not measured:
the test builders' ThinLTO links of every unit test on RBE workers.
The branch was rebased onto master (774b051), which dropped its
nine merges of master. Those merges held the conflict resolutions and
the regenerated lock files and converter output, and the conflicts of
the replayed commits were resolved in favour of the replayed commit. So
the commits below this one are not expected to build on this base.

This commit restores what the merges held. Its tree is the tree of a
merge of the same master into the branch as it was before the rebase,
and that tree was checked: gn_to_bazel's tests (284) and --check,
check_build_configs.sh, check_generated_bazel_files.sh,
bazel_ci_test.py (23), engine_build_configs (84), bazel_tools (46),
buildifier, tools/gn_test.py (7), the analyzer over flutter_tools and
the ten tool test files the branch touches (324 tests); Bazel analysis
of ci_host_debug, of host_release_test and of //:tool_tests
//:dart_tests.

In it:
- deps.lock.bzl and pub.lock.bzl for master's DEPS and pubspec.lock,
  and the converter's output: builds.json, configs.bazelrc,
  ci/builders/BUILD.bazel and six generated BUILD.bazel files.
- dev/bots/BUILD.bazel and packages/flutter_tools/BUILD.bazel for the
  durations file upstream renamed to tool_test_durations.json.
- The builder files without `--no-goma`, which tools/gn no longer
  accepts (flutter#196212), and without mac_unopt.json, which upstream removed
  (flutter#196027).
- Upstream's side of hunks both sides had changed: dev/bots
  (run_command.dart, test.dart and their tests), tools/gn_test.py
  (test_thin_lto), shell/platform/darwin/ios/BUILD.gn, and the tool's
  android_gradle_builder_test.dart in its form after flutter#196125.
@chingjun
chingjun force-pushed the bazel-migration/phase0 branch from b72717b to 1a6c8cf Compare November 12, 2026 22:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) a: animation Animation APIs a: desktop Running on desktop a: internationalization Supporting other languages or locales. (aka i18n) a: tests "flutter test", flutter_test, or one of our tests a: text input Entering text in a text field or keyboard related problems CICD Run CI/CD d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos e: impeller Impeller rendering backend issues and features requests engine flutter/engine related. See also e: labels. f: cupertino f: gestures flutter/packages/flutter/gestures repository. f: integration_test The flutter/packages/integration_test plugin flutter-gpu framework flutter/packages/flutter repository. See also f: labels. p: material_ui material_ui package in flutter/packages platform-android Android applications specifically platform-ios iOS applications specifically platform-linux Building on or for Linux specifically platform-macos Building on or for macOS specifically platform-web Web applications specifically platform-windows Building on or for Windows specifically team-android Owned by Android platform team team-fluttergpu Owned by Flutter GPU team team-ios Owned by iOS platform team team-linux Owned by the Linux platform team team-macos Owned by the macOS platform team team-web Owned by Web platform team team-windows Owned by the Windows platform team tool Affects the "flutter" command-line tool. See also t: labels.

Projects

Status: 🤔 Needs Triage

Development

Successfully merging this pull request may close these issues.

1 participant