Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions packages/flutter_tools/lib/runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,12 @@ Future<int> _handleToolError(
}
} else {
// We've crashed; emit a log report.
safeStdioWrite(stderr, '\n');
globals.stdio.stderrWrite('\n');

if (!reportCrashes) {
// Print the stack trace on the bots - don't write a crash report.
safeStdioWrite(stderr, '$error\n');
safeStdioWrite(stderr, '$stackTrace\n');
globals.stdio.stderrWrite('$error\n');
globals.stdio.stderrWrite('$stackTrace\n');
return _exit(1);
}

Expand All @@ -138,8 +138,7 @@ Future<int> _handleToolError(

return _exit(1);
} catch (error) {
safeStdioWrite(
stderr,
globals.stdio.stderrWrite(
'Unable to generate crash report due to secondary error: $error\n'
'please let us know at https://github.com/flutter/flutter/issues.\n',
);
Expand Down
27 changes: 21 additions & 6 deletions packages/flutter_tools/lib/src/android/android_workflow.dart
Original file line number Diff line number Diff line change
Expand Up @@ -323,19 +323,34 @@ class AndroidLicenseValidator extends DoctorValidator {

// The real stdin will never finish streaming. Pipe until the child process
// finishes.
unawaited(process.stdin.addStream(stdin));
unawaited(process.stdin.addStream(globals.stdio.stdin)
// If the process exits unexpectedly with an error, that will be
// handled by the caller.
.catchError((dynamic err, StackTrace stack) {
globals.printTrace('Echoing stdin to the licenses subprocess failed:');
globals.printTrace('$err\n$stack');
}
));

// Wait for stdout and stderr to be fully processed, because process.exitCode
// may complete first.
await waitGroup<void>(<Future<void>>[
stdout.addStream(process.stdout),
stderr.addStream(process.stderr),
]);
try {
await waitGroup<void>(<Future<void>>[
globals.stdio.addStdoutStream(process.stdout),
globals.stdio.addStderrStream(process.stderr),
]);
} catch (err, stack) {
globals.printTrace('Echoing stdout or stderr from the license subprocess failed:');
globals.printTrace('$err\n$stack');
}

final int exitCode = await process.exitCode;
return exitCode == 0;
} on ProcessException catch (e) {
throwToolExit(userMessages.androidCannotRunSdkManager(
androidSdk.sdkManagerPath, e.toString()));
androidSdk.sdkManagerPath,
e.toString(),
));
return false;
}
}
Expand Down
56 changes: 38 additions & 18 deletions packages/flutter_tools/lib/src/base/io.dart
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,17 @@ class _PosixProcessSignal extends ProcessSignal {
}
}

