The problem
lib/src/generator.dart sets the executable bit by shelling out:
if (file.executable) {
// Dart has no chmod; the shell one is universal on the platforms that
// can build these packages at all.
Process.runSync('chmod', ['+x', target.path]);
}
There is no chmod on Windows, and Dart's Process.runSync throws when
the executable cannot be found — it does not return a non-zero exit code:
ProcessException: No such file or directory
Command: chmod +x ...\tool\fetch_ios_sdk.sh
That throw happens inside the write loop, so some files are already on disk.
bin/binary_sdk_bridge.dart catches only FormatException and StateError,
so a Windows user gets a raw Dart stack trace and a half-written package
directory — precisely the outcome BridgeSpec.validate() goes out of its way
to prevent everywhere else ("a half-generated package is much worse to recover
from than an error").
The comment's premise is also worth revisiting: a Windows machine cannot build
an .xcframework, but it certainly can build the Android half, and generating
the package is not the same activity as building it.
Why it matters
A Flutter developer on Windows doing the Android integration cannot run the
generator at all, and the message they get looks like the tool is broken rather
than like an unsupported platform. .github/ISSUE_TEMPLATE/bug_report.md asks
for "Host platform (macOS / Linux)", so the limitation is real but currently
undocumented anywhere a user would look.
Suggested approach
Scope this issue to do not crash, and say so. Two small changes:
- Guard the call —
if (!Platform.isWindows) { ... }, or catch
ProcessException and carry on. The execute bit has no meaning on NTFS
anyway, and nothing is lost: plan() still marks the file
executable: true, which is what --dry-run prints and what the tests
assert. While you are in there, consider checking the ProcessResult's
exit code on Unix instead of discarding it.
- Tell the Windows user what to expect — a line in the CLI's "Next:" output,
or a short note in README.md, that tool/fetch_*.sh are bash scripts
(#!/usr/bin/env bash, curl, shasum, unzip) and need WSL or Git Bash.
Porting the fetch scripts themselves to PowerShell is a much larger piece of
work and is explicitly not part of this issue — please open a separate one
if you want to take it on.
Files involved
lib/src/generator.dart — write()
bin/binary_sdk_bridge.dart — optional "Next:" note
README.md and .github/ISSUE_TEMPLATE/bug_report.md — the platform note
.github/workflows/ci.yml
test/generator_test.dart
How to verify
There is currently no test asserting the execute bit on disk — only that
plan() marks the fetch scripts executable. Add one that is honest about the
host, e.g. in the BridgeGenerator.write group:
test('writes the fetch scripts executable where the platform has the concept', () {
final root = BridgeGenerator(_spec()).write(tmp.path);
final script = File('${root.path}/tool/fetch_ios_sdk.sh');
expect(script.existsSync(), isTrue);
if (!Platform.isWindows) {
expect(script.statSync().mode & 0x40, isNot(0)); // owner +x
}
});
Then:
dart test
dart analyze --fatal-infos
The real proof is CI. Adding windows-latest to a matrix on the test job in
.github/workflows/ci.yml would catch this class of bug permanently — note
that the "Generated output is valid" step is bash and should stay pinned to
ubuntu-latest.
The problem
lib/src/generator.dartsets the executable bit by shelling out:There is no
chmodon Windows, and Dart'sProcess.runSyncthrows whenthe executable cannot be found — it does not return a non-zero exit code:
That throw happens inside the write loop, so some files are already on disk.
bin/binary_sdk_bridge.dartcatches onlyFormatExceptionandStateError,so a Windows user gets a raw Dart stack trace and a half-written package
directory — precisely the outcome
BridgeSpec.validate()goes out of its wayto prevent everywhere else ("a half-generated package is much worse to recover
from than an error").
The comment's premise is also worth revisiting: a Windows machine cannot build
an
.xcframework, but it certainly can build the Android half, and generatingthe package is not the same activity as building it.
Why it matters
A Flutter developer on Windows doing the Android integration cannot run the
generator at all, and the message they get looks like the tool is broken rather
than like an unsupported platform.
.github/ISSUE_TEMPLATE/bug_report.mdasksfor "Host platform (macOS / Linux)", so the limitation is real but currently
undocumented anywhere a user would look.
Suggested approach
Scope this issue to do not crash, and say so. Two small changes:
if (!Platform.isWindows) { ... }, or catchProcessExceptionand carry on. The execute bit has no meaning on NTFSanyway, and nothing is lost:
plan()still marks the fileexecutable: true, which is what--dry-runprints and what the testsassert. While you are in there, consider checking the
ProcessResult'sexit code on Unix instead of discarding it.
or a short note in
README.md, thattool/fetch_*.share bash scripts(
#!/usr/bin/env bash,curl,shasum,unzip) and need WSL or Git Bash.Porting the fetch scripts themselves to PowerShell is a much larger piece of
work and is explicitly not part of this issue — please open a separate one
if you want to take it on.
Files involved
lib/src/generator.dart—write()bin/binary_sdk_bridge.dart— optional "Next:" noteREADME.mdand.github/ISSUE_TEMPLATE/bug_report.md— the platform note.github/workflows/ci.ymltest/generator_test.dartHow to verify
There is currently no test asserting the execute bit on disk — only that
plan()marks the fetch scriptsexecutable. Add one that is honest about thehost, e.g. in the
BridgeGenerator.writegroup:Then:
dart test dart analyze --fatal-infosThe real proof is CI. Adding
windows-latestto a matrix on thetestjob in.github/workflows/ci.ymlwould catch this class of bug permanently — notethat the "Generated output is valid" step is bash and should stay pinned to
ubuntu-latest.