[flutter_tools] consume package:process exceptions (#79657)
diff --git a/packages/flutter_tools/lib/src/base/error_handling_io.dart b/packages/flutter_tools/lib/src/base/error_handling_io.dart
index c00036c..e369c09 100644
--- a/packages/flutter_tools/lib/src/base/error_handling_io.dart
+++ b/packages/flutter_tools/lib/src/base/error_handling_io.dart
@@ -533,6 +533,8 @@
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, {
required Platform platform,
String? failureMessage,
@@ -540,6 +542,11 @@
assert(platform != null);
try {
return await op();
+ } on ProcessPackageExecutableNotFoundException catch (e) {
+ if (e.candidates.isNotEmpty) {
+ throwToolExit('$_kNoExecutableFound: $e');
+ }
+ rethrow;
} on FileSystemException catch (e) {
if (platform.isWindows) {
_handleWindowsException(e, failureMessage, e.osError?.errorCode ?? 0);
@@ -564,6 +571,11 @@
assert(platform != null);
try {
return op();
+ } on ProcessPackageExecutableNotFoundException catch (e) {
+ if (e.candidates.isNotEmpty) {
+ throwToolExit('$_kNoExecutableFound: $e');
+ }
+ rethrow;
} on FileSystemException catch (e) {
if (platform.isWindows) {
_handleWindowsException(e, failureMessage, e.osError?.errorCode ?? 0);
@@ -581,69 +593,6 @@
}
}
-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.
///
@@ -662,21 +611,6 @@
final ProcessManager _delegate;
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
bool canRun(dynamic executable, {String? workingDirectory}) {
@@ -705,17 +639,6 @@
Encoding stderrEncoding = io.systemEncoding,
}) {
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(
command,
workingDirectory: workingDirectory,
@@ -738,15 +661,6 @@
io.ProcessStartMode mode = io.ProcessStartMode.normal,
}) {
return _run(() {
- if (_skipCommandLookup && _delegate is LocalProcessManager) {
- return _processDelegate.start(
- command.cast<String>(),
- workingDirectory: workingDirectory,
- environment: environment,
- includeParentEnvironment: includeParentEnvironment,
- runInShell: runInShell,
- );
- }
return _delegate.start(
command,
workingDirectory: workingDirectory,
@@ -768,17 +682,6 @@
Encoding stderrEncoding = io.systemEncoding,
}) {
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(
command,
workingDirectory: workingDirectory,
diff --git a/packages/flutter_tools/lib/src/dart/pub.dart b/packages/flutter_tools/lib/src/dart/pub.dart
index 535a08b..f48658e 100644
--- a/packages/flutter_tools/lib/src/dart/pub.dart
+++ b/packages/flutter_tools/lib/src/dart/pub.dart
@@ -11,7 +11,6 @@
import '../base/bot_detector.dart';
import '../base/common.dart';
import '../base/context.dart';
-import '../base/error_handling_io.dart';
import '../base/file_system.dart';
import '../base/io.dart' as io;
import '../base/logger.dart';
@@ -334,13 +333,11 @@
bool generateSyntheticPackage = false,
}) async {
// Fully resolved pub or pub.bat is calculated based on current platform.
- final io.Process process = await ErrorHandlingProcessManager.skipCommandLookup(() async {
- return _processUtils.start(
- _pubCommand(arguments),
- workingDirectory: directory,
- environment: await _createPubEnvironment(PubContext.interactive),
- );
- });
+ final io.Process process = await _processUtils.start(
+ _pubCommand(arguments),
+ workingDirectory: directory,
+ environment: await _createPubEnvironment(PubContext.interactive),
+ );
// Pipe the Flutter tool stdin to the pub stdin.
unawaited(process.stdin.addStream(stdio.stdin)
diff --git a/packages/flutter_tools/test/general.shard/base/error_handling_io_test.dart b/packages/flutter_tools/test/general.shard/base/error_handling_io_test.dart
index ae4f2fa..4281522 100644
--- a/packages/flutter_tools/test/general.shard/base/error_handling_io_test.dart
+++ b/packages/flutter_tools/test/general.shard/base/error_handling_io_test.dart
@@ -684,25 +684,47 @@
});
});
- test('skipCommandLookup invokes Process calls directly', () async {
- final ErrorHandlingProcessManager processManager = ErrorHandlingProcessManager(
- delegate: const LocalProcessManager(),
- platform: windowsPlatform,
- );
-
- // Throws process exception because the executable does not exist.
- await ErrorHandlingProcessManager.skipCommandLookup<void>(() async {
- expect(() => processManager.runSync(<String>['foo']), throwsA(isA<ProcessException>()));
- expect(() => processManager.run(<String>['foo']), throwsA(isA<ProcessException>()));
- expect(() => processManager.start(<String>['foo']), throwsA(isA<ProcessException>()));
- });
- });
-
group('ProcessManager on windows throws tool exit', () {
const int kDeviceFull = 112;
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,
+ );
+
+ const String expectedMessage = 'The Flutter tool could not locate an executable with suitable permissions';
+
+ expect(() async => processManager.start(<String>['foo']),
+ throwsToolExit(message: expectedMessage));
+ expect(() async => processManager.runSync(<String>['foo']),
+ throwsToolExit(message: expectedMessage));
+ });
+
+ test('when PackageProcess throws an exception without containing non-executable bits', () {
+ final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
+ const FakeCommand(command: <String>['foo'], exception: ProcessPackageExecutableNotFoundException('', candidates: <String>[])),
+ 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', () {
final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(command: <String>['foo'], exception: ProcessException('', <String>[], '', kDeviceFull)),