/// A class that wraps stdout, stderr, and stdin, and exposes the allowed
/// operations.
class Stdio {
const Stdio();

Stream<List<int>> get stdin => io.stdin;

@visibleForTesting
io.Stdout get stdout => io.stdout;

@visibleForTesting
io.IOSink get stderr => io.stderr;

bool get hasTerminal => io.stdout.hasTerminal;
Expand Down Expand Up @@ -246,27 +252,41 @@ class Stdio {
int get terminalColumns => hasTerminal ? io.stdout.terminalColumns : null;
int get terminalLines => hasTerminal ? io.stdout.terminalLines : null;
bool get supportsAnsiEscapes => hasTerminal && io.stdout.supportsAnsiEscapes;
}

io.Stdout get stdout => globals.stdio.stdout;
Stream<List<int>> get stdin => globals.stdio.stdin;
io.IOSink get stderr => globals.stdio.stderr;
bool get stdinHasTerminal => globals.stdio.stdinHasTerminal;

/// Writes [message] to [sink], falling back on [fallback] if the write
/// throws any exception. The default fallback calls [print] on [message].
void safeStdioWrite(io.IOSink sink, String message, {
void Function(String, dynamic, StackTrace) fallback,
}) {
try {
sink.write(message);
} catch (err, stack) {
if (fallback == null) {
print(message);
} else {
fallback(message, err, stack);
/// Writes [message] to [stderr], falling back on [fallback] if the write
/// throws any exception. The default fallback calls [print] on [message].
void stderrWrite(
String message, {
void Function(String, dynamic, StackTrace) fallback,
}) => _stdioWrite(stderr, message, fallback: fallback);

/// Writes [message] to [stdout], falling back on [fallback] if the write
/// throws any exception. The default fallback calls [print] on [message].
void stdoutWrite(
String message, {
void Function(String, dynamic, StackTrace) fallback,
}) => _stdioWrite(stdout, message, fallback: fallback);

// Helper for safeStderrWrite and safeStdoutWrite.
void _stdioWrite(io.IOSink sink, String message, {
void Function(String, dynamic, StackTrace) fallback,
}) {
try {
sink.write(message);
} catch (err, stack) {
if (fallback == null) {
print(message);
} else {
fallback(message, err, stack);
}
}
}

/// Adds [stream] to [stdout].
Future<void> addStdoutStream(Stream<List<int>> stream) => stdout.addStream(stream);

/// Adds [srtream] to [stderr].
Future<void> addStderrStream(Stream<List<int>> stream) => stderr.addStream(stream);
}

// TODO(zra): Move pid and writePidFile into `ProcessInfo`.
Expand Down
20 changes: 6 additions & 14 deletions packages/flutter_tools/lib/src/base/logger.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'package:platform/platform.dart';

import '../base/context.dart';
import '../globals.dart' as globals;
import 'io.dart' hide stderr, stdin, stdout;
import 'io.dart';
import 'terminal.dart' show AnsiTerminal, TerminalColor, OutputPreferences;
import 'utils.dart';

Expand Down Expand Up @@ -268,14 +268,10 @@ class StdoutLogger extends Logger {
}

@protected
void writeToStdOut(String message) {
safeStdioWrite(_stdio.stdout, message);
}
void writeToStdOut(String message) => _stdio.stdoutWrite(message);

@protected
void writeToStdErr(String message) {
safeStdioWrite(_stdio.stderr, message);
}
void writeToStdErr(String message) => _stdio.stderrWrite(message);

@override
void printTrace(String message) { }
Expand Down Expand Up @@ -363,7 +359,7 @@ class WindowsStdoutLogger extends StdoutLogger {
final String windowsMessage = message
.replaceAll('✗', 'X')
.replaceAll('✓', '√');
safeStdioWrite(_stdio.stdout, windowsMessage);
_stdio.stdoutWrite(windowsMessage);
}
}

Expand Down Expand Up @@ -794,9 +790,7 @@ class SummaryStatus extends Status {
super.start();
}

void _writeToStdOut(String message) {
safeStdioWrite(_stdio.stdout, message);
}
void _writeToStdOut(String message) => _stdio.stdoutWrite(message);

void _printMessage() {
assert(!_messageShowingOnCurrentLine);
Expand Down Expand Up @@ -903,9 +897,7 @@ class AnsiSpinner extends Status {
_startSpinner();
}

void _writeToStdOut(String message) {
safeStdioWrite(_stdio.stdout, message);
}
void _writeToStdOut(String message) => _stdio.stdoutWrite(message);

void _startSpinner() {
_writeToStdOut(_clear); // for _callback to backspace over
Expand Down
13 changes: 7 additions & 6 deletions packages/flutter_tools/lib/src/commands/daemon.dart
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ class Daemon {
final dynamic id = request['id'];

if (id == null) {
safeStdioWrite(stderr, 'no id for request: $request\n');
globals.stdio.stderrWrite('no id for request: $request\n');
return;
}

Expand Down Expand Up @@ -323,9 +323,11 @@ class DaemonDomain extends Domain {
// capture the print output for testing.
print(message.message);
} else if (message.level == 'error') {
safeStdioWrite(stderr, '${message.message}\n');
globals.stdio.stderrWrite('${message.message}\n');
if (message.stackTrace != null) {
safeStdioWrite(stderr, '${message.stackTrace.toString().trimRight()}\n');
globals.stdio.stderrWrite(
'${message.stackTrace.toString().trimRight()}\n',
);
}
}
} else {
Expand Down Expand Up @@ -862,7 +864,7 @@ class DeviceDomain extends Domain {
}
}

Stream<Map<String, dynamic>> get stdinCommandStream => stdin
Stream<Map<String, dynamic>> get stdinCommandStream => globals.stdio.stdin
.transform<String>(utf8.decoder)
.transform<String>(const LineSplitter())
.where((String line) => line.startsWith('[{') && line.endsWith('}]'))
Expand All @@ -872,8 +874,7 @@ Stream<Map<String, dynamic>> get stdinCommandStream => stdin
});

void stdoutCommandResponse(Map<String, dynamic> command) {
safeStdioWrite(
stdout,
globals.stdio.stdoutWrite(
'[${jsonEncodeObject(command)}]\n',
fallback: (String message, dynamic error, StackTrace stack) {
throwToolExit('Failed to write daemon command response to stdout: $error');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import 'package:completion/completion.dart';

import '../base/common.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../globals.dart' as globals;
import '../runner/flutter_command.dart';

Expand Down Expand Up @@ -50,7 +49,7 @@ class ShellCompletionCommand extends FlutterCommand {

if (argResults.rest.isEmpty || argResults.rest.first == '-') {
final String script = generateCompletionScript(<String>['flutter']);
safeStdioWrite(stdout, script);
globals.stdio.stdoutWrite(script);
return FlutterCommandResult.warning();
}

Expand Down
6 changes: 3 additions & 3 deletions packages/flutter_tools/lib/src/dart/pub.dart
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ class _DefaultPub implements Pub {
);

// Pipe the Flutter tool stdin to the pub stdin.
unawaited(process.stdin.addStream(io.stdin)
unawaited(process.stdin.addStream(globals.stdio.stdin)
// If pub exits unexpectedly with an error, that will be reported below
// by the tool exit after the exit code check.
.catchError((dynamic err, StackTrace stack) {
Expand All @@ -320,8 +320,8 @@ class _DefaultPub implements Pub {
// Pipe the pub stdout and stderr to the tool stdout and stderr.
try {
await Future.wait<dynamic>(<Future<dynamic>>[
io.stdout.addStream(process.stdout),
io.stderr.addStream(process.stderr),
globals.stdio.addStdoutStream(process.stdout),
globals.stdio.addStderrStream(process.stderr),
]);
} catch (err, stack) {
globals.printTrace('Echoing stdout or stderr from the pub subprocess failed:');
Expand Down
3 changes: 2 additions & 1 deletion packages/flutter_tools/lib/src/runner/flutter_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,8 @@ abstract class FlutterCommand extends Command<void> {
final Map<CustomDimensions, String> additionalUsageValues =
<CustomDimensions, String>{
...?await usageValues,
CustomDimensions.commandHasTerminal: io.stdout.hasTerminal ? 'true' : 'false',
CustomDimensions.commandHasTerminal:
globals.stdio.hasTerminal ? 'true' : 'false',
};
Usage.command(commandPath, parameters: additionalUsageValues);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/flutter_tools/lib/src/test/event_printer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import '../base/io.dart' show stdout;
import '../convert.dart';
import '../globals.dart' as globals;
import 'watcher.dart';

/// Prints JSON events when running a test in --machine mode.
class EventPrinter extends TestWatcher {
EventPrinter({StringSink out}) : _out = out ?? stdout;
EventPrinter({StringSink out}) : _out = out ?? globals.stdio.stdout;

final StringSink _out;

Expand Down
15 changes: 11 additions & 4 deletions packages/flutter_tools/test/general.shard/base/logger_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,18 @@ final String bold = RegExp.escape(AnsiTerminal.bold);
final String resetBold = RegExp.escape(AnsiTerminal.resetBold);
final String resetColor = RegExp.escape(AnsiTerminal.resetColor);

class MockStdio extends Mock implements Stdio {}
class MockStdout extends Mock implements Stdout {}

class ThrowingStdio extends Stdio {
ThrowingStdio(this.stdout, this.stderr);

@override
final Stdout stdout;

@override
final IOSink stderr;
}

void main() {
group('AppContext', () {
FakeStopwatch fakeStopWatch;
Expand Down Expand Up @@ -82,13 +91,11 @@ void main() {
});

testWithoutContext('Logger does not throw when stdio write throws', () async {
final MockStdio stdio = MockStdio();
final MockStdout stdout = MockStdout();
final MockStdout stderr = MockStdout();
final ThrowingStdio stdio = ThrowingStdio(stdout, stderr);
bool stdoutThrew = false;
bool stderrThrew = false;
when(stdio.stdout).thenReturn(stdout);
when(stdio.stderr).thenReturn(stderr);
when(stdout.write(any)).thenAnswer((_) {
stdoutThrew = true;
throw 'Error';
Expand Down