Skip to content

Refresh the 3.32.8 line onto current dev - #70

Merged
MAUstaoglu merged 21 commits into
release/flutter-3.32.8from
chore/flutter-3.32.8-refresh
Aug 21, 2026
Merged

Refresh the 3.32.8 line onto current dev#70
MAUstaoglu merged 21 commits into
release/flutter-3.32.8from
chore/flutter-3.32.8-refresh

Conversation

@DenisovAV

@DenisovAV DenisovAV commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Refreshes the 3.32.8 line onto current dev.

release/flutter-3.32.8 is 17 commits behind dev — it branches from 3e135ec, before the 1.6.0/1.7.0 work. Most importantly it predates #66, so a project generated from it has no "Check Flutter build mode" phase and can silently archive a debug engine into a release build: installs fine from Xcode, blank screen from TestFlight. That is the defect this line most needs, and it is exactly the kind of thing a client-pinned line should not be missing.

This branch redoes the port on top of dev (3c0231a) instead.

Convergence with the existing port

Worth saying up front: I did the port independently before finding release/flutter-3.32.8, and it landed on the same shape — same lldb removal (import, fields, _lldbAttachTimeout), same synchronous isSupported, same verboseHelp drop, same tvos_cache.dart and tvos_precache_test / tvos_shader_bundle_test adjustments. Two independent passes agreeing is reassuring about the approach.

What is new relative to that port

dev moved to 3.47.0/3.47.1 in between, which added two things that do not exist in 3.32.8:

  • Record-use. KernelSnapshot.recordedUsesFileName, recordedUsesEmptyContent and FeatureFlags.isRecordUseEnabled are all 3.47.0. KernelSnapshot.outputs never declares recorded_uses.json here, so the mirroring block has nothing to satisfy — removed, along with its test group.
  • DartBuildLinkHooks. Both are gone here; copyAssets predates dartHookResult entirely. We already skip the native-assets targets for tvOS and write an empty manifest ourselves, so there were never hook results to pass on.

Also cleaned up what the removals stranded: _findAppPid (only the lldb path called it), three unused imports, and the ArtifactSet.displayName override, which cannot be called here because the member does not exist — precache prints the bare stamp name instead, cosmetic only.

Verified

  • flutter-tvos --version → Flutter 3.32.8 • Dart 3.8.1
  • create --platforms=tvos → project generated, and it carries the Check Flutter build mode phase with FLUTTER_STAGED_BUILD_MODE written correctly
  • build tvos --simulator --debug → Xcode build done, 22.0s
  • build tvos --simulator --release → Xcode build done, 18.9s (AOT + icon tree-shake)
  • dart analyze lib/ → exits 0, no errors or warnings
  • flutter test → 398/399

The one failure is tvos_device_test.dart "rejects a coredevice name that does not resolve": it resolves a deliberately non-existent .local name and expects NXDOMAIN. On my network that name resolves to a real address (.local lookups are hijacked), so the gate correctly reports the host as resolvable. It should pass on CI.

Open question: is on-device debug actually available on this line?

The README on release/flutter-3.32.8 states that debug on a physical Apple TV works, via Xcode's debug action over OSA scripting rather than lldb. I can confirm the mechanism exists and is what this branch falls through to.

What I cannot confirm is the engine side. fluttertv/engine's README says the published v1.0.1-flutter3.32.8 debug artifact carries both the RWX marker and Dart's Must run with --write-protect-code, and aborts at VM init on tvOS 26. I checked the shipped binary and both strings are present, and CheckIfNeedDebuggerHelpWithRX() does FATAL when the flag is off on a new enough OS — which is the state 01-dart-globals.patch creates. But I could not locate IsAtLeastIOS18_4()'s definition in the pinned Dart tree, so whether it returns true on tvOS is unverified by me, and I have no Apple TV to test on.

