-
Notifications
You must be signed in to change notification settings - Fork 221
Description
I work on many different Dart and Flutter projects and often use build_runner to manage generated code.
When build_runner watch detects a graph update and outputs the following:
[INFO] Caching finalized dependency graph...
[INFO] Caching finalized dependency graph completed, took 61ms
[INFO] Succeeded after 91ms with 0 outputs (0 actions)
(Update pubspec.yaml and run dart pub get in a different terminal here...)
[SEVERE] Terminating builds due to package graph update, please restart the build.
[INFO] Terminating. No further builds will be scheduled
[INFO] Builds finished. Safe to exit
(Terminates with exit code 0.)
This behavior forces developers to experience the following:
- While we are busy refactoring our code, suddenly things that depend on generated code have errors, or we don't see properties or methods that were supposed to be generated. We see a lot of red in the editor, have to switch to the
build_runner watchterminal to see what happened, and see that the process has exited. - Re-run
build_runner watch, rinse, and repeat. - Finally, when we get tired of this, go through several iterations of shell scripts, because
build_runnerexits0whether it was terminated with Control-C (SIGINT) or for the above reason. So we cannot determine via exit code the reason for terminating, so we can't just loop and re-run it forever, because that would also put us in an endless loop, even when we want to exit.
- Shouldn't a
SEVEREerror as shown in the console at least return an error status (maybe1instead of0)?
Yes, I have technically "solved" the problem for myself with the following script that I can drop into just or whatever process I use, but I must do this for every Dart/Flutter project, or create a general bash function in my environment that I can invoke to wrap build_runner:
#!/usr/bin/env bash
set -o pipefail
trap 'echo "Exiting..."; exit 1' INT
while true; do
tmpfile=$(mktemp)
dart run build_runner watch -d 2>&1 | tee $tmpfile
if grep -q "No further builds will be scheduled" $tmpfile; then
rm $tmpfile
echo "RESTARTING build_runner."
continue
fi
rm $tmpfile
sleep 1
doneBut it took me several iterations, and having to tee and grep a temp file because the exit status is always 0. I argue that this experience:
- interrupts developers' workflows;
- confuses developers when their builds silently stop running;
- wastes time and energy developing and maintaining wrapper scripts that they shouldn't have to.
I recall reading somewhere in this repo that there were technical reasons for terminating the build process on dependency graph update. I appreciate and respect that.
I also argue that trying to "fix" such technical limitations to achieve "incremental" rebuilds across dependency graph changes, e.g. trying to "diff" two different dependency graphs (I'm no Dart internals expert, only guessing), is probably way overkill for the minimal speed/performance gain it might achieve. Using my script above, sure, a rebuild may take longer than an incremental build, but I don't even notice it, as it almost always happens faster than I can update my dependencies and switch to any code in my editor that depends on the build.
So, the big question is: would it be possible to just "restart" the build by default from a "fresh" state, as if the user just re-ran the command, so that we wouldn't have to use wrapper scripts? Maybe there are edge cases in which it would error out, but why can't it just log the errors and wait for the developer to update their code again? And there could always be an option to revert to the old behavior.
The reason I ask for a change in the default behavior, as opposed to just adding another flag, is that I believe it is a normal developer expectation that when they run "watch", the watch just continuously runs until interrupted. Further, we already have so many flags to keep track of, like --delete-conflicting-outputs / -d.
I would be happy to be a tester of this feature or provide any assistance that I could. I have a lot of experience in Dart apps, but not its internals. Due to this, I would be happy to attempt and do a PR, but the style or method of my solution may not be in line with Dart authors' expectations. Thank you for your consideration.