killBuildRunner function
Kills any existing build_runner processes before starting a new one.
build_runner uses a lock file (.dart_tool/build/lock) so a second
invocation in the same project will hang indefinitely waiting for the
lock. This function uses two complementary strategies:
Strategy 1 — lock-file owner (most reliable)
Uses lsof -t to find the exact PID(s) holding the lock file and
kills them directly. This bypasses command-line truncation issues that
can cause pkill -f to miss the process (e.g. when Flutter/puro runs
build_runner via a long snapshot path).
Strategy 2 — pkill fallback
pkill -f build_runner catches any remaining dart processes whose
argv contains "build_runner" in case lsof found nothing (e.g. when
the lock file doesn't exist yet but the process is still starting).
Finally, the entire .dart_tool/build directory is deleted so no
stale lock can block the new process, even if the killed process held
the file handle right up to SIGKILL.
Returns the number of processes that were killed (0 = none were running).
Implementation
Future<int> killBuildRunner({String? workingDirectory}) async {
int killed = 0;
if (Platform.isWindows) {
killed += await _killBuildRunnerWindows();
} else {
killed += await _killBuildRunnerPosix(workingDirectory);
}
// ── Delete the entire .dart_tool/build directory ─────────────────────────
// Deleting just the lock file is insufficient — the OS may keep the file
// descriptor open briefly after SIGKILL. Removing the whole directory
// guarantees a clean slate. Retry once after a short pause.
if (workingDirectory != null) {
await _deleteBuildCache(workingDirectory);
}
return killed;
}