feat: support Linux ARM64 by invalidating bundled x64 stamps - #58
feat: support Linux ARM64 by invalidating bundled x64 stamps#58ooaklee wants to merge 2 commits into
Conversation
Flutter's official Linux tarball bundles an x86-64 dart-sdk under
bin/cache/dart-sdk/ plus matching engine-dart-sdk.stamp and
flutter_tools.stamp. On non-x86_64 Linux hosts (aarch64, riscv64) the
bundled dart binary cannot execute, and the first `flutter` invocation
fails with:
.../bin/internal/shared.sh: line 273:
.../bin/cache/dart-sdk/bin/dart: cannot execute binary file: Exec format error
Because both shipped stamps match on first run, Flutter's self-heal
(update_dart_sdk.sh) is never reached - it is only invoked when
flutter_tools.stamp is invalid - so the wrong-arch dart binary is never
replaced.
Fix: after extraction, on non-x86_64 Linux hosts delete both
engine-dart-sdk.stamp and flutter_tools.stamp so update_dart_sdk.sh
runs on first invocation, detects the host arch, and fetches the
matching dart-sdk (e.g. dart-sdk-linux-arm64.zip).
The stamp-invalidation logic is extracted into a standalone function
(invalidate_bundled_stamps_for_non_x64_linux) so it can be unit-tested
in isolation. A bats suite (test/install.bats) guards the regression on
both x86_64 (stamps preserved) and aarch64 (stamps removed), and CI
runs the suite on ubuntu-24.04 and ubuntu-24.04-arm runners.
Drive-by fix: bin/utils.sh line 3 used ${VAR:=default} without a
leading ':' no-op, which tried to execute the URL as a command and
returned exit 127 under `set -e`. Fixed to the standard
`: "${VAR:=default}"` idiom.
See docs/adr/adr001-linux-arm64-support.md for the full diagnosis and
rationale, and the README troubleshooting section for user-facing
context.
There was a problem hiding this comment.
2 issues found and verified against the latest diff
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".github/workflows/test.yml">
<violation number="1" location=".github/workflows/test.yml:33">
P2: The bats installation step clones from the repository's default branch without pinning to a specific tag or commit, which means upstream changes can break CI unexpectedly. Consider cloning a specific release tag for reproducible builds.</violation>
</file>
<file name="docs/adr/adr001-linux-arm64-support.md">
<violation number="1" location="docs/adr/adr001-linux-arm64-support.md:139">
P2: The ADR documents the fix incorrectly here. Since the plugin deletes `engine-dart-sdk.stamp` as well as `flutter_tools.stamp`, `update_dart_sdk.sh` running unconditionally would still see a missing `engine-dart-sdk.stamp` and trigger re-download of the correct architecture SDK — the workaround would still work. The more accurate regression risk would be if `update_dart_sdk.sh` itself stops checking `engine-dart-sdk.stamp` or is bypassed entirely, not simply if it's no longer gated behind `flutter_tools.stamp`. Consider rephrasing this consequence so future maintainers understand the real dependency on the engine-stamp check inside `update_dart_sdk.sh` rather than on the outer `flutter_tools.stamp` gate.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
|
|
||
| - name: Install bats | ||
| run: | | ||
| git clone --depth 1 https://github.com/bats-core/bats-core.git /tmp/bats-core |
There was a problem hiding this comment.
P2: The bats installation step clones from the repository's default branch without pinning to a specific tag or commit, which means upstream changes can break CI unexpectedly. Consider cloning a specific release tag for reproducible builds.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/test.yml, line 33:
<comment>The bats installation step clones from the repository's default branch without pinning to a specific tag or commit, which means upstream changes can break CI unexpectedly. Consider cloning a specific release tag for reproducible builds.</comment>
<file context>
@@ -0,0 +1,43 @@
+
+ - name: Install bats
+ run: |
+ git clone --depth 1 https://github.com/bats-core/bats-core.git /tmp/bats-core
+ /tmp/bats-core/install.sh "$HOME/.local"
+ echo "$HOME/.local/bin" >> "$GITHUB_PATH"
</file context>
| git clone --depth 1 https://github.com/bats-core/bats-core.git /tmp/bats-core | |
| git clone --depth 1 --branch v1.11.0 https://github.com/bats-core/bats-core.git /tmp/bats-core |
| stops gating `update_dart_sdk.sh` behind it, this workaround may silently | ||
| regress back to shipping an x86-64 binary on arm64 hosts. The failure mode | ||
| would be the original "Exec format error" again. | ||
| - Deleting `flutter_tools.stamp` also forces a rebuild of `flutter_tools.snapshot` | ||
| from source on first run. This is unavoidable: the shipped snapshot was built |
There was a problem hiding this comment.
P2: The ADR documents the fix incorrectly here. Since the plugin deletes engine-dart-sdk.stamp as well as flutter_tools.stamp, update_dart_sdk.sh running unconditionally would still see a missing engine-dart-sdk.stamp and trigger re-download of the correct architecture SDK — the workaround would still work. The more accurate regression risk would be if update_dart_sdk.sh itself stops checking engine-dart-sdk.stamp or is bypassed entirely, not simply if it's no longer gated behind flutter_tools.stamp. Consider rephrasing this consequence so future maintainers understand the real dependency on the engine-stamp check inside update_dart_sdk.sh rather than on the outer flutter_tools.stamp gate.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/adr/adr001-linux-arm64-support.md, line 139:
<comment>The ADR documents the fix incorrectly here. Since the plugin deletes `engine-dart-sdk.stamp` as well as `flutter_tools.stamp`, `update_dart_sdk.sh` running unconditionally would still see a missing `engine-dart-sdk.stamp` and trigger re-download of the correct architecture SDK — the workaround would still work. The more accurate regression risk would be if `update_dart_sdk.sh` itself stops checking `engine-dart-sdk.stamp` or is bypassed entirely, not simply if it's no longer gated behind `flutter_tools.stamp`. Consider rephrasing this consequence so future maintainers understand the real dependency on the engine-stamp check inside `update_dart_sdk.sh` rather than on the outer `flutter_tools.stamp` gate.</comment>
<file context>
@@ -0,0 +1,145 @@
+ is noticeable.
+- The fix depends on Flutter's internal stamp protocol staying as described.
+ If a future Flutter release changes how `flutter_tools.stamp` is computed or
+ stops gating `update_dart_sdk.sh` behind it, this workaround may silently
+ regress back to shipping an x86-64 binary on arm64 hosts. The failure mode
+ would be the original "Exec format error" again.
</file context>
| stops gating `update_dart_sdk.sh` behind it, this workaround may silently | |
| regress back to shipping an x86-64 binary on arm64 hosts. The failure mode | |
| would be the original "Exec format error" again. | |
| - Deleting `flutter_tools.stamp` also forces a rebuild of `flutter_tools.snapshot` | |
| from source on first run. This is unavoidable: the shipped snapshot was built | |
| - The fix depends on Flutter's internal stamp protocol staying as described. | |
| - If a future Flutter release changes `update_dart_sdk.sh` to not check | |
| `engine-dart-sdk.stamp`, or bypasses `update_dart_sdk.sh` entirely, | |
| this workaround may silently regress back to shipping an x86-64 binary | |
| on arm64 hosts. The failure mode would be the original "Exec format | |
| error" again. |
Problem
On Linux
aarch64hosts,asdf install flutter <version>completes successfully but the firstflutterinvocation fails with:```
.../bin/internal/shared.sh: line 273:
.../bin/cache/dart-sdk/bin/dart: cannot execute binary file: Exec format error
```
Root cause
Flutter publishes a single Linux release tarball per version/channel. That tarball bundles an x86-64 dart-sdk under
bin/cache/dart-sdk/plus two stamp files that match on first run:bin/cache/engine-dart-sdk.stampbin/cache/flutter_tools.stampFlutter's bootstrap (
bin/internal/shared.sh) only invokes its self-heal scriptbin/internal/update_dart_sdk.shwhenflutter_tools.stampis missing/empty/mismatched. Because the shipped stamps match,update_dart_sdk.shis never reached on first run, so it never gets the chance to detectaarch64and fetchdart-sdk-linux-arm64.zip(which Flutter does publish on the engine bucket - it just isn't shipped inside the tarball).The error surfaces from Flutter's own bootstrap, not from the plugin's install step, which made this hard to diagnose.
Note: macOS is unaffected because the install script already filters
releases_macos.jsonbydart_sdk_arch.releases_linux.jsonexposes nodart_sdk_archfield (all 719 entries arex64), so the same filtering approach cannot be reused for Linux.Fix
After extracting the tarball, on non-
x86_64Linux hosts delete bothengine-dart-sdk.stampandflutter_tools.stamp. On the firstflutterinvocationupdate_dart_sdk.shthen runs, detects the host arch viauname -m, and replaces the bundled dart-sdk with the matching one (e.g.dart-sdk-linux-arm64.zip).The stamp-invalidation logic is extracted into a standalone function (
invalidate_bundled_stamps_for_non_x64_linux) so it can be unit-tested in isolation. Architecture detection is deliberately delegated to Flutter's ownupdate_dart_sdk.sh, which already handlesaarch64andriscv64correctly.Changes
bin/install- newinvalidate_bundled_stamps_for_non_x64_linux()called at the end ofinstall(); top-level execution guarded by aBASH_SOURCEcheck so the file can be sourced by tests;dirname $0->dirname "\${BASH_SOURCE[0]}"so paths resolve correctly when sourced.bin/utils.sh- drive-by fix: `${VAR:=default}` -> `: "${VAR:=default}"`. The previous form tried to execute the URL as a command and returned exit 127 under `set -e` (bats), blocking the test harness.test/install.bats- 7 bats tests covering x86_64 (preserve), aarch64 (remove each stamp), riscv64 (remove both), darwin arm64/x86_64 (preserve), and an unknown-arch default-branch guard..github/workflows/test.yml- CI matrix running bats on `ubuntu-24.04` (x86_64) and `ubuntu-24.04-arm` (aarch64).docs/adr/adr001-linux-arm64-support.md- ADR documenting the context, decision, and consequences.README.md- new troubleshooting section with a link to the ADR.Verification
Exec format erroron `aarch64` Linux with `3.44.4-stable`.Notes
Summary by cubic
Fixes the “Exec format error” on Linux ARM64 by removing Flutter’s bundled x64 stamp files after install so
update_dart_sdk.shdownloads the correct Dart SDK on first run. Adds tests and CI on x86_64 and ARM64 to prevent regressions.x86_64Linux, deletebin/cache/engine-dart-sdk.stampandbin/cache/flutter_tools.stampafter extraction to triggerupdate_dart_sdk.sh.invalidate_bundled_stamps_for_non_x64_linux()inbin/install; guard top-level execution for sourcing and useBASH_SOURCEfor paths.bin/utils.shwith: "${FLUTTER_STORAGE_BASE_URL:=...}"to avoidset -efailures.batstests covering Linux x86_64/arm64/riscv64 and macOS; add CI matrix onubuntu-24.04andubuntu-24.04-arm.docs/adr/_adr-XXXX-template.md.Written for commit ccbf839. Summary will update on new commits.