Unverified Commit 304448af authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] consume package:process exceptions (#79657)

parent d60cc088
...@@ -533,6 +533,8 @@ class ErrorHandlingLink ...@@ -533,6 +533,8 @@ class ErrorHandlingLink
String toString() => delegate.toString(); String toString() => delegate.toString();
} }
const String _kNoExecutableFound = 'The Flutter tool could not locate an executable with suitable permissions';
Future<T> _run<T>(Future<T> Function() op, { Future<T> _run<T>(Future<T> Function() op, {
required Platform platform, required Platform platform,
String? failureMessage, String? failureMessage,
...@@ -540,6 +542,11 @@ Future<T> _run<T>(Future<T> Function() op, { ...@@ -540,6 +542,11 @@ Future<T> _run<T>(Future<T> Function() op, {
assert(platform != null); assert(platform != null);
try { try {
return await op(); return await op();
} on ProcessPackageExecutableNotFoundException catch (e) {
if (e.candidates.isNotEmpty) {
throwToolExit('$_kNoExecutableFound: $e');
}
rethrow;
} on FileSystemException catch (e) { } on FileSystemException catch (e) {
if (platform.isWindows) { if (platform.isWindows) {
_handleWindowsException(e, failureMessage, e.osError?.errorCode ?? 0); _handleWindowsException(e, failureMessage, e.osError?.errorCode ?? 0);
...@@ -564,6 +571,11 @@ T _runSync<T>(T Function() op, { ...@@ -564,6 +571,11 @@ T _runSync<T>(T Function() op, {
assert(platform != null); assert(platform != null);
try { try {
return op(); return op();
} on ProcessPackageExecutableNotFoundException catch (e) {
if (e.candidates.isNotEmpty) {
throwToolExit('$_kNoExecutableFound: $e');
}
rethrow;
} on FileSystemException catch (e) { } on FileSystemException catch (e) {
if (platform.isWindows) { if (platform.isWindows) {
_handleWindowsException(e, failureMessage, e.osError?.errorCode ?? 0); _handleWindowsException(e, failureMessage, e.osError?.errorCode ?? 0);
...@@ -581,69 +593,6 @@ T _runSync<T>(T Function() op, { ...@@ -581,69 +593,6 @@ T _runSync<T>(T Function() op, {
} }
} }
class _ProcessDelegate {
const _ProcessDelegate();
Future<io.Process> start(
List<String> command, {
String? workingDirectory,
Map<String, String>? environment,
bool includeParentEnvironment = true,
bool runInShell = false,
io.ProcessStartMode mode = io.ProcessStartMode.normal,
}) {
return io.Process.start(
command[0],
command.skip(1).toList(),
workingDirectory: workingDirectory,
environment: environment,
includeParentEnvironment: includeParentEnvironment,
runInShell: runInShell,
);
}
Future<io.ProcessResult> run(
List<String> command, {
String? workingDirectory,
Map<String, String>? environment,
bool includeParentEnvironment = true,
bool runInShell = false,
Encoding stdoutEncoding = io.systemEncoding,
Encoding stderrEncoding = io.systemEncoding,
}) {
return io.Process.run(
command[0],
command.skip(1).toList(),
workingDirectory: workingDirectory,
environment: environment,
includeParentEnvironment: includeParentEnvironment,
runInShell: runInShell,
stdoutEncoding: stdoutEncoding,
stderrEncoding: stderrEncoding,
);
}
io.ProcessResult runSync(
List<String> command, {
String? workingDirectory,
Map<String, String>? environment,
bool includeParentEnvironment = true,
bool runInShell = false,
Encoding stdoutEncoding = io.systemEncoding,
Encoding stderrEncoding = io.systemEncoding,
}) {
return io.Process.runSync(
command[0],
command.skip(1).toList(),
workingDirectory: workingDirectory,
environment: environment,
includeParentEnvironment: includeParentEnvironment,
runInShell: runInShell,
stdoutEncoding: stdoutEncoding,
stderrEncoding: stderrEncoding,
);
}
}
/// A [ProcessManager] that throws a [ToolExit] on certain errors. /// A [ProcessManager] that throws a [ToolExit] on certain errors.
/// ///
...@@ -662,21 +611,6 @@ class ErrorHandlingProcessManager extends ProcessManager { ...@@ -662,21 +611,6 @@ class ErrorHandlingProcessManager extends ProcessManager {
final ProcessManager _delegate; final ProcessManager _delegate;
final Platform _platform; final Platform _platform;
static const _ProcessDelegate _processDelegate = _ProcessDelegate();
static bool _skipCommandLookup = false;
/// Bypass package:process command lookup for all functions in this block.
///
/// This required that the fully resolved executable path is provided.
static Future<T> skipCommandLookup<T>(Future<T> Function() operation) async {
final bool previousValue = ErrorHandlingProcessManager._skipCommandLookup;
try {
ErrorHandlingProcessManager._skipCommandLookup = true;
return await operation();
} finally {
ErrorHandlingProcessManager._skipCommandLookup = previousValue;
}
}
@override @override
bool canRun(dynamic executable, {String? workingDirectory}) { bool canRun(dynamic executable, {String? workingDirectory}) {
...@@ -705,17 +639,6 @@ class ErrorHandlingProcessManager extends ProcessManager { ...@@ -705,17 +639,6 @@ class ErrorHandlingProcessManager extends ProcessManager {
Encoding stderrEncoding = io.systemEncoding, Encoding stderrEncoding = io.systemEncoding,
}) { }) {
return _run(() { return _run(() {
if (_skipCommandLookup && _delegate is LocalProcessManager) {
return _processDelegate.run(
command.cast<String>(),
workingDirectory: workingDirectory,
environment: environment,
includeParentEnvironment: includeParentEnvironment,
runInShell: runInShell,
stdoutEncoding: stdoutEncoding,
stderrEncoding: stderrEncoding,
);
}
return _delegate.run( return _delegate.run(
command, command,
workingDirectory: workingDirectory, workingDirectory: workingDirectory,
...@@ -738,15 +661,6 @@ class ErrorHandlingProcessManager extends ProcessManager { ...@@ -738,15 +661,6 @@ class ErrorHandlingProcessManager extends ProcessManager {
io.ProcessStartMode mode = io.ProcessStartMode.normal, io.ProcessStartMode mode = io.ProcessStartMode.normal,
}) { }) {
return _run(() { return _run(() {
if (_skipCommandLookup && _delegate is LocalProcessManager) {
return _processDelegate.start(
command.cast<String>(),
workingDirectory: workingDirectory,
environment: environment,
includeParentEnvironment: includeParentEnvironment,
runInShell: runInShell,
);
}
return _delegate.start( return _delegate.start(
command, command,
workingDirectory: workingDirectory, workingDirectory: workingDirectory,
...@@ -768,17 +682,6 @@ class ErrorHandlingProcessManager extends ProcessManager { ...@@ -768,17 +682,6 @@ class ErrorHandlingProcessManager extends ProcessManager {
Encoding stderrEncoding = io.systemEncoding, Encoding stderrEncoding = io.systemEncoding,
}) { }) {
return _runSync(() { return _runSync(() {
if (_skipCommandLookup && _delegate is LocalProcessManager) {
return _processDelegate.runSync(
command.cast<String>(),
workingDirectory: workingDirectory,
environment: environment,
includeParentEnvironment: includeParentEnvironment,
runInShell: runInShell,
stdoutEncoding: stdoutEncoding,
stderrEncoding: stderrEncoding,
);
}
return _delegate.runSync( return _delegate.runSync(
command, command,
workingDirectory: workingDirectory, workingDirectory: workingDirectory,
......
...@@ -11,7 +11,6 @@ import 'package:process/process.dart'; ...@@ -11,7 +11,6 @@ import 'package:process/process.dart';
import '../base/bot_detector.dart'; import '../base/bot_detector.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../base/context.dart'; import '../base/context.dart';
import '../base/error_handling_io.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/io.dart' as io; import '../base/io.dart' as io;
import '../base/logger.dart'; import '../base/logger.dart';
...@@ -334,13 +333,11 @@ class _DefaultPub implements Pub { ...@@ -334,13 +333,11 @@ class _DefaultPub implements Pub {
bool generateSyntheticPackage = false, bool generateSyntheticPackage = false,
}) async { }) async {
// Fully resolved pub or pub.bat is calculated based on current platform. // Fully resolved pub or pub.bat is calculated based on current platform.
final io.Process process = await ErrorHandlingProcessManager.skipCommandLookup(() async { final io.Process process = await _processUtils.start(
return _processUtils.start(
_pubCommand(arguments), _pubCommand(arguments),
workingDirectory: directory, workingDirectory: directory,
environment: await _createPubEnvironment(PubContext.interactive), environment: await _createPubEnvironment(PubContext.interactive),
); );
});
// Pipe the Flutter tool stdin to the pub stdin. // Pipe the Flutter tool stdin to the pub stdin.
unawaited(process.stdin.addStream(stdio.stdin) unawaited(process.stdin.addStream(stdio.stdin)
......
...@@ -684,24 +684,46 @@ void main() { ...@@ -684,24 +684,46 @@ void main() {
}); });
}); });
test('skipCommandLookup invokes Process calls directly', () async { group('ProcessManager on windows throws tool exit', () {
final ErrorHandlingProcessManager processManager = ErrorHandlingProcessManager( const int kDeviceFull = 112;
delegate: const LocalProcessManager(), const int kUserMappedSectionOpened = 1224;
const int kUserPermissionDenied = 5;
test('when PackageProcess throws an exception containg non-executable bits', () {
final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(command: <String>['foo'], exception: ProcessPackageExecutableNotFoundException('', candidates: <String>['not-empty'])),
const FakeCommand(command: <String>['foo'], exception: ProcessPackageExecutableNotFoundException('', candidates: <String>['not-empty'])),
]);
final ProcessManager processManager = ErrorHandlingProcessManager(
delegate: fakeProcessManager,
platform: windowsPlatform, platform: windowsPlatform,
); );
// Throws process exception because the executable does not exist. const String expectedMessage = 'The Flutter tool could not locate an executable with suitable permissions';
await ErrorHandlingProcessManager.skipCommandLookup<void>(() async {
expect(() => processManager.runSync(<String>['foo']), throwsA(isA<ProcessException>())); expect(() async => processManager.start(<String>['foo']),
expect(() => processManager.run(<String>['foo']), throwsA(isA<ProcessException>())); throwsToolExit(message: expectedMessage));
expect(() => processManager.start(<String>['foo']), throwsA(isA<ProcessException>())); expect(() async => processManager.runSync(<String>['foo']),
}); throwsToolExit(message: expectedMessage));
}); });
group('ProcessManager on windows throws tool exit', () { test('when PackageProcess throws an exception without containing non-executable bits', () {
const int kDeviceFull = 112; final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
const int kUserMappedSectionOpened = 1224; const FakeCommand(command: <String>['foo'], exception: ProcessPackageExecutableNotFoundException('', candidates: <String>[])),
const int kUserPermissionDenied = 5; const FakeCommand(command: <String>['foo'], exception: ProcessPackageExecutableNotFoundException('', candidates: <String>[])),
]);
final ProcessManager processManager = ErrorHandlingProcessManager(
delegate: fakeProcessManager,
platform: windowsPlatform,
);
// If there were no located executables treat this as a programming error and rethrow the original
// exception.
expect(() async => processManager.start(<String>['foo']), throwsProcessException());
expect(() async => processManager.runSync(<String>['foo']), throwsProcessException());
});
test('when the device is full', () { test('when the device is full', () {
final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[ final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment