mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
[flutter_tools] consume package:process exceptions (#79657)
This commit is contained in:
parent
d60cc08877
commit
304448af8c
@ -533,6 +533,8 @@ class ErrorHandlingLink
|
||||
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 @@ Future<T> _run<T>(Future<T> Function() op, {
|
||||
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 @@ T _runSync<T>(T Function() op, {
|
||||
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 @@ 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.
|
||||
///
|
||||
@ -662,21 +611,6 @@ class ErrorHandlingProcessManager extends ProcessManager {
|
||||
|
||||
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 @@ class ErrorHandlingProcessManager extends ProcessManager {
|
||||
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 @@ class ErrorHandlingProcessManager extends ProcessManager {
|
||||
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 @@ class ErrorHandlingProcessManager extends ProcessManager {
|
||||
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,
|
||||
|
@ -11,7 +11,6 @@ import 'package:process/process.dart';
|
||||
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 @@ class _DefaultPub implements Pub {
|
||||
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)
|
||||
|
@ -684,25 +684,47 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
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)),
|
||||
|
Loading…
Reference in New Issue
Block a user