The two claims may simply be from different times — the release-line README predates the RWX finding. Worth settling on hardware before this line ships to anyone who needs on-device debug. Separately, flutter3.32.8/patches/ has no !defined(DART_HOST_OS_TVOS) exclusion while flutter3.44.8/patches/ does, which is worth its own look.

Documentation and CI

README starts from the mainline's current text and gains the release-line banner, so the line is documented at 1.7.0 rather than reverting to the 1.5.0-era copy. Its on-device-debug note now describes the Xcode path this line actually uses, and the paragraph about naming engine artifacts by commit is scoped to the mainline, since this line keeps its original version-shaped tag. CHANGELOG gains a 1.7.0-flutter3.32.8 entry.

test.yml fired only on main and dev, so release/** lines have never had CI — the existing 3.32.8 branch was never verified by anything but hand. Both triggers now include release/**.

The merge with release/flutter-3.32.8 is recorded with -s ours: its documentation was carried over by hand (rebased onto current text) and its code changes are superseded by the equivalent ones here, so the line's history stays continuous and the diff shows only what actually changes.

MAUstaoglu and others added 21 commits August 5, 2026 23:12
Adds `flutter-tvos versions` and `flutter-tvos use <version>` for switching
the checkout between Flutter versions (#50), stops `build` inheriting
upstream's platform subcommands (#52), fixes `create .` naming the project
"." (#54), and pins the engine to v1.0.2-flutter3.44.8 (#48).

`upgrade` now moves the checkout with `git checkout --force --detach`
instead of `git reset --hard`, which had silently discarded unpushed
commits.
main was released as 1.5.0 but dev never received the release-prep commit,
so dev still shows [Unreleased] and version 1.4.3. Carries only the three
version-bearing files; no code changes.
3.44.9 pins the same Dart revision (d684a576) as 3.44.8, so the existing
v1.0.2-flutter3.44.8 engine artifacts stay AOT-valid — no engine rebuild
and no re-signing. The only engine-tree changes in 3.44.9 are Android
lint/SDK bundle files, which tvOS does not build.

The build-graph overrides in lib/build_targets/application.dart are
unaffected: 3.44.9 touches nothing under build_system/ and leaves
KernelSnapshot.build() and KernelCompiler.compile untouched.
3.44.9 pins the same Dart revision (d684a576) as 3.44.8, so the existing
v1.0.2-flutter3.44.8 engine artifacts stay AOT-valid — no engine rebuild
and no re-signing. The only engine-tree changes in 3.44.9 are Android
lint/SDK bundle files, which tvOS does not build.

The build-graph overrides in lib/build_targets/application.dart are
unaffected: 3.44.9 touches nothing under build_system/ and leaves
KernelSnapshot.build() and KernelCompiler.compile untouched.
# Conflicts:
#	CHANGELOG.md
#	README.md
#	pubspec.yaml
Merge main into dev (1.5.1 release files)
Upgrades the CLI to Flutter 3.47.0 (4cf24164) and the origin-signed engine-495915c579071a4ef6a5014637166d5231cb4aac artifacts.

- Re-mirrors the 3.47.0 flutter_tools API: DartBuild -> LinkHooks, recorded_uses.json in KernelSnapshot, LLDB xcodeProjectInterpreter.
- Raises the tvOS deployment floor 13.0 -> 15.0 to match the engine's actual minos, which upstream moved with ios_deployment_target. Apps previously claimed tvOS 13 support while embedding a framework requiring 15 (ITMS-90208 at submission).
- Fixes on-device debug: the VM Service URI rewrite now polls mDNS in 5s attempts up to 60s and exits on the explanation when nothing resolves, instead of returning an undialable host and leaving hot reload silently dead.
- Verifies any host devicectl offers before trusting it; potentialHostnames stays populated after the CoreDevice tunnel drops and those names resolve for nobody, which would otherwise short-circuit the retry budget.
- Adds a workflow that fast-forwards main to the exact tested commit on dev.
The Runner project runs no Dart build. "Embed App.framework" and "Copy
flutter_assets" copy whatever the last `flutter-tvos build/run` staged into
tvos/Flutter, and the engine is the Flutter.xcframework that same run copied
in, so Xcode's CONFIGURATION never had any influence on the Flutter payload.
Archiving Release after a debug run therefore shipped the debug/JIT engine and
a kernel_blob.bin inside a release app.

That build runs from Xcode, because a development signature permits the JIT
pages the VM needs. Installed from TestFlight or the App Store, where the
distribution signature carries no get-task-allow, the VM cannot get executable
pages: the app launches, the launch screen paints, and no Flutter frame ever
arrives. Verified end to end - two builds from one source, same Release
configuration, differing only in what was staged: the clean one runs, the one
archived after a debug run shows a blank screen. Nothing upstream catches it
either, altool --validate-app and App Store processing both accept it.

Generated.xcconfig now records FLUTTER_BUILD_MODE, and the app template gained
a "Check Flutter build mode" phase - first in the target, ahead of anything
that copies a payload in - that fails the build on a mismatch and names the
command to run. A profile payload under Release only warns, since the CLI
drives profile builds through that configuration and they do run; an unset
FLUTTER_BUILD_MODE from an older CLI warns rather than fails.

project.pbxproj is written once at create time and never rewritten, so
existing projects keep archiving unguarded until regenerated. Device builds
now warn when the phase is absent.
The existing assertions only prove the phase exists and still contains the
right strings. An inverted condition, or a case pattern that stopped matching
Release, would leave all of them intact and still archive an app that cannot
start - which is the whole failure this phase is meant to prevent.

Extract the phase's shellScript from both pbxproj files, unescape it, and run
it under /bin/sh against a throwaway project directory: Release staged for
debug fails and names the fix, Release staged for release and Debug staged for
debug pass, profile under Release and an unset FLUTTER_BUILD_MODE only warn, a
release build with no App.framework staged fails, and an unrecognized
configuration is skipped rather than guessed at.

Checked against a mutant: inverting the mode comparison in the template fails
five of the seven cases, while the example's untouched copy stays green.
Moves the pinned SDK to 3.47.1 and the artifacts to the engine built against it,
and cuts the release with the archive-mode guard already on this branch.

Nothing tvOS-facing moved between 3.47.0 and 3.47.1: none of the files the tvOS
patch set touches changed, `shell/platform/darwin` is untouched, and the five
Dart SDK files behind the platform-identity patches are identical blobs at both
revisions. The engine was rebuilt anyway, because `dart_revision` moved and AOT
snapshots are keyed to the Dart SDK hash — profile and release builds against
the previous artifacts would fail to load. The two pins move together for that
reason.

Verified before publishing: 118 engine unit tests green on both tvOS 17.5 and
26.5, and the full build-and-run matrix green on the 17.5 simulator and a
physical Apple TV 4K across debug, profile and release — launch, VM service, hot
reload and restart, platform identity and FFI symbol export in each. All six
artifacts pass their checks, including origin signing on all four tvOS engines.
Review found the guard green in scenarios that need no unusual setup. All four
are real; each is addressed here.

**The remediation was a no-op.** `renderTvosRunner` returns as soon as `tvos/`
exists — before its own status line, and with no `overwrite` parameter for
`--overwrite` to reach — so `flutter-tvos create .` did nothing, silently, for
every project the warning targets. The user cleared no warning and archived
believing they were protected. It now says it declined and why, and the three
places that gave that advice (the warning, doc/publish-app.md, CHANGELOG) lead
with the mitigation that works today: run the release build immediately before
each archive.

**The marker was written mid-staging.** `_generateXcconfigs` ran at step 5, after
the engine, assets and registrant had already been replaced. A build interrupted
in that window left a marker describing a payload no longer on disk: release
build, then Ctrl-C a debug build after the JIT engine and kernel_blob were
staged but before the marker moved, and the guard blesses issue #65 verbatim.
`build()` now writes `unknown` before touching anything and the real value only
once staging has finished, so the failure mode is a rejected build rather than
an accepted one.

**An undeterminable mode passed.** Empty is the state after `flutter-tvos clean`,
on a fresh checkout (Generated.xcconfig is gitignored) and with an older CLI —
not just the last of those. Its backstop was the App.framework check, which
cannot help: nothing in a normal build deletes App.framework, so a release one
survives any number of debug builds. Under Debug this still warns; under
anything else it now fails. A payload check runs first and is immune to a stale
marker entirely: a `kernel_blob.bin` under a non-debug configuration proves the
payload is JIT whatever the marker claims.

**The marker collided with upstream's override.** `FLUTTER_BUILD_MODE` is what
upstream tells users to set themselves, and `Generated.xcconfig` is only the base
configuration, so a target-level definition shadowed it and the guard compared
the user's declared intent against itself — matching every time. It is now
`FLUTTER_STAGED_BUILD_MODE`, which nothing else writes.

Also, being cheap next to the above: the phase records `FLUTTER_STAGED_SDK` and
fails when a simulator payload is built for a device, the unrecognized-
configuration path announces itself at `warning:` rather than `note:` (which
xcbeautify and most CI filters drop) and gained an explicit opt-out, and the
suite gained the cases that were missing — Debug with a release or profile
payload, the Profile configuration, an unset CONFIGURATION, a stale marker over
a JIT payload, and SDK mismatch. Deleting the Profile arm or the payload check
now fails tests; both previously passed everything.

396 tests green, analyzer clean.
The new payload check failed a profile build that should have passed, and it was
right to: `build/tvos/` is shared across modes and nothing there removes
`kernel_blob.bin`, which only a debug build writes. `copyFlutterAssetsTree`
mirrors that directory faithfully, so a profile or release build run after any
debug build staged a 43 MB stale debug kernel and shipped it inside the AOT app.

The engine ignores it there, which is why this went unnoticed — it is dead
weight in the bundle rather than a crash, and it hands out the app's Dart kernel
with a release build.

Dropped at staging time rather than by cleaning the shared output directory,
which incremental builds rely on.
Extracted from _copyFlutterAssets so the behaviour is testable without
constructing a build: the removal is what keeps the new payload check from
failing legitimate AOT builds, so it should not rest on an e2e run noticing.
Every guard test injects FLUTTER_STAGED_BUILD_MODE straight into the process
environment, so all of them stay green even when the chain that delivers it in a
real build is broken:

  Generated.xcconfig
    -> #include'd by Flutter/Debug.xcconfig and Flutter/Release.xcconfig
      -> baseConfigurationReference on each configuration of the Runner target

Drop the include, or add a configuration without a base reference, and the phase
sees an unset marker — a hard failure for release, a warning for debug — with
nothing failing until someone archives.

The xcconfig body moved into `buildModeXcconfig` so the include is assertable at
all. The pbxproj half walks the Runner *target*'s configuration list (the
project-level configurations carry no base xcconfig, by design), requires a
baseConfigurationReference on each, and resolves it to one of the two Flutter
xcconfigs rather than to any file.

Verified against mutants: deleting a baseConfigurationReference and repointing
one at an unrelated file both fail these tests, and neither did before.

401 tests, analyzer clean.
Release 1.7.0 — Flutter 3.47.1 and the archive-mode guard
A parallel line for clients pinned to 3.32.8, branched from current dev so it
carries the archive-mode guard and the rest of #66 rather than an older base.
Not intended to merge into dev: dev is on 3.47.1, and merging this would move
the toolchain backwards.

The CLI is written against a newer flutter_tools than 3.32.8 ships, so pinning
the version alone left it uncompilable — 18 errors. Resolved in the direction
of what 3.32.8 actually provides:

- Record-use (new in 3.47.0): `KernelSnapshot.recordedUsesFileName`,
  `recordedUsesEmptyContent` and `FeatureFlags.isRecordUseEnabled` do not
  exist, and `KernelSnapshot.outputs` never declares that file here, so the
  mirroring block has nothing to satisfy.

- `copyAssets` predates `dartHookResult`, and `LinkHooks` / `DartHooksResult`
  do not exist. Dropped — we already skip the native-assets targets for tvOS
  and write an empty manifest ourselves, so there were never hook results.

- `src/ios/lldb.dart` does not exist yet, so the lldb JIT attach (Path 1 of
  the device debug flow) cannot be built. Removed it, along with the now-dead
  `_findAppPid`, and fall through to the Xcode debugger — already implemented
  here as Path 2, and the same mechanism stock Flutter uses for iOS Core
  Devices.

- `Device.isSupported` is synchronous here, not `Future<bool>`.

- `UpdatePackagesCommand` takes no constructor arguments here.

- `ArtifactSet` has no `displayName`, so that override could never be called.
  Precache prints the bare stamp name instead — cosmetic.

Three tests asserted newer-SDK specifics: the record-use group (the feature is
absent), the `informative` development artifact, and `writePackageConfigFiles`,
which is `writePackageConfigFile` here.

Verified: creates a project, builds it for the simulator in both debug and
release, the generated project carries the "Check Flutter build mode" phase,
`dart analyze` exits clean, 398/399 tests pass.

The one failing test resolves a deliberately non-existent .local hostname and
expects NXDOMAIN; it fails only on networks that hijack .local lookups.

Device debug on a physical Apple TV is not available on this line — the LLDB
helper is absent above, and the published v1.0.1-flutter3.32.8 engine aborts
at VM init on tvOS 26. Simulator and release/profile are unaffected.
README gains the release-line banner and the 3.32.8 version block, and its
on-device-debug note now describes the Xcode path this line actually uses —
3.32.8's flutter_tools has no direct-lldb attach API. The paragraph explaining
that engine artifacts are named by commit is scoped to the mainline, since this
line keeps its original version-shaped tag.

CHANGELOG gains an entry for the line, starting from the mainline's current
text rather than the 1.5.0-era copy.

test.yml only fired on main and dev, so `release/**` lines have never had CI.
Added them to both triggers.
The existing release/flutter-3.32.8 branch carries the same port done on an
older base (1.5.0 era, 17 commits behind dev). This branch redoes it on current
dev instead, so the line picks up the archive-mode guard from #66 and the rest
of the mainline work rather than a three-week-old snapshot.

Its documentation is not discarded: the release-line banner was carried over in
the previous commit, rebased onto the mainline's current README. Its code
changes are superseded by the equivalent ones here — the two ports converged
independently on the same shape. Recording the merge so the line's history is
continuous and the diff shows only what actually changes.
tool_revision returns the git HEAD SHA in a checkout, and the stamp holds the
revision from the last successful compile. HEAD does not move for an
uncommitted edit, so the two match and the old snapshot is reused silently —
editing the tool and re-running it executes the previous build.

Always hashing instead costs ~0.6s per invocation (52 files under bin/ and
lib/), which is why the SHA shortcut exists. An mtime probe answers the same
question in ~9ms: -print -quit stops at the first file newer than the stamp.
Same shape as the pubspec.yaml -nt check directly above it.

Verified: warm run does not recompile, touching lib/ makes the next run
recompile, the run after does not. Unpatched, the same edit recompiles
nothing.
@DenisovAV
DenisovAV requested a review from MAUstaoglu August 21, 2026 09:08
@MAUstaoglu
MAUstaoglu merged commit ca2176a into release/flutter-3.32.8 Aug 21, 2026
2 checks passed
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.

3 participants