Summary
A Flutter engineer encountered an issue when attempting to compile the Flutter Windows engine using a custom Dart SDK. Despite enabling --no-prebuilt-dart-sdk, modifying Dart SDK source files failed to trigger recompilation. Even after deleting the build output directory (out/host_release), Ninja reported “no work to do” and reused cached artifacts.
RX Root Cause
The core issue was incomplete dependency tracking in GN/Ninja for Dart SDK sources:
- Ninja’s dependency graph did not monitor Dart SDK source files due to missing declarations in GN build files
- The
flutter_windows.dlltarget lacked explicit dependencies on Dart SDK header/source files - Deleting
out/host_releasepreserved Ninja’s global state cache (.ninja_log/.ninja_deps) which maintained old dependency rules
Why This Happens in Real Systems
- Implicit vs. explicit dependencies:
- Build systems treat third-party code as static without explicit linkage
- Header/source files outside immediate targets often require manual dependency declarations
- Persistence of build state:
- Deleting output directories doesn’t reset Ninja’s global caching mechanism
- Historical build graphs persist across directory deletions
- Assumptions about “source”:
- Engineers expect changes to propagate automatically, but build systems track only declared inputs
Real-World Impact
- Undetected code changes: Modified SDK logic is silently ignored during compilation
- Wasted debugging time: Engineers assume bugs in their code rather than build configuration
- Broken customizations: Critical SDK fixes/experiments fail to apply
- False confidence: “Successful” builds with outdated dependencies create production risks