Skip to content

fix: Don't recompile Flutter apps that already failed to compile#3953

Closed
spydon wants to merge 1 commit into
mainfrom
worktree-tag
Closed

fix: Don't recompile Flutter apps that already failed to compile#3953
spydon wants to merge 1 commit into
mainfrom
worktree-tag

Conversation

@spydon

@spydon spydon commented Jul 19, 2026

Copy link
Copy Markdown
Member

Description

_ensure_compiled dedupes compilation through the static COMPILED list, but
COMPILED.append(...) is the last statement of the function. If the compile, the copy or one
of the asserts raises, the target is never recorded, so the next page referencing that app
compiles it all over again, even though the result is guaranteed to be identical.

The effect is quadratic in the worst case. The v1.35.1 docs reference ../flame/examples from
36 pages, so a single broken app produced 36 identical failing web builds of ~62s each:

20:30:54 Compiling Flutter app [examples]
20:31:55 Compiling Flutter app [examples]   <- same app, 62s later
20:32:58 Compiling Flutter app [examples]   <- ...11 times, no success in between

Failures are now recorded in a FAILED map alongside their error. A target that already
failed re-raises the stored error instead of paying for another flutter build web. Error
reporting is unchanged: every page referencing a broken app still reports it, so nothing is
silently swallowed.

Context

This was found while investigating why the docs build on flame-docs-site takes 4h17m. The
reason those compiles were failing in the first place turned out to be a separate bug in that
repo's build script, fixed in flame-engine/flame-docs-site#22: the versions are built
sequentially in one clone, and git checkout -f leaves ignored files alone, so a generated
web plugin registrant importing package:rive_native survived from a version using
rive ^0.14 into versions using rive ^0.13, which do not depend on it.

So that PR, not this one, is the actual fix for the failing builds. This PR is worth having
regardless: retrying a deterministic failure once per referencing page is wrong on its own
terms, and it is what turned a handful of broken apps into 590 failed compiles and ~3.3 hours
of wasted CI time. With this change, a genuinely broken app costs one compile instead of 36.

To be explicit about the numbers: on run 29694895994 this change alone would have cut the
build from 4h17m to ~58m. Once the state leak is fixed the compiles succeed, so the saving
from this PR in normal operation is small. Its value is bounding the cost of future breakage
rather than speeding up the healthy path.

Testing instructions

The behaviour only differs when an app fails to compile, so the failure has to be forced:

  1. melos run doc-setup
  2. Break one of the apps that many pages reference, for example add int x = 'oops'; to
    examples/lib/main.dart.
  3. melos run doc-build

Before this change, Compiling Flutter app [flame-examples] is logged once per referencing
page (36 times, each a full failing web build). After it, the app is compiled once and every
referencing page still reports the error.

Reverting step 2 and running melos run doc-build again should behave exactly as before,
since nothing changes on the success path.

Checklist

  • I have followed the Contributor Guide when preparing my PR.
  • [-] I have updated/added tests for ALL new/updated/fixed functionality.
  • [-] I have updated/added relevant documentation in docs and added dartdoc comments with ///.
  • [-] I have updated/added relevant examples in examples or docs.

Breaking Change?

  • Yes, this PR is a breaking change.
  • No, this PR is not a breaking change.

Related Issues

The `flutter-app` directive dedupes compilation via `COMPILED`, but that
list is only appended to after a successful compile, copy and assert. When
`flutter build web` fails, the target is never recorded, so every page that
references the same app pays for another full compile of an app that is
guaranteed to fail again in exactly the same way.

Failures are now recorded in `FAILED` alongside the error, and subsequent
pages re-report the stored error instead of recompiling. Every referencing
page still surfaces the error, so nothing is silently swallowed.
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.

1 participant