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();
|
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, {
|
|||||||
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, {
|
|||||||
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, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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';
|
|||||||
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 {
|
|||||||
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,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', () {
|
group('ProcessManager on windows throws tool exit', () {
|
||||||
const int kDeviceFull = 112;
|
const int kDeviceFull = 112;
|
||||||
const int kUserMappedSectionOpened = 1224;
|
const int kUserMappedSectionOpened = 1224;
|
||||||
const int kUserPermissionDenied = 5;
|
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', () {
|
test('when the device is full', () {
|
||||||
final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
|
final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
|
||||||
const FakeCommand(command: <String>['foo'], exception: ProcessException('', <String>[], '', kDeviceFull)),
|
const FakeCommand(command: <String>['foo'], exception: ProcessException('', <String>[], '', kDeviceFull)),
|
||||||
|
Loading…
Reference in New Issue
Block a user