blob: 666b09043aad31d24a842b4777944423cea90007 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:process/process.dart';
(Future<int> exitCode, Stream<String> output) getProcessStreams(
Process process,
) {
final Completer<void> stdoutCompleter = Completer<void>();
final Completer<void> stderrCompleter = Completer<void>();
final StreamController<String> outputController = StreamController<String>();
final StreamSubscription<void> stdoutSub = process.stdout
.transform(utf8.decoder)
.transform<String>(const LineSplitter())
.listen(outputController.add, onDone: stdoutCompleter.complete);
// From looking at historic logs, it seems that the stderr output is rare.
// Instead of prefacing every line with [stdout] unnecessarily, we'll just
// use [stderr] to indicate that it's from the stderr stream.
//
// For example, a historic log which has 0 occurrences of stderr:
// https://gist.github.com/matanlurey/84cf9c903ef6d507dcb63d4c303ca45f
final StreamSubscription<void> stderrSub = process.stderr
.transform(utf8.decoder)
.transform<String>(const LineSplitter())
.map((String line) => '[stderr] $line')
.listen(outputController.add, onDone: stderrCompleter.complete);
final Future<int> exitCode = process.exitCode.then<int>((int code) async {
await (stdoutSub.cancel(), stderrSub.cancel()).wait;
outputController.close();
return code;
});
return (exitCode, outputController.stream);
}
/// Pipes the [process] streams and writes them to [out] sink.
///
/// If [out] is null, then the current [Process.stdout] is used as the sink.
Future<int> pipeProcessStreams(
Process process, {
StringSink? out,
}) async {
out ??= stdout;
final (Future<int> exitCode, Stream<String> output) = getProcessStreams(process);
output.listen(out.writeln);
return exitCode;
}
extension RunAndForward on ProcessManager {
/// Runs [cmd], and forwards the stdout and stderr pipes to the current process stdout pipe.
Future<int> runAndForward(List<String> cmd) async {
return pipeProcessStreams(await start(cmd), out: stdout);
}
/// Runs [cmd], and captures the stdout and stderr pipes.
Future<(int, StringBuffer)> runAndCapture(List<String> cmd) async {
final StringBuffer buffer = StringBuffer();
final int exitCode = await pipeProcessStreams(await start(cmd), out: buffer);
return (exitCode, buffer);
}
}