defaultScaffoldProcessRunner function
Implementation
Future<void> defaultScaffoldProcessRunner(
String executable,
List<String> args, {
String? workingDirectory,
Duration? timeout,
}) async {
try {
Process process;
if (Platform.isWindows) {
process = await Process.start('cmd', [
'/c',
executable,
...args,
], workingDirectory: workingDirectory);
} else {
process = await Process.start(
executable,
args,
workingDirectory: workingDirectory,
);
}
// listen + Completer instead of addStream: allows cancelling
// subscriptions on timeout to free stdout/stderr for subsequent calls.
final stdoutDone = Completer<void>();
final stderrDone = Completer<void>();
final stdoutSub = process.stdout.listen(
stdout.add,
onDone: stdoutDone.complete,
);
final stderrSub = process.stderr.listen(
stderr.add,
onDone: stderrDone.complete,
);
Future<void> drainStreams() =>
Future.wait([stdoutDone.future, stderrDone.future]);
void killProcessTree() {
if (Platform.isWindows) {
// process.kill only kills cmd.exe, not child processes spawned by
// flutter.bat — taskkill /T kills the entire process tree.
try {
Process.runSync('taskkill', ['/F', '/T', '/PID', '${process.pid}']);
} catch (e) {
logger.warn('taskkill failed, falling back to sigkill: $e');
process.kill(ProcessSignal.sigkill);
}
} else {
process.kill(ProcessSignal.sigkill);
}
}
if (timeout != null) {
final exitCode = await process.exitCode.timeout(
timeout,
onTimeout: () {
killProcessTree();
return -1;
},
);
if (exitCode == -1) {
stdoutSub.cancel();
stderrSub.cancel();
throw TimeoutException(
'$executable ${args.join(' ')} timed out after '
'${timeout.inSeconds} seconds and was terminated.',
);
}
await drainStreams();
if (exitCode != 0) {
throw Exception(
'$executable ${args.join(' ')} failed with exit code $exitCode',
);
}
} else {
await drainStreams();
final exitCode = await process.exitCode;
if (exitCode != 0) {
throw Exception(
'$executable ${args.join(' ')} failed with exit code $exitCode',
);
}
}
} on ProcessException catch (e) {
throw Exception(
'Unable to run $executable. Make sure it is installed and added to PATH.\n'
'Error: ${e.message}',
);
}
}