mirror of
https://github.com/flutter/flutter
synced 2024-10-13 03:32:55 +00:00
[flutter_tool] process.dart cleanup (#39899)
This commit is contained in:
parent
3712ea63d8
commit
73c10e8ced
|
@ -227,11 +227,14 @@ class AndroidDevice extends Device {
|
|||
String workingDirectory,
|
||||
bool allowReentrantFlutter = false,
|
||||
Map<String, String> environment}) {
|
||||
return runCheckedSync(adbCommandForDevice(params), workingDirectory: workingDirectory,
|
||||
allowReentrantFlutter: allowReentrantFlutter,
|
||||
environment: environment,
|
||||
whiteListFailures: allowHeapCorruptionOnWindows
|
||||
);
|
||||
return processUtils.runSync(
|
||||
adbCommandForDevice(params),
|
||||
throwOnError: true,
|
||||
workingDirectory: workingDirectory,
|
||||
allowReentrantFlutter: allowReentrantFlutter,
|
||||
environment: environment,
|
||||
whiteListFailures: allowHeapCorruptionOnWindows,
|
||||
).stdout.trim();
|
||||
}
|
||||
|
||||
Future<RunResult> runAdbCheckedAsync(
|
||||
|
@ -239,9 +242,13 @@ class AndroidDevice extends Device {
|
|||
String workingDirectory,
|
||||
bool allowReentrantFlutter = false,
|
||||
}) async {
|
||||
return runCheckedAsync(adbCommandForDevice(params), workingDirectory: workingDirectory,
|
||||
allowReentrantFlutter: allowReentrantFlutter,
|
||||
whiteListFailures: allowHeapCorruptionOnWindows);
|
||||
return processUtils.run(
|
||||
adbCommandForDevice(params),
|
||||
throwOnError: true,
|
||||
workingDirectory: workingDirectory,
|
||||
allowReentrantFlutter: allowReentrantFlutter,
|
||||
whiteListFailures: allowHeapCorruptionOnWindows,
|
||||
);
|
||||
}
|
||||
|
||||
bool _isValidAdbVersion(String adbVersion) {
|
||||
|
@ -268,13 +275,18 @@ class AndroidDevice extends Device {
|
|||
}
|
||||
|
||||
Future<bool> _checkForSupportedAdbVersion() async {
|
||||
if (androidSdk == null)
|
||||
if (androidSdk == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
final RunResult adbVersion = await runCheckedAsync(<String>[getAdbPath(androidSdk), 'version']);
|
||||
if (_isValidAdbVersion(adbVersion.stdout))
|
||||
final RunResult adbVersion = await processUtils.run(
|
||||
<String>[getAdbPath(androidSdk), 'version'],
|
||||
throwOnError: true,
|
||||
);
|
||||
if (_isValidAdbVersion(adbVersion.stdout)) {
|
||||
return true;
|
||||
}
|
||||
printError('The ADB at "${getAdbPath(androidSdk)}" is too old; please install version 1.0.39 or later.');
|
||||
} catch (error, trace) {
|
||||
printError('Error running ADB: $error', stackTrace: trace);
|
||||
|
@ -289,7 +301,10 @@ class AndroidDevice extends Device {
|
|||
// output lines like this, which we want to ignore:
|
||||
// adb server is out of date. killing..
|
||||
// * daemon started successfully *
|
||||
await runCheckedAsync(<String>[getAdbPath(androidSdk), 'start-server']);
|
||||
await processUtils.run(
|
||||
<String>[getAdbPath(androidSdk), 'start-server'],
|
||||
throwOnError: true,
|
||||
);
|
||||
|
||||
// Sample output: '22'
|
||||
final String sdkVersion = await _getProperty('ro.build.version.sdk');
|
||||
|
@ -320,7 +335,8 @@ class AndroidDevice extends Device {
|
|||
}
|
||||
|
||||
Future<String> _getDeviceApkSha1(ApplicationPackage app) async {
|
||||
final RunResult result = await runAsync(adbCommandForDevice(<String>['shell', 'cat', _getDeviceSha1Path(app)]));
|
||||
final RunResult result = await processUtils.run(
|
||||
adbCommandForDevice(<String>['shell', 'cat', _getDeviceSha1Path(app)]));
|
||||
return result.stdout;
|
||||
}
|
||||
|
||||
|
@ -363,7 +379,8 @@ class AndroidDevice extends Device {
|
|||
return false;
|
||||
|
||||
final Status status = logger.startProgress('Installing ${fs.path.relative(apk.file.path)}...', timeout: timeoutConfiguration.slowOperation);
|
||||
final RunResult installResult = await runAsync(adbCommandForDevice(<String>['install', '-t', '-r', apk.file.path]));
|
||||
final RunResult installResult = await processUtils.run(
|
||||
adbCommandForDevice(<String>['install', '-t', '-r', apk.file.path]));
|
||||
status.stop();
|
||||
// Some versions of adb exit with exit code 0 even on failure :(
|
||||
// Parsing the output to check for failures.
|
||||
|
@ -396,7 +413,11 @@ class AndroidDevice extends Device {
|
|||
|
||||
String uninstallOut;
|
||||
try {
|
||||
uninstallOut = (await runCheckedAsync(adbCommandForDevice(<String>['uninstall', app.id]))).stdout;
|
||||
final RunResult uninstallResult = await processUtils.run(
|
||||
adbCommandForDevice(<String>['uninstall', app.id]),
|
||||
throwOnError: true,
|
||||
);
|
||||
uninstallOut = uninstallResult.stdout;
|
||||
} catch (error) {
|
||||
printError('adb uninstall failed: $error');
|
||||
return false;
|
||||
|
@ -603,13 +624,13 @@ class AndroidDevice extends Device {
|
|||
@override
|
||||
Future<bool> stopApp(ApplicationPackage app) {
|
||||
final List<String> command = adbCommandForDevice(<String>['shell', 'am', 'force-stop', app.id]);
|
||||
return runCommandAndStreamOutput(command).then<bool>(
|
||||
return processUtils.stream(command).then<bool>(
|
||||
(int exitCode) => exitCode == 0 || allowHeapCorruptionOnWindows(exitCode));
|
||||
}
|
||||
|
||||
@override
|
||||
void clearLogs() {
|
||||
runSync(adbCommandForDevice(<String>['logcat', '-c']));
|
||||
processUtils.runSync(adbCommandForDevice(<String>['logcat', '-c']));
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -650,7 +671,10 @@ class AndroidDevice extends Device {
|
|||
Future<void> takeScreenshot(File outputFile) async {
|
||||
const String remotePath = '/data/local/tmp/flutter_screenshot.png';
|
||||
await runAdbCheckedAsync(<String>['shell', 'screencap', '-p', remotePath]);
|
||||
await runCheckedAsync(adbCommandForDevice(<String>['pull', remotePath, outputFile.path]));
|
||||
await processUtils.run(
|
||||
adbCommandForDevice(<String>['pull', remotePath, outputFile.path]),
|
||||
throwOnError: true,
|
||||
);
|
||||
await runAdbCheckedAsync(<String>['shell', 'rm', remotePath]);
|
||||
}
|
||||
|
||||
|
@ -675,7 +699,10 @@ List<AndroidDevice> getAdbDevices() {
|
|||
return <AndroidDevice>[];
|
||||
String text;
|
||||
try {
|
||||
text = runSync(<String>[adbPath, 'devices', '-l']);
|
||||
text = processUtils.runSync(
|
||||
<String>[adbPath, 'devices', '-l'],
|
||||
throwOnError: true,
|
||||
).stdout.trim();
|
||||
} on ArgumentError catch (exception) {
|
||||
throwToolExit('Unable to find "adb", check your Android SDK installation and '
|
||||
'ANDROID_HOME environment variable: ${exception.message}');
|
||||
|
@ -694,7 +721,7 @@ Future<List<String>> getAdbDeviceDiagnostics() async {
|
|||
if (adbPath == null)
|
||||
return <String>[];
|
||||
|
||||
final RunResult result = await runAsync(<String>[adbPath, 'devices', '-l']);
|
||||
final RunResult result = await processUtils.run(<String>[adbPath, 'devices', '-l']);
|
||||
if (result.exitCode != 0) {
|
||||
return <String>[];
|
||||
} else {
|
||||
|
@ -816,11 +843,12 @@ class _AdbLogReader extends DeviceLogReader {
|
|||
// Start the adb logcat process.
|
||||
final List<String> args = <String>['shell', '-x', 'logcat', '-v', 'time'];
|
||||
final String lastTimestamp = device.lastLogcatTimestamp;
|
||||
if (lastTimestamp != null)
|
||||
if (lastTimestamp != null) {
|
||||
_timeOrigin = _adbTimestampToDateTime(lastTimestamp);
|
||||
else
|
||||
} else {
|
||||
_timeOrigin = null;
|
||||
runCommand(device.adbCommandForDevice(args)).then<void>((Process process) {
|
||||
}
|
||||
processUtils.start(device.adbCommandForDevice(args)).then<void>((Process process) {
|
||||
_process = process;
|
||||
// We expect logcat streams to occasionally contain invalid utf-8,
|
||||
// see: https://github.com/flutter/flutter/pull/8864.
|
||||
|
@ -959,33 +987,37 @@ class _AndroidDevicePortForwarder extends DevicePortForwarder {
|
|||
|
||||
String stdout;
|
||||
try {
|
||||
stdout = runCheckedSync(device.adbCommandForDevice(
|
||||
<String>['forward', '--list']
|
||||
));
|
||||
} catch (error) {
|
||||
stdout = processUtils.runSync(
|
||||
device.adbCommandForDevice(<String>['forward', '--list']),
|
||||
throwOnError: true,
|
||||
).stdout.trim();
|
||||
} on ProcessException catch (error) {
|
||||
printError('Failed to list forwarded ports: $error.');
|
||||
return ports;
|
||||
}
|
||||
|
||||
final List<String> lines = LineSplitter.split(stdout).toList();
|
||||
for (String line in lines) {
|
||||
if (line.startsWith(device.id)) {
|
||||
final List<String> splitLine = line.split('tcp:');
|
||||
|
||||
// Sanity check splitLine.
|
||||
if (splitLine.length != 3)
|
||||
continue;
|
||||
|
||||
// Attempt to extract ports.
|
||||
final int hostPort = _extractPort(splitLine[1]);
|
||||
final int devicePort = _extractPort(splitLine[2]);
|
||||
|
||||
// Failed, skip.
|
||||
if (hostPort == null || devicePort == null)
|
||||
continue;
|
||||
|
||||
ports.add(ForwardedPort(hostPort, devicePort));
|
||||
if (!line.startsWith(device.id)) {
|
||||
continue;
|
||||
}
|
||||
final List<String> splitLine = line.split('tcp:');
|
||||
|
||||
// Sanity check splitLine.
|
||||
if (splitLine.length != 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Attempt to extract ports.
|
||||
final int hostPort = _extractPort(splitLine[1]);
|
||||
final int devicePort = _extractPort(splitLine[2]);
|
||||
|
||||
// Failed, skip.
|
||||
if (hostPort == null || devicePort == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ports.add(ForwardedPort(hostPort, devicePort));
|
||||
}
|
||||
|
||||
return ports;
|
||||
|
@ -994,23 +1026,35 @@ class _AndroidDevicePortForwarder extends DevicePortForwarder {
|
|||
@override
|
||||
Future<int> forward(int devicePort, { int hostPort }) async {
|
||||
hostPort ??= 0;
|
||||
final RunResult process = await runCheckedAsync(device.adbCommandForDevice(
|
||||
<String>['forward', 'tcp:$hostPort', 'tcp:$devicePort']
|
||||
));
|
||||
final List<String> forwardCommand = <String>[
|
||||
'forward',
|
||||
'tcp:$hostPort',
|
||||
'tcp:$devicePort',
|
||||
];
|
||||
final RunResult process = await processUtils.run(
|
||||
device.adbCommandForDevice(forwardCommand),
|
||||
throwOnError: true,
|
||||
);
|
||||
|
||||
if (process.stderr.isNotEmpty)
|
||||
if (process.stderr.isNotEmpty) {
|
||||
process.throwException('adb returned error:\n${process.stderr}');
|
||||
}
|
||||
|
||||
if (process.exitCode != 0) {
|
||||
if (process.stdout.isNotEmpty)
|
||||
if (process.stdout.isNotEmpty) {
|
||||
process.throwException('adb returned error:\n${process.stdout}');
|
||||
}
|
||||
process.throwException('adb failed without a message');
|
||||
}
|
||||
|
||||
if (hostPort == 0) {
|
||||
if (process.stdout.isEmpty)
|
||||
if (process.stdout.isEmpty) {
|
||||
process.throwException('adb did not report forwarded port');
|
||||
hostPort = int.tryParse(process.stdout) ?? (throw 'adb returned invalid port number:\n${process.stdout}');
|
||||
}
|
||||
hostPort = int.tryParse(process.stdout);
|
||||
if (hostPort == null) {
|
||||
process.throwException('adb returned invalid port number:\n${process.stdout}');
|
||||
}
|
||||
} else {
|
||||
// stdout may be empty or the port we asked it to forward, though it's
|
||||
// not documented (or obvious) what triggers each case.
|
||||
|
@ -1025,8 +1069,9 @@ class _AndroidDevicePortForwarder extends DevicePortForwarder {
|
|||
// To cover all cases, we accept the output being either empty or exactly
|
||||
// the port number, but treat any other output as probably being an error
|
||||
// message.
|
||||
if (process.stdout.isNotEmpty && process.stdout.trim() != '$hostPort')
|
||||
if (process.stdout.isNotEmpty && process.stdout.trim() != '$hostPort') {
|
||||
process.throwException('adb returned error:\n${process.stdout}');
|
||||
}
|
||||
}
|
||||
|
||||
return hostPort;
|
||||
|
@ -1034,8 +1079,14 @@ class _AndroidDevicePortForwarder extends DevicePortForwarder {
|
|||
|
||||
@override
|
||||
Future<void> unforward(ForwardedPort forwardedPort) async {
|
||||
await runCheckedAsync(device.adbCommandForDevice(
|
||||
<String>['forward', '--remove', 'tcp:${forwardedPort.hostPort}']
|
||||
));
|
||||
final List<String> unforwardCommand = <String>[
|
||||
'forward',
|
||||
'--remove',
|
||||
'tcp:${forwardedPort.hostPort}',
|
||||
];
|
||||
await processUtils.run(
|
||||
device.adbCommandForDevice(unforwardCommand),
|
||||
throwOnError: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,7 @@ import 'package:meta/meta.dart';
|
|||
import '../android/android_sdk.dart';
|
||||
import '../android/android_workflow.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/process_manager.dart';
|
||||
import '../base/process.dart';
|
||||
import '../device.dart';
|
||||
import '../emulator.dart';
|
||||
import 'android_sdk.dart';
|
||||
|
@ -50,13 +49,10 @@ class AndroidEmulator extends Emulator {
|
|||
|
||||
@override
|
||||
Future<void> launch() async {
|
||||
final Future<void> launchResult =
|
||||
processManager.run(<String>[getEmulatorPath(), '-avd', id])
|
||||
.then((ProcessResult runResult) {
|
||||
if (runResult.exitCode != 0) {
|
||||
throw '${runResult.stdout}\n${runResult.stderr}'.trimRight();
|
||||
}
|
||||
});
|
||||
final Future<void> launchResult = processUtils.run(
|
||||
<String>[getEmulatorPath(), '-avd', id],
|
||||
throwOnError: true,
|
||||
);
|
||||
// The emulator continues running on a successful launch, so if it hasn't
|
||||
// quit within 3 seconds we assume that's a success and just return. This
|
||||
// means that on a slow machine, a failure that takes more than three
|
||||
|
@ -75,7 +71,8 @@ List<AndroidEmulator> getEmulatorAvds() {
|
|||
return <AndroidEmulator>[];
|
||||
}
|
||||
|
||||
final String listAvdsOutput = processManager.runSync(<String>[emulatorPath, '-list-avds']).stdout;
|
||||
final String listAvdsOutput = processUtils.runSync(
|
||||
<String>[emulatorPath, '-list-avds']).stdout.trim();
|
||||
|
||||
final List<AndroidEmulator> emulators = <AndroidEmulator>[];
|
||||
if (listAvdsOutput != null) {
|
||||
|
|
|
@ -7,7 +7,6 @@ import 'package:meta/meta.dart';
|
|||
import '../base/common.dart';
|
||||
import '../base/context.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart' show ProcessResult;
|
||||
import '../base/os.dart';
|
||||
import '../base/platform.dart';
|
||||
import '../base/process.dart';
|
||||
|
@ -526,9 +525,9 @@ class AndroidSdk {
|
|||
|
||||
/// First try Java bundled with Android Studio, then sniff JAVA_HOME, then fallback to PATH.
|
||||
static String findJavaBinary() {
|
||||
|
||||
if (android_studio.javaPath != null)
|
||||
if (android_studio.javaPath != null) {
|
||||
return fs.path.join(android_studio.javaPath, 'bin', 'java');
|
||||
}
|
||||
|
||||
final String javaHomeEnv = platform.environment[_javaHomeEnvironmentVariable];
|
||||
if (javaHomeEnv != null) {
|
||||
|
@ -540,7 +539,11 @@ class AndroidSdk {
|
|||
// See: http://stackoverflow.com/questions/14292698/how-do-i-check-if-the-java-jdk-is-installed-on-mac.
|
||||
if (platform.isMacOS) {
|
||||
try {
|
||||
final String javaHomeOutput = runCheckedSync(<String>['/usr/libexec/java_home'], hideStdout: true);
|
||||
final String javaHomeOutput = processUtils.runSync(
|
||||
<String>['/usr/libexec/java_home'],
|
||||
throwOnError: true,
|
||||
hideStdout: true,
|
||||
).stdout.trim();
|
||||
if (javaHomeOutput != null) {
|
||||
final List<String> javaHomeOutputSplit = javaHomeOutput.split('\n');
|
||||
if ((javaHomeOutputSplit != null) && (javaHomeOutputSplit.isNotEmpty)) {
|
||||
|
@ -575,7 +578,10 @@ class AndroidSdk {
|
|||
String get sdkManagerVersion {
|
||||
if (!processManager.canRun(sdkManagerPath))
|
||||
throwToolExit('Android sdkmanager not found. Update to the latest Android SDK to resolve this.');
|
||||
final ProcessResult result = processManager.runSync(<String>[sdkManagerPath, '--version'], environment: sdkManagerEnv);
|
||||
final RunResult result = processUtils.runSync(
|
||||
<String>[sdkManagerPath, '--version'],
|
||||
environment: sdkManagerEnv,
|
||||
);
|
||||
if (result.exitCode != 0) {
|
||||
printTrace('sdkmanager --version failed: exitCode: ${result.exitCode} stdout: ${result.stdout} stderr: ${result.stderr}');
|
||||
return null;
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
import '../base/common.dart';
|
||||
import '../base/context.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/platform.dart';
|
||||
import '../base/process.dart';
|
||||
import '../base/process_manager.dart';
|
||||
import '../base/version.dart';
|
||||
import '../globals.dart';
|
||||
|
@ -290,7 +290,7 @@ class AndroidStudio implements Comparable<AndroidStudio> {
|
|||
if (!processManager.canRun(javaExecutable)) {
|
||||
_validationMessages.add('Unable to find bundled Java version.');
|
||||
} else {
|
||||
final ProcessResult result = processManager.runSync(<String>[javaExecutable, '-version']);
|
||||
final RunResult result = processUtils.runSync(<String>[javaExecutable, '-version']);
|
||||
if (result.exitCode == 0) {
|
||||
final List<String> versionLines = result.stderr.split('\n');
|
||||
final String javaVersion = versionLines.length >= 2 ? versionLines[1] : versionLines[0];
|
||||
|
|
|
@ -259,7 +259,7 @@ class AndroidLicenseValidator extends DoctorValidator {
|
|||
}
|
||||
|
||||
try {
|
||||
final Process process = await runCommand(
|
||||
final Process process = await processUtils.start(
|
||||
<String>[androidSdk.sdkManagerPath, '--licenses'],
|
||||
environment: androidSdk.sdkManagerEnv,
|
||||
);
|
||||
|
@ -302,7 +302,7 @@ class AndroidLicenseValidator extends DoctorValidator {
|
|||
}
|
||||
|
||||
try {
|
||||
final Process process = await runCommand(
|
||||
final Process process = await processUtils.start(
|
||||
<String>[androidSdk.sdkManagerPath, '--licenses'],
|
||||
environment: androidSdk.sdkManagerEnv,
|
||||
);
|
||||
|
|
|
@ -145,8 +145,9 @@ Future<void> checkGradleDependencies() async {
|
|||
final Status progress = logger.startProgress('Ensuring gradle dependencies are up to date...', timeout: timeoutConfiguration.slowOperation);
|
||||
final FlutterProject flutterProject = FlutterProject.current();
|
||||
final String gradlew = await gradleUtils.getExecutable(flutterProject);
|
||||
await runCheckedAsync(
|
||||
await processUtils.run(
|
||||
<String>[gradlew, 'dependencies'],
|
||||
throwOnError: true,
|
||||
workingDirectory: flutterProject.android.hostAppGradleRoot.path,
|
||||
environment: _gradleEnv,
|
||||
);
|
||||
|
@ -234,13 +235,15 @@ Future<GradleProject> _readGradleProject({bool isLibrary = false}) async {
|
|||
// Get the properties and tasks from Gradle, so we can determinate the `buildDir`,
|
||||
// flavors and build types defined in the project. If gradle fails, then check if the failure is due to t
|
||||
try {
|
||||
final RunResult propertiesRunResult = await runCheckedAsync(
|
||||
final RunResult propertiesRunResult = await processUtils.run(
|
||||
<String>[gradlew, isLibrary ? 'properties' : 'app:properties'],
|
||||
throwOnError: true,
|
||||
workingDirectory: hostAppGradleRoot.path,
|
||||
environment: _gradleEnv,
|
||||
);
|
||||
final RunResult tasksRunResult = await runCheckedAsync(
|
||||
final RunResult tasksRunResult = await processUtils.run(
|
||||
<String>[gradlew, isLibrary ? 'tasks': 'app:tasks', '--all', '--console=auto'],
|
||||
throwOnError: true,
|
||||
workingDirectory: hostAppGradleRoot.path,
|
||||
environment: _gradleEnv,
|
||||
);
|
||||
|
@ -306,22 +309,29 @@ Future<String> _initializeGradle(FlutterProject project) async {
|
|||
injectGradleWrapperIfNeeded(android);
|
||||
|
||||
final String gradle = _locateGradlewExecutable(android);
|
||||
if (gradle == null)
|
||||
if (gradle == null) {
|
||||
status.stop();
|
||||
throwToolExit('Unable to locate gradlew script');
|
||||
}
|
||||
printTrace('Using gradle from $gradle.');
|
||||
// Validates the Gradle executable by asking for its version.
|
||||
// Makes Gradle Wrapper download and install Gradle distribution, if needed.
|
||||
try {
|
||||
await runCheckedAsync(<String>[gradle, '-v'], environment: _gradleEnv);
|
||||
} catch (e) {
|
||||
if (e is ProcessException &&
|
||||
e.toString().contains('java.io.FileNotFoundException: https://downloads.gradle.org') ||
|
||||
e.toString().contains('java.io.IOException: Unable to tunnel through proxy')) {
|
||||
await processUtils.run(
|
||||
<String>[gradle, '-v'],
|
||||
throwOnError: true,
|
||||
environment: _gradleEnv,
|
||||
);
|
||||
} on ProcessException catch (e) {
|
||||
final String error = e.toString();
|
||||
if (error.contains('java.io.FileNotFoundException: https://downloads.gradle.org') ||
|
||||
error.contains('java.io.IOException: Unable to tunnel through proxy')) {
|
||||
throwToolExit('$gradle threw an error while trying to update itself.\n$e');
|
||||
}
|
||||
rethrow;
|
||||
} finally {
|
||||
status.stop();
|
||||
}
|
||||
status.stop();
|
||||
return gradle;
|
||||
}
|
||||
|
||||
|
@ -595,7 +605,7 @@ Future<void> buildGradleAar({
|
|||
int exitCode = 1;
|
||||
|
||||
try {
|
||||
exitCode = await runCommandAndStreamOutput(
|
||||
exitCode = await processUtils.stream(
|
||||
command,
|
||||
workingDirectory: project.android.hostAppGradleRoot.path,
|
||||
allowReentrantFlutter: true,
|
||||
|
@ -633,7 +643,7 @@ Future<void> _buildGradleProjectV1(FlutterProject project) async {
|
|||
multilineOutput: true,
|
||||
);
|
||||
final Stopwatch sw = Stopwatch()..start();
|
||||
final int exitCode = await runCommandAndStreamOutput(
|
||||
final int exitCode = await processUtils.stream(
|
||||
<String>[fs.file(gradlew).absolute.path, 'build'],
|
||||
workingDirectory: project.android.hostAppGradleRoot.path,
|
||||
allowReentrantFlutter: true,
|
||||
|
@ -756,7 +766,7 @@ Future<void> _buildGradleProjectV2(
|
|||
final Stopwatch sw = Stopwatch()..start();
|
||||
int exitCode = 1;
|
||||
try {
|
||||
exitCode = await runCommandAndStreamOutput(
|
||||
exitCode = await processUtils.stream(
|
||||
command,
|
||||
workingDirectory: flutterProject.android.hostAppGradleRoot.path,
|
||||
allowReentrantFlutter: true,
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'android/gradle.dart';
|
|||
import 'base/common.dart';
|
||||
import 'base/context.dart';
|
||||
import 'base/file_system.dart';
|
||||
import 'base/io.dart';
|
||||
import 'base/os.dart' show os;
|
||||
import 'base/process.dart';
|
||||
import 'base/user_messages.dart';
|
||||
|
@ -115,14 +116,17 @@ class AndroidApk extends ApplicationPackage {
|
|||
|
||||
String apptStdout;
|
||||
try {
|
||||
apptStdout = runCheckedSync(<String>[
|
||||
aaptPath,
|
||||
'dump',
|
||||
'xmltree',
|
||||
apk.path,
|
||||
'AndroidManifest.xml',
|
||||
]);
|
||||
} catch (error) {
|
||||
apptStdout = processUtils.runSync(
|
||||
<String>[
|
||||
aaptPath,
|
||||
'dump',
|
||||
'xmltree',
|
||||
apk.path,
|
||||
'AndroidManifest.xml',
|
||||
],
|
||||
throwOnError: true,
|
||||
).stdout.trim();
|
||||
} on ProcessException catch (error) {
|
||||
printError('Failed to extract manifest from APK: $error.');
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -69,7 +69,10 @@ class GenSnapshot {
|
|||
outputFilter = (String line) => line != kStripWarning ? line : null;
|
||||
}
|
||||
|
||||
return runCommandAndStreamOutput(<String>[snapshotterPath, ...args], mapFunction: outputFilter);
|
||||
return processUtils.stream(
|
||||
<String>[snapshotterPath, ...args],
|
||||
mapFunction: outputFilter,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -154,30 +154,45 @@ class _PosixUtils extends OperatingSystemUtils {
|
|||
|
||||
@override
|
||||
void zip(Directory data, File zipFile) {
|
||||
runSync(<String>['zip', '-r', '-q', zipFile.path, '.'], workingDirectory: data.path);
|
||||
processUtils.runSync(
|
||||
<String>['zip', '-r', '-q', zipFile.path, '.'],
|
||||
workingDirectory: data.path,
|
||||
throwOnError: true,
|
||||
);
|
||||
}
|
||||
|
||||
// unzip -o -q zipfile -d dest
|
||||
@override
|
||||
void unzip(File file, Directory targetDirectory) {
|
||||
runSync(<String>['unzip', '-o', '-q', file.path, '-d', targetDirectory.path]);
|
||||
processUtils.runSync(
|
||||
<String>['unzip', '-o', '-q', file.path, '-d', targetDirectory.path],
|
||||
throwOnError: true,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool verifyZip(File zipFile) => exitsHappy(<String>['zip', '-T', zipFile.path]);
|
||||
bool verifyZip(File zipFile) =>
|
||||
processUtils.exitsHappySync(<String>['zip', '-T', zipFile.path]);
|
||||
|
||||
// tar -xzf tarball -C dest
|
||||
@override
|
||||
void unpack(File gzippedTarFile, Directory targetDirectory) {
|
||||
runSync(<String>['tar', '-xzf', gzippedTarFile.path, '-C', targetDirectory.path]);
|
||||
processUtils.runSync(
|
||||
<String>['tar', '-xzf', gzippedTarFile.path, '-C', targetDirectory.path],
|
||||
throwOnError: true,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool verifyGzip(File gzippedFile) => exitsHappy(<String>['gzip', '-t', gzippedFile.path]);
|
||||
bool verifyGzip(File gzippedFile) =>
|
||||
processUtils.exitsHappySync(<String>['gzip', '-t', gzippedFile.path]);
|
||||
|
||||
@override
|
||||
File makePipe(String path) {
|
||||
runSync(<String>['mkfifo', path]);
|
||||
processUtils.runSync(
|
||||
<String>['mkfifo', path],
|
||||
throwOnError: true,
|
||||
);
|
||||
return fs.file(path);
|
||||
}
|
||||
|
||||
|
@ -187,12 +202,12 @@ class _PosixUtils extends OperatingSystemUtils {
|
|||
String get name {
|
||||
if (_name == null) {
|
||||
if (platform.isMacOS) {
|
||||
final List<ProcessResult> results = <ProcessResult>[
|
||||
processManager.runSync(<String>['sw_vers', '-productName']),
|
||||
processManager.runSync(<String>['sw_vers', '-productVersion']),
|
||||
processManager.runSync(<String>['sw_vers', '-buildVersion']),
|
||||
final List<RunResult> results = <RunResult>[
|
||||
processUtils.runSync(<String>['sw_vers', '-productName']),
|
||||
processUtils.runSync(<String>['sw_vers', '-productVersion']),
|
||||
processUtils.runSync(<String>['sw_vers', '-buildVersion']),
|
||||
];
|
||||
if (results.every((ProcessResult result) => result.exitCode == 0)) {
|
||||
if (results.every((RunResult result) => result.exitCode == 0)) {
|
||||
_name = '${results[0].stdout.trim()} ${results[1].stdout
|
||||
.trim()} ${results[2].stdout.trim()}';
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'dart:async';
|
|||
|
||||
import '../convert.dart';
|
||||
import '../globals.dart';
|
||||
import 'common.dart';
|
||||
import 'context.dart';
|
||||
import 'file_system.dart';
|
||||
import 'io.dart';
|
||||
import 'process_manager.dart';
|
||||
|
@ -98,379 +98,6 @@ Future<void> runShutdownHooks() async {
|
|||
printTrace('Shutdown hooks complete');
|
||||
}
|
||||
|
||||
Map<String, String> _environment(bool allowReentrantFlutter, [ Map<String, String> environment ]) {
|
||||
if (allowReentrantFlutter) {
|
||||
if (environment == null)
|
||||
environment = <String, String>{'FLUTTER_ALREADY_LOCKED': 'true'};
|
||||
else
|
||||
environment['FLUTTER_ALREADY_LOCKED'] = 'true';
|
||||
}
|
||||
|
||||
return environment;
|
||||
}
|
||||
|
||||
/// This runs the command in the background from the specified working
|
||||
/// directory. Completes when the process has been started.
|
||||
Future<Process> runCommand(
|
||||
List<String> cmd, {
|
||||
String workingDirectory,
|
||||
bool allowReentrantFlutter = false,
|
||||
Map<String, String> environment,
|
||||
}) {
|
||||
_traceCommand(cmd, workingDirectory: workingDirectory);
|
||||
return processManager.start(
|
||||
cmd,
|
||||
workingDirectory: workingDirectory,
|
||||
environment: _environment(allowReentrantFlutter, environment),
|
||||
);
|
||||
}
|
||||
|
||||
/// This runs the command and streams stdout/stderr from the child process to
|
||||
/// this process' stdout/stderr. Completes with the process's exit code.
|
||||
///
|
||||
/// If [filter] is null, no lines are removed.
|
||||
///
|
||||
/// If [filter] is non-null, all lines that do not match it are removed. If
|
||||
/// [mapFunction] is present, all lines that match [filter] are also forwarded
|
||||
/// to [mapFunction] for further processing.
|
||||
Future<int> runCommandAndStreamOutput(
|
||||
List<String> cmd, {
|
||||
String workingDirectory,
|
||||
bool allowReentrantFlutter = false,
|
||||
String prefix = '',
|
||||
bool trace = false,
|
||||
RegExp filter,
|
||||
StringConverter mapFunction,
|
||||
Map<String, String> environment,
|
||||
}) async {
|
||||
final Process process = await runCommand(
|
||||
cmd,
|
||||
workingDirectory: workingDirectory,
|
||||
allowReentrantFlutter: allowReentrantFlutter,
|
||||
environment: environment,
|
||||
);
|
||||
final StreamSubscription<String> stdoutSubscription = process.stdout
|
||||
.transform<String>(utf8.decoder)
|
||||
.transform<String>(const LineSplitter())
|
||||
.where((String line) => filter == null || filter.hasMatch(line))
|
||||
.listen((String line) {
|
||||
if (mapFunction != null)
|
||||
line = mapFunction(line);
|
||||
if (line != null) {
|
||||
final String message = '$prefix$line';
|
||||
if (trace)
|
||||
printTrace(message);
|
||||
else
|
||||
printStatus(message, wrap: false);
|
||||
}
|
||||
});
|
||||
final StreamSubscription<String> stderrSubscription = process.stderr
|
||||
.transform<String>(utf8.decoder)
|
||||
.transform<String>(const LineSplitter())
|
||||
.where((String line) => filter == null || filter.hasMatch(line))
|
||||
.listen((String line) {
|
||||
if (mapFunction != null)
|
||||
line = mapFunction(line);
|
||||
if (line != null)
|
||||
printError('$prefix$line', wrap: false);
|
||||
});
|
||||
|
||||
// Wait for stdout to be fully processed
|
||||
// because process.exitCode may complete first causing flaky tests.
|
||||
await waitGroup<void>(<Future<void>>[
|
||||
stdoutSubscription.asFuture<void>(),
|
||||
stderrSubscription.asFuture<void>(),
|
||||
]);
|
||||
|
||||
await waitGroup<void>(<Future<void>>[
|
||||
stdoutSubscription.cancel(),
|
||||
stderrSubscription.cancel(),
|
||||
]);
|
||||
|
||||
return await process.exitCode;
|
||||
}
|
||||
|
||||
/// Runs the [command] interactively, connecting the stdin/stdout/stderr
|
||||
/// streams of this process to those of the child process. Completes with
|
||||
/// the exit code of the child process.
|
||||
Future<int> runInteractively(
|
||||
List<String> command, {
|
||||
String workingDirectory,
|
||||
bool allowReentrantFlutter = false,
|
||||
Map<String, String> environment,
|
||||
}) async {
|
||||
final Process process = await runCommand(
|
||||
command,
|
||||
workingDirectory: workingDirectory,
|
||||
allowReentrantFlutter: allowReentrantFlutter,
|
||||
environment: environment,
|
||||
);
|
||||
// The real stdin will never finish streaming. Pipe until the child process
|
||||
// finishes.
|
||||
unawaited(process.stdin.addStream(stdin));
|
||||
// Wait for stdout and stderr to be fully processed, because process.exitCode
|
||||
// may complete first.
|
||||
await Future.wait<dynamic>(<Future<dynamic>>[
|
||||
stdout.addStream(process.stdout),
|
||||
stderr.addStream(process.stderr),
|
||||
]);
|
||||
return await process.exitCode;
|
||||
}
|
||||
|
||||
Future<Process> runDetached(List<String> cmd) {
|
||||
_traceCommand(cmd);
|
||||
final Future<Process> proc = processManager.start(
|
||||
cmd,
|
||||
mode: ProcessStartMode.detached,
|
||||
);
|
||||
return proc;
|
||||
}
|
||||
|
||||
Future<RunResult> runAsync(
|
||||
List<String> cmd, {
|
||||
String workingDirectory,
|
||||
bool allowReentrantFlutter = false,
|
||||
Map<String, String> environment,
|
||||
Duration timeout,
|
||||
int timeoutRetries = 0,
|
||||
}) async {
|
||||
_traceCommand(cmd, workingDirectory: workingDirectory);
|
||||
|
||||
// When there is no timeout, there's no need to kill a running process, so
|
||||
// we can just use processManager.run().
|
||||
if (timeout == null) {
|
||||
final ProcessResult results = await processManager.run(
|
||||
cmd,
|
||||
workingDirectory: workingDirectory,
|
||||
environment: _environment(allowReentrantFlutter, environment),
|
||||
);
|
||||
final RunResult runResults = RunResult(results, cmd);
|
||||
printTrace(runResults.toString());
|
||||
return runResults;
|
||||
}
|
||||
|
||||
// When there is a timeout, we have to kill the running process, so we have
|
||||
// to use processManager.start() through runCommand() above.
|
||||
while (true) {
|
||||
assert(timeoutRetries >= 0);
|
||||
timeoutRetries = timeoutRetries - 1;
|
||||
|
||||
final Process process = await runCommand(
|
||||
cmd,
|
||||
workingDirectory: workingDirectory,
|
||||
allowReentrantFlutter: allowReentrantFlutter,
|
||||
environment: environment,
|
||||
);
|
||||
|
||||
final StringBuffer stdoutBuffer = StringBuffer();
|
||||
final StringBuffer stderrBuffer = StringBuffer();
|
||||
final Future<void> stdoutFuture = process.stdout
|
||||
.transform<String>(const Utf8Decoder(reportErrors: false))
|
||||
.listen(stdoutBuffer.write)
|
||||
.asFuture<void>(null);
|
||||
final Future<void> stderrFuture = process.stderr
|
||||
.transform<String>(const Utf8Decoder(reportErrors: false))
|
||||
.listen(stderrBuffer.write)
|
||||
.asFuture<void>(null);
|
||||
|
||||
int exitCode;
|
||||
exitCode = await process.exitCode.timeout(timeout, onTimeout: () {
|
||||
// The process timed out. Kill it.
|
||||
processManager.killPid(process.pid);
|
||||
return null;
|
||||
});
|
||||
|
||||
String stdoutString;
|
||||
String stderrString;
|
||||
try {
|
||||
Future<void> stdioFuture =
|
||||
Future.wait<void>(<Future<void>>[stdoutFuture, stderrFuture]);
|
||||
if (exitCode == null) {
|
||||
// If we had to kill the process for a timeout, only wait a short time
|
||||
// for the stdio streams to drain in case killing the process didn't
|
||||
// work.
|
||||
stdioFuture = stdioFuture.timeout(const Duration(seconds: 1));
|
||||
}
|
||||
await stdioFuture;
|
||||
} catch (_) {
|
||||
// Ignore errors on the process' stdout and stderr streams. Just capture
|
||||
// whatever we got, and use the exit code
|
||||
}
|
||||
stdoutString = stdoutBuffer.toString();
|
||||
stderrString = stderrBuffer.toString();
|
||||
|
||||
final ProcessResult result = ProcessResult(
|
||||
process.pid, exitCode ?? -1, stdoutString, stderrString);
|
||||
final RunResult runResult = RunResult(result, cmd);
|
||||
|
||||
// If the process did not timeout. We are done.
|
||||
if (exitCode != null) {
|
||||
printTrace(runResult.toString());
|
||||
return runResult;
|
||||
}
|
||||
|
||||
// If we are out of timeoutRetries, throw a ProcessException.
|
||||
if (timeoutRetries < 0) {
|
||||
throw ProcessException(cmd[0], cmd.sublist(1),
|
||||
'Process "${cmd[0]}" timed out: $runResult', exitCode);
|
||||
}
|
||||
|
||||
// Log the timeout with a trace message in verbose mode.
|
||||
printTrace('Process "${cmd[0]}" timed out. $timeoutRetries attempts left: $runResult');
|
||||
}
|
||||
|
||||
// Unreachable.
|
||||
}
|
||||
|
||||
typedef RunResultChecker = bool Function(int);
|
||||
|
||||
Future<RunResult> runCheckedAsync(
|
||||
List<String> cmd, {
|
||||
String workingDirectory,
|
||||
bool allowReentrantFlutter = false,
|
||||
Map<String, String> environment,
|
||||
RunResultChecker whiteListFailures,
|
||||
Duration timeout,
|
||||
int timeoutRetries = 0,
|
||||
}) async {
|
||||
final RunResult result = await runAsync(
|
||||
cmd,
|
||||
workingDirectory: workingDirectory,
|
||||
allowReentrantFlutter: allowReentrantFlutter,
|
||||
environment: environment,
|
||||
timeout: timeout,
|
||||
timeoutRetries: timeoutRetries,
|
||||
);
|
||||
if (result.exitCode != 0) {
|
||||
if (whiteListFailures == null || !whiteListFailures(result.exitCode)) {
|
||||
throw ProcessException(cmd[0], cmd.sublist(1),
|
||||
'Process "${cmd[0]}" exited abnormally:\n$result', result.exitCode);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool exitsHappy(
|
||||
List<String> cli, {
|
||||
Map<String, String> environment,
|
||||
}) {
|
||||
_traceCommand(cli);
|
||||
try {
|
||||
return processManager.runSync(cli, environment: environment).exitCode == 0;
|
||||
} catch (error) {
|
||||
printTrace('$cli failed with $error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> exitsHappyAsync(
|
||||
List<String> cli, {
|
||||
Map<String, String> environment,
|
||||
}) async {
|
||||
_traceCommand(cli);
|
||||
try {
|
||||
return (await processManager.run(cli, environment: environment)).exitCode == 0;
|
||||
} catch (error) {
|
||||
printTrace('$cli failed with $error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Run cmd and return stdout.
|
||||
///
|
||||
/// Throws an error if cmd exits with a non-zero value.
|
||||
String runCheckedSync(
|
||||
List<String> cmd, {
|
||||
String workingDirectory,
|
||||
bool allowReentrantFlutter = false,
|
||||
bool hideStdout = false,
|
||||
Map<String, String> environment,
|
||||
RunResultChecker whiteListFailures,
|
||||
}) {
|
||||
return _runWithLoggingSync(
|
||||
cmd,
|
||||
workingDirectory: workingDirectory,
|
||||
allowReentrantFlutter: allowReentrantFlutter,
|
||||
hideStdout: hideStdout,
|
||||
checked: true,
|
||||
noisyErrors: true,
|
||||
environment: environment,
|
||||
whiteListFailures: whiteListFailures
|
||||
);
|
||||
}
|
||||
|
||||
/// Run cmd and return stdout.
|
||||
String runSync(
|
||||
List<String> cmd, {
|
||||
String workingDirectory,
|
||||
bool allowReentrantFlutter = false,
|
||||
}) {
|
||||
return _runWithLoggingSync(
|
||||
cmd,
|
||||
workingDirectory: workingDirectory,
|
||||
allowReentrantFlutter: allowReentrantFlutter,
|
||||
);
|
||||
}
|
||||
|
||||
void _traceCommand(List<String> args, { String workingDirectory }) {
|
||||
final String argsText = args.join(' ');
|
||||
if (workingDirectory == null) {
|
||||
printTrace('executing: $argsText');
|
||||
} else {
|
||||
printTrace('executing: [$workingDirectory${fs.path.separator}] $argsText');
|
||||
}
|
||||
}
|
||||
|
||||
String _runWithLoggingSync(
|
||||
List<String> cmd, {
|
||||
bool checked = false,
|
||||
bool noisyErrors = false,
|
||||
bool throwStandardErrorOnError = false,
|
||||
String workingDirectory,
|
||||
bool allowReentrantFlutter = false,
|
||||
bool hideStdout = false,
|
||||
Map<String, String> environment,
|
||||
RunResultChecker whiteListFailures,
|
||||
}) {
|
||||
_traceCommand(cmd, workingDirectory: workingDirectory);
|
||||
final ProcessResult results = processManager.runSync(
|
||||
cmd,
|
||||
workingDirectory: workingDirectory,
|
||||
environment: _environment(allowReentrantFlutter, environment),
|
||||
);
|
||||
|
||||
printTrace('Exit code ${results.exitCode} from: ${cmd.join(' ')}');
|
||||
|
||||
bool failedExitCode = results.exitCode != 0;
|
||||
if (whiteListFailures != null && failedExitCode) {
|
||||
failedExitCode = !whiteListFailures(results.exitCode);
|
||||
}
|
||||
|
||||
if (results.stdout.isNotEmpty && !hideStdout) {
|
||||
if (failedExitCode && noisyErrors)
|
||||
printStatus(results.stdout.trim());
|
||||
else
|
||||
printTrace(results.stdout.trim());
|
||||
}
|
||||
|
||||
if (failedExitCode) {
|
||||
if (results.stderr.isNotEmpty) {
|
||||
if (noisyErrors)
|
||||
printError(results.stderr.trim());
|
||||
else
|
||||
printTrace(results.stderr.trim());
|
||||
}
|
||||
|
||||
if (throwStandardErrorOnError)
|
||||
throw results.stderr.trim();
|
||||
|
||||
if (checked)
|
||||
throw 'Exit code ${results.exitCode} from: ${cmd.join(' ')}';
|
||||
}
|
||||
|
||||
return results.stdout.trim();
|
||||
}
|
||||
|
||||
class ProcessExit implements Exception {
|
||||
ProcessExit(this.exitCode, {this.immediate = false});
|
||||
|
||||
|
@ -516,3 +143,382 @@ class RunResult {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
typedef RunResultChecker = bool Function(int);
|
||||
|
||||
ProcessUtils get processUtils => ProcessUtils.instance;
|
||||
|
||||
abstract class ProcessUtils {
|
||||
factory ProcessUtils() => _DefaultProcessUtils();
|
||||
|
||||
static ProcessUtils get instance => context.get<ProcessUtils>();
|
||||
|
||||
/// Spawns a child process to run the command [cmd].
|
||||
///
|
||||
/// When [throwOnError] is `true`, if the child process finishes with a non-zero
|
||||
/// exit code, a [ProcessException] is thrown.
|
||||
///
|
||||
/// If [throwOnError] is `true`, and [whiteListFailures] is supplied,
|
||||
/// a [ProcessException] is only thrown on a non-zero exit code if
|
||||
/// [whiteListFailures] returns false when passed the exit code.
|
||||
///
|
||||
/// When [workingDirectory] is set, it is the working directory of the child
|
||||
/// process.
|
||||
///
|
||||
/// When [allowReentrantFlutter] is set to `true`, the child process is
|
||||
/// permitted to call the Flutter tool. By default it is not.
|
||||
///
|
||||
/// When [environment] is supplied, it is used as the environment for the child
|
||||
/// process.
|
||||
///
|
||||
/// When [timeout] is supplied, [runAsync] will kill the child process and
|
||||
/// throw a [ProcessException] when it doesn't finish in time.
|
||||
///
|
||||
/// If [timeout] is supplied, the command will be retried [timeoutRetries] times
|
||||
/// if it times out.
|
||||
Future<RunResult> run(
|
||||
List<String> cmd, {
|
||||
bool throwOnError = false,
|
||||
RunResultChecker whiteListFailures,
|
||||
String workingDirectory,
|
||||
bool allowReentrantFlutter = false,
|
||||
Map<String, String> environment,
|
||||
Duration timeout,
|
||||
int timeoutRetries = 0,
|
||||
});
|
||||
|
||||
/// Run the command and block waiting for its result.
|
||||
RunResult runSync(
|
||||
List<String> cmd, {
|
||||
bool throwOnError = false,
|
||||
RunResultChecker whiteListFailures,
|
||||
bool hideStdout = false,
|
||||
String workingDirectory,
|
||||
Map<String, String> environment,
|
||||
bool allowReentrantFlutter = false,
|
||||
});
|
||||
|
||||
/// This runs the command in the background from the specified working
|
||||
/// directory. Completes when the process has been started.
|
||||
Future<Process> start(
|
||||
List<String> cmd, {
|
||||
String workingDirectory,
|
||||
bool allowReentrantFlutter = false,
|
||||
Map<String, String> environment,
|
||||
});
|
||||
|
||||
/// This runs the command and streams stdout/stderr from the child process to
|
||||
/// this process' stdout/stderr. Completes with the process's exit code.
|
||||
///
|
||||
/// If [filter] is null, no lines are removed.
|
||||
///
|
||||
/// If [filter] is non-null, all lines that do not match it are removed. If
|
||||
/// [mapFunction] is present, all lines that match [filter] are also forwarded
|
||||
/// to [mapFunction] for further processing.
|
||||
Future<int> stream(
|
||||
List<String> cmd, {
|
||||
String workingDirectory,
|
||||
bool allowReentrantFlutter = false,
|
||||
String prefix = '',
|
||||
bool trace = false,
|
||||
RegExp filter,
|
||||
StringConverter mapFunction,
|
||||
Map<String, String> environment,
|
||||
});
|
||||
|
||||
bool exitsHappySync(
|
||||
List<String> cli, {
|
||||
Map<String, String> environment,
|
||||
});
|
||||
|
||||
Future<bool> exitsHappy(
|
||||
List<String> cli, {
|
||||
Map<String, String> environment,
|
||||
});
|
||||
}
|
||||
|
||||
class _DefaultProcessUtils implements ProcessUtils {
|
||||
@override
|
||||
Future<RunResult> run(
|
||||
List<String> cmd, {
|
||||
bool throwOnError = false,
|
||||
RunResultChecker whiteListFailures,
|
||||
String workingDirectory,
|
||||
bool allowReentrantFlutter = false,
|
||||
Map<String, String> environment,
|
||||
Duration timeout,
|
||||
int timeoutRetries = 0,
|
||||
}) async {
|
||||
if (cmd == null || cmd.isEmpty) {
|
||||
throw ArgumentError('cmd must be a non-empty list');
|
||||
}
|
||||
if (timeoutRetries < 0) {
|
||||
throw ArgumentError('timeoutRetries must be non-negative');
|
||||
}
|
||||
_traceCommand(cmd, workingDirectory: workingDirectory);
|
||||
|
||||
// When there is no timeout, there's no need to kill a running process, so
|
||||
// we can just use processManager.run().
|
||||
if (timeout == null) {
|
||||
final ProcessResult results = await processManager.run(
|
||||
cmd,
|
||||
workingDirectory: workingDirectory,
|
||||
environment: _environment(allowReentrantFlutter, environment),
|
||||
);
|
||||
final RunResult runResult = RunResult(results, cmd);
|
||||
printTrace(runResult.toString());
|
||||
if (throwOnError && runResult.exitCode != 0 &&
|
||||
(whiteListFailures == null || !whiteListFailures(runResult.exitCode))) {
|
||||
runResult.throwException('Process exited abnormally:\n$runResult');
|
||||
}
|
||||
return runResult;
|
||||
}
|
||||
|
||||
// When there is a timeout, we have to kill the running process, so we have
|
||||
// to use processManager.start() through _runCommand() above.
|
||||
while (true) {
|
||||
assert(timeoutRetries >= 0);
|
||||
timeoutRetries = timeoutRetries - 1;
|
||||
|
||||
final Process process = await start(
|
||||
cmd,
|
||||
workingDirectory: workingDirectory,
|
||||
allowReentrantFlutter: allowReentrantFlutter,
|
||||
environment: environment,
|
||||
);
|
||||
|
||||
final StringBuffer stdoutBuffer = StringBuffer();
|
||||
final StringBuffer stderrBuffer = StringBuffer();
|
||||
final Future<void> stdoutFuture = process.stdout
|
||||
.transform<String>(const Utf8Decoder(reportErrors: false))
|
||||
.listen(stdoutBuffer.write)
|
||||
.asFuture<void>(null);
|
||||
final Future<void> stderrFuture = process.stderr
|
||||
.transform<String>(const Utf8Decoder(reportErrors: false))
|
||||
.listen(stderrBuffer.write)
|
||||
.asFuture<void>(null);
|
||||
|
||||
int exitCode;
|
||||
exitCode = await process.exitCode.timeout(timeout, onTimeout: () {
|
||||
// The process timed out. Kill it.
|
||||
processManager.killPid(process.pid);
|
||||
return null;
|
||||
});
|
||||
|
||||
String stdoutString;
|
||||
String stderrString;
|
||||
try {
|
||||
Future<void> stdioFuture =
|
||||
Future.wait<void>(<Future<void>>[stdoutFuture, stderrFuture]);
|
||||
if (exitCode == null) {
|
||||
// If we had to kill the process for a timeout, only wait a short time
|
||||
// for the stdio streams to drain in case killing the process didn't
|
||||
// work.
|
||||
stdioFuture = stdioFuture.timeout(const Duration(seconds: 1));
|
||||
}
|
||||
await stdioFuture;
|
||||
} catch (_) {
|
||||
// Ignore errors on the process' stdout and stderr streams. Just capture
|
||||
// whatever we got, and use the exit code
|
||||
}
|
||||
stdoutString = stdoutBuffer.toString();
|
||||
stderrString = stderrBuffer.toString();
|
||||
|
||||
final ProcessResult result = ProcessResult(
|
||||
process.pid, exitCode ?? -1, stdoutString, stderrString);
|
||||
final RunResult runResult = RunResult(result, cmd);
|
||||
|
||||
// If the process did not timeout. We are done.
|
||||
if (exitCode != null) {
|
||||
printTrace(runResult.toString());
|
||||
if (throwOnError && runResult.exitCode != 0 &&
|
||||
(whiteListFailures == null || !whiteListFailures(exitCode))) {
|
||||
runResult.throwException('Process exited abnormally:\n$runResult');
|
||||
}
|
||||
return runResult;
|
||||
}
|
||||
|
||||
// If we are out of timeoutRetries, throw a ProcessException.
|
||||
if (timeoutRetries < 0) {
|
||||
runResult.throwException('Process timed out:\n$runResult');
|
||||
}
|
||||
|
||||
// Log the timeout with a trace message in verbose mode.
|
||||
printTrace('Process "${cmd[0]}" timed out. $timeoutRetries attempts left:\n'
|
||||
'$runResult');
|
||||
}
|
||||
|
||||
// Unreachable.
|
||||
}
|
||||
|
||||
@override
|
||||
RunResult runSync(
|
||||
List<String> cmd, {
|
||||
bool throwOnError = false,
|
||||
RunResultChecker whiteListFailures,
|
||||
bool hideStdout = false,
|
||||
String workingDirectory,
|
||||
Map<String, String> environment,
|
||||
bool allowReentrantFlutter = false,
|
||||
}) {
|
||||
_traceCommand(cmd, workingDirectory: workingDirectory);
|
||||
final ProcessResult results = processManager.runSync(
|
||||
cmd,
|
||||
workingDirectory: workingDirectory,
|
||||
environment: _environment(allowReentrantFlutter, environment),
|
||||
);
|
||||
final RunResult runResult = RunResult(results, cmd);
|
||||
|
||||
printTrace('Exit code ${runResult.exitCode} from: ${cmd.join(' ')}');
|
||||
|
||||
bool failedExitCode = runResult.exitCode != 0;
|
||||
if (whiteListFailures != null && failedExitCode) {
|
||||
failedExitCode = !whiteListFailures(runResult.exitCode);
|
||||
}
|
||||
|
||||
if (runResult.stdout.isNotEmpty && !hideStdout) {
|
||||
if (failedExitCode && throwOnError) {
|
||||
printStatus(runResult.stdout.trim());
|
||||
} else {
|
||||
printTrace(runResult.stdout.trim());
|
||||
}
|
||||
}
|
||||
|
||||
if (runResult.stderr.isNotEmpty) {
|
||||
if (failedExitCode && throwOnError) {
|
||||
printError(runResult.stderr.trim());
|
||||
} else {
|
||||
printTrace(runResult.stderr.trim());
|
||||
}
|
||||
}
|
||||
|
||||
if (failedExitCode && throwOnError) {
|
||||
runResult.throwException('The command failed');
|
||||
}
|
||||
|
||||
return runResult;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Process> start(
|
||||
List<String> cmd, {
|
||||
String workingDirectory,
|
||||
bool allowReentrantFlutter = false,
|
||||
Map<String, String> environment,
|
||||
}) {
|
||||
_traceCommand(cmd, workingDirectory: workingDirectory);
|
||||
return processManager.start(
|
||||
cmd,
|
||||
workingDirectory: workingDirectory,
|
||||
environment: _environment(allowReentrantFlutter, environment),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> stream(
|
||||
List<String> cmd, {
|
||||
String workingDirectory,
|
||||
bool allowReentrantFlutter = false,
|
||||
String prefix = '',
|
||||
bool trace = false,
|
||||
RegExp filter,
|
||||
StringConverter mapFunction,
|
||||
Map<String, String> environment,
|
||||
}) async {
|
||||
final Process process = await start(
|
||||
cmd,
|
||||
workingDirectory: workingDirectory,
|
||||
allowReentrantFlutter: allowReentrantFlutter,
|
||||
environment: environment,
|
||||
);
|
||||
final StreamSubscription<String> stdoutSubscription = process.stdout
|
||||
.transform<String>(utf8.decoder)
|
||||
.transform<String>(const LineSplitter())
|
||||
.where((String line) => filter == null || filter.hasMatch(line))
|
||||
.listen((String line) {
|
||||
if (mapFunction != null)
|
||||
line = mapFunction(line);
|
||||
if (line != null) {
|
||||
final String message = '$prefix$line';
|
||||
if (trace)
|
||||
printTrace(message);
|
||||
else
|
||||
printStatus(message, wrap: false);
|
||||
}
|
||||
});
|
||||
final StreamSubscription<String> stderrSubscription = process.stderr
|
||||
.transform<String>(utf8.decoder)
|
||||
.transform<String>(const LineSplitter())
|
||||
.where((String line) => filter == null || filter.hasMatch(line))
|
||||
.listen((String line) {
|
||||
if (mapFunction != null)
|
||||
line = mapFunction(line);
|
||||
if (line != null)
|
||||
printError('$prefix$line', wrap: false);
|
||||
});
|
||||
|
||||
// Wait for stdout to be fully processed
|
||||
// because process.exitCode may complete first causing flaky tests.
|
||||
await waitGroup<void>(<Future<void>>[
|
||||
stdoutSubscription.asFuture<void>(),
|
||||
stderrSubscription.asFuture<void>(),
|
||||
]);
|
||||
|
||||
await waitGroup<void>(<Future<void>>[
|
||||
stdoutSubscription.cancel(),
|
||||
stderrSubscription.cancel(),
|
||||
]);
|
||||
|
||||
return await process.exitCode;
|
||||
}
|
||||
|
||||
@override
|
||||
bool exitsHappySync(
|
||||
List<String> cli, {
|
||||
Map<String, String> environment,
|
||||
}) {
|
||||
_traceCommand(cli);
|
||||
try {
|
||||
return processManager.runSync(cli, environment: environment).exitCode == 0;
|
||||
} catch (error) {
|
||||
printTrace('$cli failed with $error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> exitsHappy(
|
||||
List<String> cli, {
|
||||
Map<String, String> environment,
|
||||
}) async {
|
||||
_traceCommand(cli);
|
||||
try {
|
||||
return (await processManager.run(cli, environment: environment)).exitCode == 0;
|
||||
} catch (error) {
|
||||
printTrace('$cli failed with $error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> _environment(bool allowReentrantFlutter, [
|
||||
Map<String, String> environment,
|
||||
]) {
|
||||
if (allowReentrantFlutter) {
|
||||
if (environment == null)
|
||||
environment = <String, String>{'FLUTTER_ALREADY_LOCKED': 'true'};
|
||||
else
|
||||
environment['FLUTTER_ALREADY_LOCKED'] = 'true';
|
||||
}
|
||||
|
||||
return environment;
|
||||
}
|
||||
|
||||
void _traceCommand(List<String> args, { String workingDirectory }) {
|
||||
final String argsText = args.join(' ');
|
||||
if (workingDirectory == null) {
|
||||
printTrace('executing: $argsText');
|
||||
} else {
|
||||
printTrace('executing: [$workingDirectory${fs.path.separator}] $argsText');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import '../base/build.dart';
|
|||
import '../base/common.dart';
|
||||
import '../base/context.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/logger.dart';
|
||||
import '../base/process.dart';
|
||||
import '../base/version.dart';
|
||||
|
@ -142,14 +143,18 @@ class BuildAotCommand extends BuildSubCommand with TargetPlatformBasedDevelopmen
|
|||
|
||||
// Merge arch-specific App.frameworks into a multi-arch App.framework.
|
||||
if ((await Future.wait<int>(exitCodes.values)).every((int buildExitCode) => buildExitCode == 0)) {
|
||||
final Iterable<String> dylibs = iosBuilds.values.map<String>((String outputDir) => fs.path.join(outputDir, 'App.framework', 'App'));
|
||||
final Iterable<String> dylibs = iosBuilds.values.map<String>(
|
||||
(String outputDir) => fs.path.join(outputDir, 'App.framework', 'App'));
|
||||
fs.directory(fs.path.join(outputPath, 'App.framework'))..createSync();
|
||||
await runCheckedAsync(<String>[
|
||||
'lipo',
|
||||
...dylibs,
|
||||
'-create',
|
||||
'-output', fs.path.join(outputPath, 'App.framework', 'App'),
|
||||
]);
|
||||
await processUtils.run(
|
||||
<String>[
|
||||
'lipo',
|
||||
...dylibs,
|
||||
'-create',
|
||||
'-output', fs.path.join(outputPath, 'App.framework', 'App'),
|
||||
],
|
||||
throwOnError: true,
|
||||
);
|
||||
} else {
|
||||
status?.cancel();
|
||||
exitCodes.forEach((DarwinArch iosArch, Future<int> exitCodeFuture) async {
|
||||
|
@ -173,10 +178,10 @@ class BuildAotCommand extends BuildSubCommand with TargetPlatformBasedDevelopmen
|
|||
throwToolExit('Snapshotting exited with non-zero exit code: $snapshotExitCode');
|
||||
}
|
||||
}
|
||||
} on String catch (error) {
|
||||
// Catch the String exceptions thrown from the `runCheckedSync` methods below.
|
||||
} on ProcessException catch (error) {
|
||||
// Catch the String exceptions thrown from the `runSync` methods below.
|
||||
status?.cancel();
|
||||
printError(error);
|
||||
printError(error.toString());
|
||||
return null;
|
||||
}
|
||||
status?.stop();
|
||||
|
|
|
@ -61,7 +61,7 @@ class ChannelCommand extends FlutterCommand {
|
|||
showAll = showAll || currentChannel != currentBranch;
|
||||
|
||||
printStatus('Flutter channels:');
|
||||
final int result = await runCommandAndStreamOutput(
|
||||
final int result = await processUtils.stream(
|
||||
<String>['git', 'branch', '-r'],
|
||||
workingDirectory: Cache.flutterRoot,
|
||||
mapFunction: (String line) {
|
||||
|
@ -111,28 +111,28 @@ class ChannelCommand extends FlutterCommand {
|
|||
|
||||
static Future<void> _checkout(String branchName) async {
|
||||
// Get latest refs from upstream.
|
||||
int result = await runCommandAndStreamOutput(
|
||||
int result = await processUtils.stream(
|
||||
<String>['git', 'fetch'],
|
||||
workingDirectory: Cache.flutterRoot,
|
||||
prefix: 'git: ',
|
||||
);
|
||||
|
||||
if (result == 0) {
|
||||
result = await runCommandAndStreamOutput(
|
||||
result = await processUtils.stream(
|
||||
<String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/$branchName'],
|
||||
workingDirectory: Cache.flutterRoot,
|
||||
prefix: 'git: ',
|
||||
);
|
||||
if (result == 0) {
|
||||
// branch already exists, try just switching to it
|
||||
result = await runCommandAndStreamOutput(
|
||||
result = await processUtils.stream(
|
||||
<String>['git', 'checkout', branchName, '--'],
|
||||
workingDirectory: Cache.flutterRoot,
|
||||
prefix: 'git: ',
|
||||
);
|
||||
} else {
|
||||
// branch does not exist, we have to create it
|
||||
result = await runCommandAndStreamOutput(
|
||||
result = await processUtils.stream(
|
||||
<String>['git', 'checkout', '--track', '-b', branchName, 'origin/$branchName'],
|
||||
workingDirectory: Cache.flutterRoot,
|
||||
prefix: 'git: ',
|
||||
|
|
|
@ -292,7 +292,7 @@ Future<void> _runTests(List<String> testArgs, String observatoryUri) async {
|
|||
|
||||
PackageMap.globalPackagesPath = fs.path.normalize(fs.path.absolute(PackageMap.globalPackagesPath));
|
||||
final String dartVmPath = fs.path.join(dartSdkPath, 'bin', 'dart');
|
||||
final int result = await runCommandAndStreamOutput(
|
||||
final int result = await processUtils.stream(
|
||||
<String>[
|
||||
dartVmPath,
|
||||
...dartVmFlags,
|
||||
|
|
|
@ -76,9 +76,10 @@ class FormatCommand extends FlutterCommand {
|
|||
...argResults.rest,
|
||||
];
|
||||
|
||||
final int result = await runCommandAndStreamOutput(command);
|
||||
if (result != 0)
|
||||
final int result = await processUtils.stream(command);
|
||||
if (result != 0) {
|
||||
throwToolExit('Formatting failed: $result', exitCode: result);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
import '../artifacts.dart';
|
||||
import '../base/common.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/process_manager.dart';
|
||||
import '../base/process.dart';
|
||||
import '../build_info.dart';
|
||||
import '../cache.dart';
|
||||
import '../globals.dart';
|
||||
|
@ -284,7 +283,7 @@ class ArtifactUnpacker {
|
|||
_deleteFrameworkIfPresent(
|
||||
fs.path.join(targetDirectory, fs.path.basename(frameworkPath)));
|
||||
|
||||
final ProcessResult result = processManager
|
||||
final RunResult result = processUtils
|
||||
.runSync(<String>['cp', '-R', frameworkPath, targetDirectory]);
|
||||
if (result.exitCode != 0) {
|
||||
throw Exception(
|
||||
|
|
|
@ -121,7 +121,7 @@ class UpgradeCommandRunner {
|
|||
}
|
||||
|
||||
Future<void> flutterUpgradeContinue() async {
|
||||
final int code = await runCommandAndStreamOutput(
|
||||
final int code = await processUtils.stream(
|
||||
<String>[
|
||||
fs.path.join('bin', 'flutter'),
|
||||
'upgrade',
|
||||
|
@ -146,9 +146,11 @@ class UpgradeCommandRunner {
|
|||
|
||||
Future<bool> hasUncomittedChanges() async {
|
||||
try {
|
||||
final RunResult result = await runCheckedAsync(<String>[
|
||||
'git', 'status', '-s'
|
||||
], workingDirectory: Cache.flutterRoot);
|
||||
final RunResult result = await processUtils.run(
|
||||
<String>['git', 'status', '-s'],
|
||||
throwOnError: true,
|
||||
workingDirectory: Cache.flutterRoot,
|
||||
);
|
||||
return result.stdout.trim().isNotEmpty;
|
||||
} on ProcessException catch (error) {
|
||||
throwToolExit(
|
||||
|
@ -167,9 +169,11 @@ class UpgradeCommandRunner {
|
|||
/// Exits tool if there is no upstream.
|
||||
Future<void> verifyUpstreamConfigured() async {
|
||||
try {
|
||||
await runCheckedAsync(<String>[
|
||||
'git', 'rev-parse', '@{u}',
|
||||
], workingDirectory: Cache.flutterRoot);
|
||||
await processUtils.run(
|
||||
<String>[ 'git', 'rev-parse', '@{u}'],
|
||||
throwOnError: true,
|
||||
workingDirectory: Cache.flutterRoot,
|
||||
);
|
||||
} catch (e) {
|
||||
throwToolExit(
|
||||
'Unable to upgrade Flutter: no origin repository configured. '
|
||||
|
@ -191,9 +195,11 @@ class UpgradeCommandRunner {
|
|||
tag = 'v${gitTagVersion.x}.${gitTagVersion.y}.${gitTagVersion.z}';
|
||||
}
|
||||
try {
|
||||
await runCheckedAsync(<String>[
|
||||
'git', 'reset', '--hard', tag,
|
||||
], workingDirectory: Cache.flutterRoot);
|
||||
await processUtils.run(
|
||||
<String>['git', 'reset', '--hard', tag],
|
||||
throwOnError: true,
|
||||
workingDirectory: Cache.flutterRoot,
|
||||
);
|
||||
} on ProcessException catch (error) {
|
||||
throwToolExit(
|
||||
'Unable to upgrade Flutter: The tool could not update to the version $tag. '
|
||||
|
@ -218,7 +224,7 @@ class UpgradeCommandRunner {
|
|||
/// If there haven't been any hot fixes or local changes, this is equivalent
|
||||
/// to a fast-forward.
|
||||
Future<void> attemptFastForward() async {
|
||||
final int code = await runCommandAndStreamOutput(
|
||||
final int code = await processUtils.stream(
|
||||
<String>['git', 'pull', '--ff'],
|
||||
workingDirectory: Cache.flutterRoot,
|
||||
mapFunction: (String line) => matchesGitLine(line) ? null : line,
|
||||
|
@ -236,7 +242,7 @@ class UpgradeCommandRunner {
|
|||
Future<void> precacheArtifacts() async {
|
||||
printStatus('');
|
||||
printStatus('Upgrading engine...');
|
||||
final int code = await runCommandAndStreamOutput(
|
||||
final int code = await processUtils.stream(
|
||||
<String>[
|
||||
fs.path.join('bin', 'flutter'), '--no-color', '--no-version-check', 'precache',
|
||||
],
|
||||
|
@ -263,7 +269,7 @@ class UpgradeCommandRunner {
|
|||
Future<void> runDoctor() async {
|
||||
printStatus('');
|
||||
printStatus('Running flutter doctor...');
|
||||
await runCommandAndStreamOutput(
|
||||
await processUtils.stream(
|
||||
<String>[
|
||||
fs.path.join('bin', 'flutter'), '--no-version-check', 'doctor',
|
||||
],
|
||||
|
|
|
@ -37,8 +37,9 @@ class VersionCommand extends FlutterCommand {
|
|||
Future<List<String>> getTags() async {
|
||||
RunResult runResult;
|
||||
try {
|
||||
runResult = await runCheckedAsync(
|
||||
runResult = await processUtils.run(
|
||||
<String>['git', 'tag', '-l', 'v*', '--sort=-creatordate'],
|
||||
throwOnError: true,
|
||||
workingDirectory: Cache.flutterRoot,
|
||||
);
|
||||
} on ProcessException catch (error) {
|
||||
|
@ -83,8 +84,9 @@ class VersionCommand extends FlutterCommand {
|
|||
}
|
||||
|
||||
try {
|
||||
await runCheckedAsync(
|
||||
await processUtils.run(
|
||||
<String>['git', 'checkout', 'v$version'],
|
||||
throwOnError: true,
|
||||
workingDirectory: Cache.flutterRoot,
|
||||
);
|
||||
} catch (e) {
|
||||
|
@ -101,7 +103,7 @@ class VersionCommand extends FlutterCommand {
|
|||
// if necessary.
|
||||
printStatus('');
|
||||
printStatus('Downloading engine...');
|
||||
int code = await runCommandAndStreamOutput(<String>[
|
||||
int code = await processUtils.stream(<String>[
|
||||
fs.path.join('bin', 'flutter'),
|
||||
'--no-color',
|
||||
'precache',
|
||||
|
@ -128,7 +130,7 @@ class VersionCommand extends FlutterCommand {
|
|||
// Run a doctor check in case system requirements have changed.
|
||||
printStatus('');
|
||||
printStatus('Running flutter doctor...');
|
||||
code = await runCommandAndStreamOutput(
|
||||
code = await processUtils.stream(
|
||||
<String>[
|
||||
fs.path.join('bin', 'flutter'),
|
||||
'doctor',
|
||||
|
|
|
@ -19,6 +19,7 @@ import 'base/io.dart';
|
|||
import 'base/logger.dart';
|
||||
import 'base/os.dart';
|
||||
import 'base/platform.dart';
|
||||
import 'base/process.dart';
|
||||
import 'base/time.dart';
|
||||
import 'base/user_messages.dart';
|
||||
import 'base/utils.dart';
|
||||
|
@ -101,6 +102,7 @@ Future<T> runInContext<T>(
|
|||
Logger: () => platform.isWindows ? WindowsStdoutLogger() : StdoutLogger(),
|
||||
MacOSWorkflow: () => const MacOSWorkflow(),
|
||||
OperatingSystemUtils: () => OperatingSystemUtils(),
|
||||
ProcessUtils: () => ProcessUtils(),
|
||||
SimControl: () => SimControl(),
|
||||
Stdio: () => const Stdio(),
|
||||
SystemClock: () => const SystemClock(),
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:meta/meta.dart';
|
|||
|
||||
import '../base/common.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart' as io;
|
||||
import '../base/logger.dart';
|
||||
import '../base/platform.dart';
|
||||
import '../base/process.dart';
|
||||
|
@ -156,22 +157,25 @@ Future<void> pub(
|
|||
int code;
|
||||
while (true) {
|
||||
attempts += 1;
|
||||
code = await runCommandAndStreamOutput(
|
||||
code = await processUtils.stream(
|
||||
_pubCommand(arguments),
|
||||
workingDirectory: directory,
|
||||
mapFunction: filter,
|
||||
environment: _createPubEnvironment(context),
|
||||
);
|
||||
if (code != 69) // UNAVAILABLE in https://github.com/dart-lang/pub/blob/master/lib/src/exit_codes.dart
|
||||
if (code != 69) { // UNAVAILABLE in https://github.com/dart-lang/pub/blob/master/lib/src/exit_codes.dart
|
||||
break;
|
||||
}
|
||||
printStatus('$failureMessage ($code) -- attempting retry $attempts in $duration second${ duration == 1 ? "" : "s"}...');
|
||||
await Future<void>.delayed(Duration(seconds: duration));
|
||||
if (duration < 64)
|
||||
if (duration < 64) {
|
||||
duration *= 2;
|
||||
}
|
||||
}
|
||||
assert(code != null);
|
||||
if (code != 0)
|
||||
if (code != 0) {
|
||||
throwToolExit('$failureMessage ($code)', exitCode: code);
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs pub in 'interactive' mode, directly piping the stdin stream of this
|
||||
|
@ -182,13 +186,26 @@ Future<void> pubInteractively(
|
|||
String directory,
|
||||
}) async {
|
||||
Cache.releaseLockEarly();
|
||||
final int code = await runInteractively(
|
||||
final io.Process process = await processUtils.start(
|
||||
_pubCommand(arguments),
|
||||
workingDirectory: directory,
|
||||
environment: _createPubEnvironment(PubContext.interactive),
|
||||
);
|
||||
if (code != 0)
|
||||
|
||||
// Pipe the Flutter tool stdin to the pub stdin.
|
||||
unawaited(process.stdin.addStream(io.stdin));
|
||||
|
||||
// Pipe the put stdout and stderr to the tool stdout and stderr.
|
||||
await Future.wait<dynamic>(<Future<dynamic>>[
|
||||
io.stdout.addStream(process.stdout),
|
||||
io.stderr.addStream(process.stderr),
|
||||
]);
|
||||
|
||||
// Wait for pub to exit.
|
||||
final int code = await process.exitCode;
|
||||
if (code != 0) {
|
||||
throwToolExit('pub finished with exit code $code', exitCode: code);
|
||||
}
|
||||
}
|
||||
|
||||
/// The command used for running pub.
|
||||
|
|
|
@ -14,7 +14,7 @@ import 'base/file_system.dart';
|
|||
import 'base/logger.dart';
|
||||
import 'base/os.dart';
|
||||
import 'base/platform.dart';
|
||||
import 'base/process_manager.dart';
|
||||
import 'base/process.dart';
|
||||
import 'base/terminal.dart';
|
||||
import 'base/user_messages.dart';
|
||||
import 'base/utils.dart';
|
||||
|
@ -607,7 +607,7 @@ class FlutterValidator extends DoctorValidator {
|
|||
bool _genSnapshotRuns(String genSnapshotPath) {
|
||||
const int kExpectedExitCode = 255;
|
||||
try {
|
||||
return processManager.runSync(<String>[genSnapshotPath]).exitCode == kExpectedExitCode;
|
||||
return processUtils.runSync(<String>[genSnapshotPath]).exitCode == kExpectedExitCode;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -8,8 +8,7 @@ import 'dart:math' as math;
|
|||
import 'android/android_emulator.dart';
|
||||
import 'android/android_sdk.dart';
|
||||
import 'base/context.dart';
|
||||
import 'base/io.dart' show ProcessResult;
|
||||
import 'base/process_manager.dart';
|
||||
import 'base/process.dart';
|
||||
import 'device.dart';
|
||||
import 'globals.dart';
|
||||
import 'ios/ios_emulators.dart';
|
||||
|
@ -118,7 +117,7 @@ class EmulatorManager {
|
|||
'-k', sdkId,
|
||||
'-d', device,
|
||||
];
|
||||
final ProcessResult runResult = processManager.runSync(args,
|
||||
final RunResult runResult = processUtils.runSync(args,
|
||||
environment: androidSdk?.sdkManagerEnv);
|
||||
return CreateEmulatorResult(
|
||||
name,
|
||||
|
@ -139,10 +138,11 @@ class EmulatorManager {
|
|||
'device',
|
||||
'-c',
|
||||
];
|
||||
final ProcessResult runResult = processManager.runSync(args,
|
||||
final RunResult runResult = processUtils.runSync(args,
|
||||
environment: androidSdk?.sdkManagerEnv);
|
||||
if (runResult.exitCode != 0)
|
||||
if (runResult.exitCode != 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final List<String> availableDevices = runResult.stdout
|
||||
.split('\n')
|
||||
|
@ -165,7 +165,7 @@ class EmulatorManager {
|
|||
'avd',
|
||||
'-n', 'temp',
|
||||
];
|
||||
final ProcessResult runResult = processManager.runSync(args,
|
||||
final RunResult runResult = processUtils.runSync(args,
|
||||
environment: androidSdk?.sdkManagerEnv);
|
||||
|
||||
// Get the list of IDs that match our criteria
|
||||
|
|
|
@ -32,7 +32,7 @@ class FuchsiaDevFinder {
|
|||
'list',
|
||||
'-full'
|
||||
];
|
||||
final RunResult result = await runAsync(command);
|
||||
final RunResult result = await processUtils.run(command);
|
||||
if (result.exitCode != 0) {
|
||||
printError('dev_finder failed: ${result.stderr}');
|
||||
return null;
|
||||
|
@ -57,7 +57,7 @@ class FuchsiaDevFinder {
|
|||
'-device-limit', '1',
|
||||
deviceName
|
||||
];
|
||||
final RunResult result = await runAsync(command);
|
||||
final RunResult result = await processUtils.run(command);
|
||||
if (result.exitCode != 0) {
|
||||
printError('dev_finder failed: ${result.stderr}');
|
||||
return null;
|
||||
|
|
|
@ -454,7 +454,7 @@ class FuchsiaDevice extends Device {
|
|||
throwToolExit('Cannot interact with device. No ssh config.\n'
|
||||
'Try setting FUCHSIA_SSH_CONFIG or FUCHSIA_BUILD_DIR.');
|
||||
}
|
||||
return await runAsync(<String>[
|
||||
return await processUtils.run(<String>[
|
||||
'ssh',
|
||||
'-F',
|
||||
fuchsiaArtifacts.sshConfig.absolute.path,
|
||||
|
|
|
@ -85,7 +85,7 @@ class FuchsiaKernelCompiler {
|
|||
fuchsiaArtifacts.kernelCompiler.path,
|
||||
...flags,
|
||||
];
|
||||
final Process process = await runCommand(command);
|
||||
final Process process = await processUtils.start(command);
|
||||
final Status status = logger.startProgress(
|
||||
'Building Fuchsia application...',
|
||||
timeout: null,
|
||||
|
|
|
@ -118,7 +118,7 @@ class FuchsiaPM {
|
|||
'-l',
|
||||
'$host:$port',
|
||||
];
|
||||
final Process process = await runCommand(command);
|
||||
final Process process = await processUtils.start(command);
|
||||
process.stdout
|
||||
.transform(utf8.decoder)
|
||||
.transform(const LineSplitter())
|
||||
|
@ -152,7 +152,7 @@ class FuchsiaPM {
|
|||
throwToolExit('Fuchsia pm tool not found');
|
||||
}
|
||||
final List<String> command = <String>[fuchsiaArtifacts.pm.path] + args;
|
||||
final RunResult result = await runAsync(command);
|
||||
final RunResult result = await processUtils.run(command);
|
||||
return result.exitCode == 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,8 +96,9 @@ Future<Map<String, String>> getCodeSigningIdentityDevelopmentTeam({
|
|||
BuildableIOSApp iosApp,
|
||||
}) async {
|
||||
final Map<String, String> buildSettings = iosApp.project.buildSettings;
|
||||
if (buildSettings == null)
|
||||
if (buildSettings == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If the user already has it set in the project build settings itself,
|
||||
// continue with that.
|
||||
|
@ -114,16 +115,21 @@ Future<Map<String, String>> getCodeSigningIdentityDevelopmentTeam({
|
|||
|
||||
// If the user's environment is missing the tools needed to find and read
|
||||
// certificates, abandon. Tools should be pre-equipped on macOS.
|
||||
if (!exitsHappy(const <String>['which', 'security']) || !exitsHappy(const <String>['which', 'openssl']))
|
||||
if (!await processUtils.exitsHappy(const <String>['which', 'security']) ||
|
||||
!await processUtils.exitsHappy(const <String>['which', 'openssl'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const List<String> findIdentityCommand =
|
||||
<String>['security', 'find-identity', '-p', 'codesigning', '-v'];
|
||||
|
||||
String findIdentityStdout;
|
||||
try {
|
||||
findIdentityStdout = runCheckedSync(findIdentityCommand);
|
||||
} catch (error) {
|
||||
findIdentityStdout = (await processUtils.run(
|
||||
findIdentityCommand,
|
||||
throwOnError: true,
|
||||
)).stdout.trim();
|
||||
} on ProcessException catch (error) {
|
||||
printTrace('Unexpected failure from find-identity: $error.');
|
||||
return null;
|
||||
}
|
||||
|
@ -142,8 +148,9 @@ Future<Map<String, String>> getCodeSigningIdentityDevelopmentTeam({
|
|||
final String signingIdentity = await _chooseSigningIdentity(validCodeSigningIdentities);
|
||||
|
||||
// If none are chosen, return null.
|
||||
if (signingIdentity == null)
|
||||
if (signingIdentity == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
printStatus('Signing iOS app for device deployment using developer identity: "$signingIdentity"');
|
||||
|
||||
|
@ -153,20 +160,23 @@ Future<Map<String, String>> getCodeSigningIdentityDevelopmentTeam({
|
|||
?.group(1);
|
||||
|
||||
// If `security`'s output format changes, we'd have to update the above regex.
|
||||
if (signingCertificateId == null)
|
||||
if (signingCertificateId == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String signingCertificateStdout;
|
||||
try {
|
||||
signingCertificateStdout = runCheckedSync(
|
||||
<String>['security', 'find-certificate', '-c', signingCertificateId, '-p']
|
||||
);
|
||||
} catch (error) {
|
||||
signingCertificateStdout = (await processUtils.run(
|
||||
<String>['security', 'find-certificate', '-c', signingCertificateId, '-p'],
|
||||
throwOnError: true,
|
||||
)).stdout.trim();
|
||||
} on ProcessException catch (error) {
|
||||
printTrace('Couldn\'t find the certificate: $error.');
|
||||
return null;
|
||||
}
|
||||
|
||||
final Process opensslProcess = await runCommand(const <String>['openssl', 'x509', '-subject']);
|
||||
final Process opensslProcess = await processUtils.start(
|
||||
const <String>['openssl', 'x509', '-subject']);
|
||||
await (opensslProcess.stdin..write(signingCertificateStdout)).close();
|
||||
|
||||
final String opensslOutput = await utf8.decodeStream(opensslProcess.stdout);
|
||||
|
|
|
@ -64,7 +64,7 @@ class IOSDeploy {
|
|||
iosDeployEnv['PATH'] = '/usr/bin:${iosDeployEnv['PATH']}';
|
||||
iosDeployEnv.addEntries(<MapEntry<String, String>>[cache.dyLdLibEntry]);
|
||||
|
||||
return await runCommandAndStreamOutput(
|
||||
return await processUtils.stream(
|
||||
launchCommand,
|
||||
mapFunction: _monitorInstallationFailure,
|
||||
trace: true,
|
||||
|
@ -195,8 +195,9 @@ class IOSDevice extends Device {
|
|||
Future<bool> isAppInstalled(ApplicationPackage app) async {
|
||||
RunResult apps;
|
||||
try {
|
||||
apps = await runCheckedAsync(
|
||||
apps = await processUtils.run(
|
||||
<String>[_installerPath, '--list-apps'],
|
||||
throwOnError: true,
|
||||
environment: Map<String, String>.fromEntries(
|
||||
<MapEntry<String, String>>[cache.dyLdLibEntry],
|
||||
),
|
||||
|
@ -220,8 +221,9 @@ class IOSDevice extends Device {
|
|||
}
|
||||
|
||||
try {
|
||||
await runCheckedAsync(
|
||||
await processUtils.run(
|
||||
<String>[_installerPath, '-i', iosApp.deviceBundlePath],
|
||||
throwOnError: true,
|
||||
environment: Map<String, String>.fromEntries(
|
||||
<MapEntry<String, String>>[cache.dyLdLibEntry],
|
||||
),
|
||||
|
@ -236,8 +238,9 @@ class IOSDevice extends Device {
|
|||
@override
|
||||
Future<bool> uninstallApp(ApplicationPackage app) async {
|
||||
try {
|
||||
await runCheckedAsync(
|
||||
await processUtils.run(
|
||||
<String>[_installerPath, '-U', app.id],
|
||||
throwOnError: true,
|
||||
environment: Map<String, String>.fromEntries(
|
||||
<MapEntry<String, String>>[cache.dyLdLibEntry],
|
||||
),
|
||||
|
@ -610,7 +613,7 @@ class _IOSDevicePortForwarder extends DevicePortForwarder {
|
|||
while (!connected) {
|
||||
printTrace('attempting to forward device port $devicePort to host port $hostPort');
|
||||
// Usage: iproxy LOCAL_TCP_PORT DEVICE_TCP_PORT UDID
|
||||
process = await runCommand(
|
||||
process = await processUtils.start(
|
||||
<String>[
|
||||
device._iproxyPath,
|
||||
hostPort.toString(),
|
||||
|
|
|
@ -47,7 +47,7 @@ class IOSEmulator extends Emulator {
|
|||
.followedBy(<String>['-a', xcode.getSimulatorPath()])
|
||||
.toList();
|
||||
|
||||
final RunResult launchResult = await runAsync(args);
|
||||
final RunResult launchResult = await processUtils.run(args);
|
||||
if (launchResult.exitCode != 0) {
|
||||
printError('$launchResult');
|
||||
return false;
|
||||
|
|
|
@ -107,7 +107,7 @@ class IMobileDevice {
|
|||
final String _idevicescreenshotPath;
|
||||
|
||||
bool get isInstalled {
|
||||
_isInstalled ??= exitsHappy(
|
||||
_isInstalled ??= processUtils.exitsHappySync(
|
||||
<String>[
|
||||
_ideviceIdPath,
|
||||
'-h'
|
||||
|
@ -136,7 +136,7 @@ class IMobileDevice {
|
|||
final Map<String, String> executionEnv = Map<String, String>.fromEntries(
|
||||
<MapEntry<String, String>>[cache.dyLdLibEntry]
|
||||
);
|
||||
final ProcessResult ideviceResult = (await runAsync(
|
||||
final ProcessResult ideviceResult = (await processUtils.run(
|
||||
<String>[
|
||||
_ideviceinfoPath,
|
||||
'-u',
|
||||
|
@ -150,7 +150,7 @@ class IMobileDevice {
|
|||
}
|
||||
|
||||
// If no device is attached, we're unable to detect any problems. Assume all is well.
|
||||
final ProcessResult result = (await runAsync(
|
||||
final ProcessResult result = (await processUtils.run(
|
||||
<String>[
|
||||
_ideviceIdPath,
|
||||
'-l',
|
||||
|
@ -161,7 +161,7 @@ class IMobileDevice {
|
|||
_isWorking = true;
|
||||
} else {
|
||||
// Check that we can look up the names of any attached devices.
|
||||
_isWorking = await exitsHappyAsync(
|
||||
_isWorking = await processUtils.exitsHappy(
|
||||
<String>[_idevicenamePath],
|
||||
environment: executionEnv,
|
||||
);
|
||||
|
@ -229,7 +229,7 @@ class IMobileDevice {
|
|||
|
||||
/// Starts `idevicesyslog` and returns the running process.
|
||||
Future<Process> startLogger(String deviceID) {
|
||||
return runCommand(
|
||||
return processUtils.start(
|
||||
<String>[
|
||||
_idevicesyslogPath,
|
||||
'-u',
|
||||
|
@ -243,11 +243,12 @@ class IMobileDevice {
|
|||
|
||||
/// Captures a screenshot to the specified outputFile.
|
||||
Future<void> takeScreenshot(File outputFile) {
|
||||
return runCheckedAsync(
|
||||
return processUtils.run(
|
||||
<String>[
|
||||
_idevicescreenshotPath,
|
||||
outputFile.path
|
||||
],
|
||||
throwOnError: true,
|
||||
environment: Map<String, String>.fromEntries(
|
||||
<MapEntry<String, String>>[cache.dyLdLibEntry]
|
||||
),
|
||||
|
@ -318,8 +319,9 @@ Future<XcodeBuildResult> buildXcodeProject({
|
|||
}
|
||||
|
||||
Map<String, String> autoSigningConfigs;
|
||||
if (codesign && buildForDevice)
|
||||
if (codesign && buildForDevice) {
|
||||
autoSigningConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
|
||||
}
|
||||
|
||||
// Before the build, all service definitions must be updated and the dylibs
|
||||
// copied over to a location that is suitable for Xcodebuild to find them.
|
||||
|
@ -440,7 +442,7 @@ Future<XcodeBuildResult> buildXcodeProject({
|
|||
|
||||
final Stopwatch sw = Stopwatch()..start();
|
||||
initialBuildStatus = logger.startProgress('Running Xcode build...', timeout: timeoutConfiguration.fastOperation);
|
||||
final RunResult buildResult = await runAsync(
|
||||
final RunResult buildResult = await processUtils.run(
|
||||
buildCommands,
|
||||
workingDirectory: app.project.hostAppRoot.path,
|
||||
allowReentrantFlutter: true,
|
||||
|
@ -476,8 +478,9 @@ Future<XcodeBuildResult> buildXcodeProject({
|
|||
const Duration showBuildSettingsTimeout = Duration(minutes: 1);
|
||||
Map<String, String> buildSettings;
|
||||
try {
|
||||
final RunResult showBuildSettingsResult = await runCheckedAsync(
|
||||
final RunResult showBuildSettingsResult = await processUtils.run(
|
||||
showBuildSettingsCommand,
|
||||
throwOnError: true,
|
||||
workingDirectory: app.project.hostAppRoot.path,
|
||||
timeout: showBuildSettingsTimeout,
|
||||
timeoutRetries: 1,
|
||||
|
@ -677,7 +680,10 @@ Future<void> _copyServiceFrameworks(List<Map<String, String>> services, Director
|
|||
continue;
|
||||
}
|
||||
// Shell out so permissions on the dylib are preserved.
|
||||
await runCheckedAsync(<String>['/bin/cp', dylib.path, frameworksDirectory.path]);
|
||||
await processUtils.run(
|
||||
<String>['/bin/cp', dylib.path, frameworksDirectory.path],
|
||||
throwOnError: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import '../base/context.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/process.dart';
|
||||
import '../convert.dart';
|
||||
import '../globals.dart';
|
||||
|
@ -38,9 +39,12 @@ class PlistParser {
|
|||
final List<String> args = <String>[
|
||||
executable, '-convert', 'json', '-o', '-', normalizedPlistPath,
|
||||
];
|
||||
final String jsonContent = runCheckedSync(args);
|
||||
final String jsonContent = processUtils.runSync(
|
||||
args,
|
||||
throwOnError: true,
|
||||
).stdout.trim();
|
||||
return json.decode(jsonContent);
|
||||
} catch (error) {
|
||||
} on ProcessException catch (error) {
|
||||
printTrace('$error');
|
||||
return const <String, dynamic>{};
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@ class SimControl {
|
|||
}
|
||||
|
||||
Future<bool> isInstalled(String deviceId, String appId) {
|
||||
return exitsHappyAsync(<String>[
|
||||
return processUtils.exitsHappy(<String>[
|
||||
_xcrunPath,
|
||||
'simctl',
|
||||
'get_app_container',
|
||||
|
@ -142,7 +142,10 @@ class SimControl {
|
|||
Future<RunResult> install(String deviceId, String appPath) {
|
||||
Future<RunResult> result;
|
||||
try {
|
||||
result = runCheckedAsync(<String>[_xcrunPath, 'simctl', 'install', deviceId, appPath]);
|
||||
result = processUtils.run(
|
||||
<String>[_xcrunPath, 'simctl', 'install', deviceId, appPath],
|
||||
throwOnError: true,
|
||||
);
|
||||
} on ProcessException catch (exception) {
|
||||
throwToolExit('Unable to install $appPath on $deviceId:\n$exception');
|
||||
}
|
||||
|
@ -152,7 +155,10 @@ class SimControl {
|
|||
Future<RunResult> uninstall(String deviceId, String appId) {
|
||||
Future<RunResult> result;
|
||||
try {
|
||||
result = runCheckedAsync(<String>[_xcrunPath, 'simctl', 'uninstall', deviceId, appId]);
|
||||
result = processUtils.run(
|
||||
<String>[_xcrunPath, 'simctl', 'uninstall', deviceId, appId],
|
||||
throwOnError: true,
|
||||
);
|
||||
} on ProcessException catch (exception) {
|
||||
throwToolExit('Unable to uninstall $appId from $deviceId:\n$exception');
|
||||
}
|
||||
|
@ -162,14 +168,17 @@ class SimControl {
|
|||
Future<RunResult> launch(String deviceId, String appIdentifier, [ List<String> launchArgs ]) {
|
||||
Future<RunResult> result;
|
||||
try {
|
||||
result = runCheckedAsync(<String>[
|
||||
_xcrunPath,
|
||||
'simctl',
|
||||
'launch',
|
||||
deviceId,
|
||||
appIdentifier,
|
||||
...?launchArgs,
|
||||
]);
|
||||
result = processUtils.run(
|
||||
<String>[
|
||||
_xcrunPath,
|
||||
'simctl',
|
||||
'launch',
|
||||
deviceId,
|
||||
appIdentifier,
|
||||
...?launchArgs,
|
||||
],
|
||||
throwOnError: true,
|
||||
);
|
||||
} on ProcessException catch (exception) {
|
||||
throwToolExit('Unable to launch $appIdentifier on $deviceId:\n$exception');
|
||||
}
|
||||
|
@ -178,7 +187,10 @@ class SimControl {
|
|||
|
||||
Future<void> takeScreenshot(String deviceId, String outputPath) async {
|
||||
try {
|
||||
await runCheckedAsync(<String>[_xcrunPath, 'simctl', 'io', deviceId, 'screenshot', outputPath]);
|
||||
await processUtils.run(
|
||||
<String>[_xcrunPath, 'simctl', 'io', deviceId, 'screenshot', outputPath],
|
||||
throwOnError: true,
|
||||
);
|
||||
} on ProcessException catch (exception) {
|
||||
throwToolExit('Unable to take screenshot of $deviceId:\n$exception');
|
||||
}
|
||||
|
@ -518,20 +530,22 @@ class IOSSimulator extends Device {
|
|||
/// Launches the device log reader process on the host.
|
||||
Future<Process> launchDeviceLogTool(IOSSimulator device) async {
|
||||
// Versions of iOS prior to iOS 11 log to the simulator syslog file.
|
||||
if (await device.sdkMajorVersion < 11)
|
||||
return runCommand(<String>['tail', '-n', '0', '-F', device.logFilePath]);
|
||||
if (await device.sdkMajorVersion < 11) {
|
||||
return processUtils.start(<String>['tail', '-n', '0', '-F', device.logFilePath]);
|
||||
}
|
||||
|
||||
// For iOS 11 and above, use /usr/bin/log to tail process logs.
|
||||
// Run in interactive mode (via script), otherwise /usr/bin/log buffers in 4k chunks. (radar: 34420207)
|
||||
return runCommand(<String>[
|
||||
return processUtils.start(<String>[
|
||||
'script', '/dev/null', '/usr/bin/log', 'stream', '--style', 'syslog', '--predicate', 'processImagePath CONTAINS "${device.id}"',
|
||||
]);
|
||||
}
|
||||
|
||||
Future<Process> launchSystemLogTool(IOSSimulator device) async {
|
||||
// Versions of iOS prior to 11 tail the simulator syslog file.
|
||||
if (await device.sdkMajorVersion < 11)
|
||||
return runCommand(<String>['tail', '-n', '0', '-F', '/private/var/log/system.log']);
|
||||
if (await device.sdkMajorVersion < 11) {
|
||||
return processUtils.start(<String>['tail', '-n', '0', '-F', '/private/var/log/system.log']);
|
||||
}
|
||||
|
||||
// For iOS 11 and later, all relevant detail is in the device log.
|
||||
return null;
|
||||
|
|
|
@ -14,7 +14,6 @@ import '../base/logger.dart';
|
|||
import '../base/os.dart';
|
||||
import '../base/platform.dart';
|
||||
import '../base/process.dart';
|
||||
import '../base/process_manager.dart';
|
||||
import '../base/utils.dart';
|
||||
import '../build_info.dart';
|
||||
import '../cache.dart';
|
||||
|
@ -211,7 +210,9 @@ class XcodeProjectInterpreter {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
final ProcessResult result = processManager.runSync(<String>[_executable, '-version']);
|
||||
final RunResult result = processUtils.runSync(
|
||||
<String>[_executable, '-version'],
|
||||
);
|
||||
if (result.exitCode != 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -255,16 +256,20 @@ class XcodeProjectInterpreter {
|
|||
/// version below.
|
||||
Map<String, String> getBuildSettings(String projectPath, String target) {
|
||||
try {
|
||||
final String out = runCheckedSync(<String>[
|
||||
_executable,
|
||||
'-project',
|
||||
fs.path.absolute(projectPath),
|
||||
'-target',
|
||||
target,
|
||||
'-showBuildSettings',
|
||||
], workingDirectory: projectPath);
|
||||
final String out = processUtils.runSync(
|
||||
<String>[
|
||||
_executable,
|
||||
'-project',
|
||||
fs.path.absolute(projectPath),
|
||||
'-target',
|
||||
target,
|
||||
'-showBuildSettings',
|
||||
],
|
||||
throwOnError: true,
|
||||
workingDirectory: projectPath,
|
||||
).stdout.trim();
|
||||
return parseXcodeBuildSettings(out);
|
||||
} catch(error) {
|
||||
} on ProcessException catch (error) {
|
||||
printTrace('Unexpected failure to get the build settings: $error.');
|
||||
return const <String, String>{};
|
||||
}
|
||||
|
@ -291,8 +296,9 @@ class XcodeProjectInterpreter {
|
|||
// showBuildSettings is reported to ocassionally timeout. Here, we give it
|
||||
// a lot of wiggle room (locally on Flutter Gallery, this takes ~1s).
|
||||
// When there is a timeout, we retry once.
|
||||
final RunResult result = await runCheckedAsync(
|
||||
final RunResult result = await processUtils.run(
|
||||
showBuildSettingsCommand,
|
||||
throwOnError: true,
|
||||
workingDirectory: projectPath,
|
||||
timeout: timeout,
|
||||
timeoutRetries: 1,
|
||||
|
@ -313,7 +319,7 @@ class XcodeProjectInterpreter {
|
|||
}
|
||||
|
||||
void cleanWorkspace(String workspacePath, String scheme) {
|
||||
runSync(<String>[
|
||||
processUtils.runSync(<String>[
|
||||
_executable,
|
||||
'-workspace',
|
||||
workspacePath,
|
||||
|
@ -325,11 +331,15 @@ class XcodeProjectInterpreter {
|
|||
}
|
||||
|
||||
Future<XcodeProjectInfo> getInfo(String projectPath, {String projectFilename}) async {
|
||||
final RunResult result = await runCheckedAsync(<String>[
|
||||
_executable,
|
||||
'-list',
|
||||
if (projectFilename != null) ...<String>['-project', projectFilename],
|
||||
], workingDirectory: projectPath);
|
||||
final RunResult result = await processUtils.run(
|
||||
<String>[
|
||||
_executable,
|
||||
'-list',
|
||||
if (projectFilename != null) ...<String>['-project', projectFilename],
|
||||
],
|
||||
throwOnError: true,
|
||||
workingDirectory: projectPath,
|
||||
);
|
||||
return XcodeProjectInfo.fromXcodeBuildOutput(result.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,10 +67,11 @@ class CocoaPods {
|
|||
String get cocoaPodsMinimumVersion => '1.6.0';
|
||||
String get cocoaPodsRecommendedVersion => '1.6.0';
|
||||
|
||||
Future<bool> get isInstalled => exitsHappyAsync(<String>['which', 'pod']);
|
||||
Future<bool> get isInstalled =>
|
||||
processUtils.exitsHappy(<String>['which', 'pod']);
|
||||
|
||||
Future<String> get cocoaPodsVersionText {
|
||||
_versionText ??= runAsync(<String>['pod', '--version']).then<String>((RunResult result) {
|
||||
_versionText ??= processUtils.run(<String>['pod', '--version']).then<String>((RunResult result) {
|
||||
return result.exitCode == 0 ? result.stdout.trim() : null;
|
||||
}, onError: (dynamic _) => null);
|
||||
return _versionText;
|
||||
|
|
|
@ -10,7 +10,6 @@ import '../base/file_system.dart';
|
|||
import '../base/io.dart';
|
||||
import '../base/platform.dart';
|
||||
import '../base/process.dart';
|
||||
import '../base/process_manager.dart';
|
||||
import '../ios/xcodeproj.dart';
|
||||
|
||||
const int kXcodeRequiredVersionMajor = 9;
|
||||
|
@ -25,7 +24,9 @@ class Xcode {
|
|||
String get xcodeSelectPath {
|
||||
if (_xcodeSelectPath == null) {
|
||||
try {
|
||||
_xcodeSelectPath = processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']).stdout.trim();
|
||||
_xcodeSelectPath = processUtils.runSync(
|
||||
<String>['/usr/bin/xcode-select', '--print-path'],
|
||||
).stdout.trim();
|
||||
} on ProcessException {
|
||||
// Ignored, return null below.
|
||||
} on ArgumentError {
|
||||
|
@ -52,13 +53,16 @@ class Xcode {
|
|||
bool get eulaSigned {
|
||||
if (_eulaSigned == null) {
|
||||
try {
|
||||
final ProcessResult result = processManager.runSync(<String>['/usr/bin/xcrun', 'clang']);
|
||||
if (result.stdout != null && result.stdout.contains('license'))
|
||||
final RunResult result = processUtils.runSync(
|
||||
<String>['/usr/bin/xcrun', 'clang'],
|
||||
);
|
||||
if (result.stdout != null && result.stdout.contains('license')) {
|
||||
_eulaSigned = false;
|
||||
else if (result.stderr != null && result.stderr.contains('license'))
|
||||
} else if (result.stderr != null && result.stderr.contains('license')) {
|
||||
_eulaSigned = false;
|
||||
else
|
||||
} else {
|
||||
_eulaSigned = true;
|
||||
}
|
||||
} on ProcessException {
|
||||
_eulaSigned = false;
|
||||
}
|
||||
|
@ -74,7 +78,9 @@ class Xcode {
|
|||
try {
|
||||
// This command will error if additional components need to be installed in
|
||||
// xcode 9.2 and above.
|
||||
final ProcessResult result = processManager.runSync(<String>['/usr/bin/xcrun', 'simctl', 'list']);
|
||||
final RunResult result = processUtils.runSync(
|
||||
<String>['/usr/bin/xcrun', 'simctl', 'list'],
|
||||
);
|
||||
_isSimctlInstalled = result.stderr == null || result.stderr == '';
|
||||
} on ProcessException {
|
||||
_isSimctlInstalled = false;
|
||||
|
@ -94,16 +100,23 @@ class Xcode {
|
|||
}
|
||||
|
||||
Future<RunResult> cc(List<String> args) {
|
||||
return runCheckedAsync(<String>['xcrun', 'cc', ...args]);
|
||||
return processUtils.run(
|
||||
<String>['xcrun', 'cc', ...args],
|
||||
throwOnError: true,
|
||||
);
|
||||
}
|
||||
|
||||
Future<RunResult> clang(List<String> args) {
|
||||
return runCheckedAsync(<String>['xcrun', 'clang', ...args]);
|
||||
return processUtils.run(
|
||||
<String>['xcrun', 'clang', ...args],
|
||||
throwOnError: true,
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> iPhoneSdkLocation() async {
|
||||
final RunResult runResult = await runCheckedAsync(
|
||||
final RunResult runResult = await processUtils.run(
|
||||
<String>['xcrun', '--sdk', 'iphoneos', '--show-sdk-path'],
|
||||
throwOnError: true,
|
||||
);
|
||||
if (runResult.exitCode != 0) {
|
||||
throwToolExit('Could not find iPhone SDK location: ${runResult.stderr}');
|
||||
|
|
|
@ -11,7 +11,7 @@ import '../base/io.dart';
|
|||
import '../base/logger.dart';
|
||||
import '../base/os.dart';
|
||||
import '../base/platform.dart';
|
||||
import '../base/process_manager.dart';
|
||||
import '../base/process.dart';
|
||||
import '../dart/package_map.dart';
|
||||
import '../globals.dart';
|
||||
import '../vmservice.dart';
|
||||
|
@ -150,7 +150,7 @@ class CoverageCollector extends TestWatcher {
|
|||
final Directory tempDir = fs.systemTempDirectory.createTempSync('flutter_tools_test_coverage.');
|
||||
try {
|
||||
final File sourceFile = coverageFile.copySync(fs.path.join(tempDir.path, 'lcov.source.info'));
|
||||
final ProcessResult result = processManager.runSync(<String>[
|
||||
final RunResult result = processUtils.runSync(<String>[
|
||||
'lcov',
|
||||
'--add-tracefile', baseCoverageData,
|
||||
'--add-tracefile', sourceFile.path,
|
||||
|
|
|
@ -538,7 +538,10 @@ String _runSync(List<String> command, { bool lenient = true }) {
|
|||
}
|
||||
|
||||
String _runGit(String command) {
|
||||
return runSync(command.split(' '), workingDirectory: Cache.flutterRoot);
|
||||
return processUtils.runSync(
|
||||
command.split(' '),
|
||||
workingDirectory: Cache.flutterRoot,
|
||||
).stdout.trim();
|
||||
}
|
||||
|
||||
/// Runs [command] in the root of the Flutter installation and returns the
|
||||
|
|
|
@ -6,7 +6,7 @@ import '../base/context.dart';
|
|||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/platform.dart';
|
||||
import '../base/process_manager.dart';
|
||||
import '../base/process.dart';
|
||||
import '../convert.dart';
|
||||
|
||||
VisualStudio get visualStudio => context.get<VisualStudio>();
|
||||
|
@ -167,7 +167,7 @@ class VisualStudio {
|
|||
'-utf8',
|
||||
'-latest',
|
||||
];
|
||||
final ProcessResult whereResult = processManager.runSync(<String>[
|
||||
final RunResult whereResult = processUtils.runSync(<String>[
|
||||
_vswherePath,
|
||||
...defaultArguments,
|
||||
...?additionalArguments,
|
||||
|
|
|
@ -8,7 +8,7 @@ import '../application_package.dart';
|
|||
import '../base/io.dart';
|
||||
import '../base/os.dart';
|
||||
import '../base/platform.dart';
|
||||
import '../base/process_manager.dart';
|
||||
import '../base/process.dart';
|
||||
import '../build_info.dart';
|
||||
import '../desktop.dart';
|
||||
import '../device.dart';
|
||||
|
@ -88,7 +88,7 @@ class WindowsDevice extends Device {
|
|||
);
|
||||
}
|
||||
await stopApp(package);
|
||||
final Process process = await processManager.start(<String>[
|
||||
final Process process = await processUtils.start(<String>[
|
||||
package.executable(debuggingOptions?.buildInfo?.mode)
|
||||
]);
|
||||
if (debuggingOptions?.buildInfo?.isRelease == true) {
|
||||
|
@ -114,7 +114,9 @@ class WindowsDevice extends Device {
|
|||
if (process == null) {
|
||||
return false;
|
||||
}
|
||||
final ProcessResult result = await processManager.run(<String>['Taskkill', '/PID', process.first, '/F']);
|
||||
final RunResult result = await processUtils.run(
|
||||
<String>['Taskkill', '/PID', process.first, '/F'],
|
||||
);
|
||||
return result.exitCode == 0;
|
||||
}
|
||||
|
||||
|
@ -163,7 +165,9 @@ final RegExp _whitespace = RegExp(r'\s+');
|
|||
@visibleForTesting
|
||||
List<String> runningProcess(String processName) {
|
||||
// TODO(jonahwilliams): find a way to do this without powershell.
|
||||
final ProcessResult result = processManager.runSync(<String>['powershell', '-script="Get-CimInstance Win32_Process"']);
|
||||
final RunResult result = processUtils.runSync(
|
||||
<String>['powershell', '-script="Get-CimInstance Win32_Process"'],
|
||||
);
|
||||
if (result.exitCode != 0) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ void main() {
|
|||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
),
|
||||
).thenReturn(ProcessResult(0, 0, _aaptDataWithDefaultEnabledAndMainLauncherActivity, null));
|
||||
).thenReturn(ProcessResult(0, 0, _aaptDataWithDefaultEnabledAndMainLauncherActivity, ''));
|
||||
|
||||
final ApplicationPackage applicationPackage = await ApplicationPackageFactory.instance.getPackageForPlatform(
|
||||
TargetPlatform.android_arm,
|
||||
|
|
|
@ -26,10 +26,11 @@ void main() {
|
|||
mockProcessManager = PlainMockProcessManager();
|
||||
});
|
||||
|
||||
testUsingContext('runCheckedAsync exceptions should be ProcessException objects', () async {
|
||||
when(mockProcessManager.run(<String>['false']))
|
||||
.thenAnswer((Invocation invocation) => Future<ProcessResult>.value(ProcessResult(0, 1, '', '')));
|
||||
expect(() async => await runCheckedAsync(<String>['false']), throwsA(isInstanceOf<ProcessException>()));
|
||||
testUsingContext('runAsync throwOnError: exceptions should be ProcessException objects', () async {
|
||||
when(mockProcessManager.run(<String>['false'])).thenAnswer(
|
||||
(Invocation invocation) => Future<ProcessResult>.value(ProcessResult(0, 1, '', '')));
|
||||
expect(() async => await processUtils.run(<String>['false'], throwOnError: true),
|
||||
throwsA(isInstanceOf<ProcessException>()));
|
||||
}, overrides: <Type, Generator>{ProcessManager: () => mockProcessManager});
|
||||
});
|
||||
|
||||
|
@ -86,7 +87,7 @@ void main() {
|
|||
testUsingContext('Command output is not wrapped.', () async {
|
||||
final List<String> testString = <String>['0123456789' * 10];
|
||||
mockProcessManager.processFactory = processMetaFactory(testString, stderr: testString);
|
||||
await runCommandAndStreamOutput(<String>['command']);
|
||||
await processUtils.stream(<String>['command']);
|
||||
expect(mockLogger.statusText, equals('${testString[0]}\n'));
|
||||
expect(mockLogger.errorText, equals('${testString[0]}\n'));
|
||||
}, overrides: <Type, Generator>{
|
||||
|
@ -97,22 +98,82 @@ void main() {
|
|||
});
|
||||
});
|
||||
|
||||
group('runAsync timeout and retry', () {
|
||||
group('run', () {
|
||||
const Duration delay = Duration(seconds: 2);
|
||||
MockProcessManager flakyProcessManager;
|
||||
ProcessManager mockProcessManager;
|
||||
|
||||
setUp(() {
|
||||
// MockProcessManager has an implementation of start() that returns the
|
||||
// result of processFactory.
|
||||
flakyProcessManager = MockProcessManager();
|
||||
mockProcessManager = MockProcessManager();
|
||||
});
|
||||
|
||||
testUsingContext('flaky process fails without retry', () async {
|
||||
testUsingContext(' succeeds on success', () async {
|
||||
when(mockProcessManager.run(<String>['whoohoo'])).thenAnswer((_) {
|
||||
return Future<ProcessResult>.value(ProcessResult(0, 0, '', ''));
|
||||
});
|
||||
expect((await processUtils.run(<String>['whoohoo'])).exitCode, 0);
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => mockProcessManager,
|
||||
});
|
||||
|
||||
testUsingContext(' fails on failure', () async {
|
||||
when(mockProcessManager.run(<String>['boohoo'])).thenAnswer((_) {
|
||||
return Future<ProcessResult>.value(ProcessResult(0, 1, '', ''));
|
||||
});
|
||||
expect((await processUtils.run(<String>['boohoo'])).exitCode, 1);
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => mockProcessManager,
|
||||
});
|
||||
|
||||
testUsingContext(' throws on failure with throwOnError', () async {
|
||||
when(mockProcessManager.run(<String>['kaboom'])).thenAnswer((_) {
|
||||
return Future<ProcessResult>.value(ProcessResult(0, 1, '', ''));
|
||||
});
|
||||
expect(() => processUtils.run(<String>['kaboom'], throwOnError: true),
|
||||
throwsA(isA<ProcessException>()));
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => mockProcessManager,
|
||||
});
|
||||
|
||||
testUsingContext(' does not throw on failure with whitelist', () async {
|
||||
when(mockProcessManager.run(<String>['kaboom'])).thenAnswer((_) {
|
||||
return Future<ProcessResult>.value(ProcessResult(0, 1, '', ''));
|
||||
});
|
||||
expect(
|
||||
(await processUtils.run(
|
||||
<String>['kaboom'],
|
||||
throwOnError: true,
|
||||
whiteListFailures: (int c) => c == 1,
|
||||
)).exitCode,
|
||||
1);
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => mockProcessManager,
|
||||
});
|
||||
|
||||
testUsingContext(' throws on failure when not in whitelist', () async {
|
||||
when(mockProcessManager.run(<String>['kaboom'])).thenAnswer((_) {
|
||||
return Future<ProcessResult>.value(ProcessResult(0, 2, '', ''));
|
||||
});
|
||||
expect(
|
||||
() => processUtils.run(
|
||||
<String>['kaboom'],
|
||||
throwOnError: true,
|
||||
whiteListFailures: (int c) => c == 1,
|
||||
),
|
||||
throwsA(isA<ProcessException>()));
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => mockProcessManager,
|
||||
});
|
||||
|
||||
testUsingContext(' flaky process fails without retry', () async {
|
||||
flakyProcessManager.processFactory = flakyProcessFactory(
|
||||
flakes: 1,
|
||||
delay: delay,
|
||||
);
|
||||
final RunResult result = await runAsync(
|
||||
final RunResult result = await processUtils.run(
|
||||
<String>['dummy'],
|
||||
timeout: delay + const Duration(seconds: 1),
|
||||
);
|
||||
|
@ -121,12 +182,12 @@ void main() {
|
|||
ProcessManager: () => flakyProcessManager,
|
||||
});
|
||||
|
||||
testUsingContext('flaky process succeeds with retry', () async {
|
||||
testUsingContext(' flaky process succeeds with retry', () async {
|
||||
flakyProcessManager.processFactory = flakyProcessFactory(
|
||||
flakes: 1,
|
||||
delay: delay,
|
||||
);
|
||||
final RunResult result = await runAsync(
|
||||
final RunResult result = await processUtils.run(
|
||||
<String>['dummy'],
|
||||
timeout: delay - const Duration(milliseconds: 500),
|
||||
timeoutRetries: 1,
|
||||
|
@ -136,7 +197,7 @@ void main() {
|
|||
ProcessManager: () => flakyProcessManager,
|
||||
});
|
||||
|
||||
testUsingContext('flaky process generates ProcessException on timeout', () async {
|
||||
testUsingContext(' flaky process generates ProcessException on timeout', () async {
|
||||
final Completer<List<int>> flakyStderr = Completer<List<int>>();
|
||||
final Completer<List<int>> flakyStdout = Completer<List<int>>();
|
||||
flakyProcessManager.processFactory = flakyProcessFactory(
|
||||
|
@ -153,7 +214,7 @@ void main() {
|
|||
flakyStdout.complete(<int>[]);
|
||||
return true;
|
||||
});
|
||||
expect(() async => await runAsync(
|
||||
expect(() => processUtils.run(
|
||||
<String>['dummy'],
|
||||
timeout: delay - const Duration(milliseconds: 500),
|
||||
timeoutRetries: 0,
|
||||
|
@ -162,6 +223,159 @@ void main() {
|
|||
ProcessManager: () => flakyProcessManager,
|
||||
});
|
||||
});
|
||||
|
||||
group('runSync', () {
|
||||
ProcessManager mockProcessManager;
|
||||
|
||||
setUp(() {
|
||||
mockProcessManager = MockProcessManager();
|
||||
});
|
||||
|
||||
testUsingContext(' succeeds on success', () async {
|
||||
when(mockProcessManager.runSync(<String>['whoohoo'])).thenReturn(
|
||||
ProcessResult(0, 0, '', '')
|
||||
);
|
||||
expect(processUtils.runSync(<String>['whoohoo']).exitCode, 0);
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => mockProcessManager,
|
||||
});
|
||||
|
||||
testUsingContext(' fails on failure', () async {
|
||||
when(mockProcessManager.runSync(<String>['boohoo'])).thenReturn(
|
||||
ProcessResult(0, 1, '', '')
|
||||
);
|
||||
expect(processUtils.runSync(<String>['boohoo']).exitCode, 1);
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => mockProcessManager,
|
||||
});
|
||||
|
||||
testUsingContext(' throws on failure with throwOnError', () async {
|
||||
when(mockProcessManager.runSync(<String>['kaboom'])).thenReturn(
|
||||
ProcessResult(0, 1, '', '')
|
||||
);
|
||||
expect(() => processUtils.runSync(<String>['kaboom'], throwOnError: true),
|
||||
throwsA(isA<ProcessException>()));
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => mockProcessManager,
|
||||
});
|
||||
|
||||
testUsingContext(' does not throw on failure with whitelist', () async {
|
||||
when(mockProcessManager.runSync(<String>['kaboom'])).thenReturn(
|
||||
ProcessResult(0, 1, '', '')
|
||||
);
|
||||
expect(
|
||||
processUtils.runSync(
|
||||
<String>['kaboom'],
|
||||
throwOnError: true,
|
||||
whiteListFailures: (int c) => c == 1,
|
||||
).exitCode,
|
||||
1);
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => mockProcessManager,
|
||||
});
|
||||
|
||||
testUsingContext(' throws on failure when not in whitelist', () async {
|
||||
when(mockProcessManager.runSync(<String>['kaboom'])).thenReturn(
|
||||
ProcessResult(0, 2, '', '')
|
||||
);
|
||||
expect(
|
||||
() => processUtils.runSync(
|
||||
<String>['kaboom'],
|
||||
throwOnError: true,
|
||||
whiteListFailures: (int c) => c == 1,
|
||||
),
|
||||
throwsA(isA<ProcessException>()));
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => mockProcessManager,
|
||||
});
|
||||
|
||||
testUsingContext(' prints stdout and stderr to trace on success', () async {
|
||||
when(mockProcessManager.runSync(<String>['whoohoo'])).thenReturn(
|
||||
ProcessResult(0, 0, 'stdout', 'stderr')
|
||||
);
|
||||
expect(processUtils.runSync(<String>['whoohoo']).exitCode, 0);
|
||||
expect(testLogger.traceText, contains('stdout'));
|
||||
expect(testLogger.traceText, contains('stderr'));
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => mockProcessManager,
|
||||
});
|
||||
|
||||
testUsingContext(' prints stdout to status and stderr to error on failure with throwOnError', () async {
|
||||
when(mockProcessManager.runSync(<String>['kaboom'])).thenReturn(
|
||||
ProcessResult(0, 1, 'stdout', 'stderr')
|
||||
);
|
||||
expect(() => processUtils.runSync(<String>['kaboom'], throwOnError: true),
|
||||
throwsA(isA<ProcessException>()));
|
||||
expect(testLogger.statusText, contains('stdout'));
|
||||
expect(testLogger.errorText, contains('stderr'));
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => mockProcessManager,
|
||||
});
|
||||
|
||||
testUsingContext(' does not print stdout with hideStdout', () async {
|
||||
when(mockProcessManager.runSync(<String>['whoohoo'])).thenReturn(
|
||||
ProcessResult(0, 0, 'stdout', 'stderr')
|
||||
);
|
||||
expect(processUtils.runSync(<String>['whoohoo'], hideStdout: true).exitCode, 0);
|
||||
expect(testLogger.traceText.contains('stdout'), isFalse);
|
||||
expect(testLogger.traceText, contains('stderr'));
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => mockProcessManager,
|
||||
});
|
||||
});
|
||||
|
||||
group('exitsHappySync', () {
|
||||
ProcessManager mockProcessManager;
|
||||
|
||||
setUp(() {
|
||||
mockProcessManager = MockProcessManager();
|
||||
});
|
||||
|
||||
testUsingContext(' succeeds on success', () async {
|
||||
when(mockProcessManager.runSync(<String>['whoohoo'])).thenReturn(
|
||||
ProcessResult(0, 0, '', '')
|
||||
);
|
||||
expect(processUtils.exitsHappySync(<String>['whoohoo']), isTrue);
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => mockProcessManager,
|
||||
});
|
||||
|
||||
testUsingContext(' fails on failure', () async {
|
||||
when(mockProcessManager.runSync(<String>['boohoo'])).thenReturn(
|
||||
ProcessResult(0, 1, '', '')
|
||||
);
|
||||
expect(processUtils.exitsHappySync(<String>['boohoo']), isFalse);
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => mockProcessManager,
|
||||
});
|
||||
});
|
||||
|
||||
group('exitsHappy', () {
|
||||
ProcessManager mockProcessManager;
|
||||
|
||||
setUp(() {
|
||||
mockProcessManager = MockProcessManager();
|
||||
});
|
||||
|
||||
testUsingContext(' succeeds on success', () async {
|
||||
when(mockProcessManager.run(<String>['whoohoo'])).thenAnswer((_) {
|
||||
return Future<ProcessResult>.value(ProcessResult(0, 0, '', ''));
|
||||
});
|
||||
expect(await processUtils.exitsHappy(<String>['whoohoo']), isTrue);
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => mockProcessManager,
|
||||
});
|
||||
|
||||
testUsingContext(' fails on failure', () async {
|
||||
when(mockProcessManager.run(<String>['boohoo'])).thenAnswer((_) {
|
||||
return Future<ProcessResult>.value(ProcessResult(0, 1, '', ''));
|
||||
});
|
||||
expect(await processUtils.exitsHappy(<String>['boohoo']), isFalse);
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => mockProcessManager,
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
class PlainMockProcessManager extends Mock implements ProcessManager {}
|
||||
|
|
|
@ -59,8 +59,8 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('No auto-sign if security or openssl not available', () async {
|
||||
when(mockProcessManager.runSync(<String>['which', 'security']))
|
||||
.thenReturn(exitsFail);
|
||||
when(mockProcessManager.run(<String>['which', 'security']))
|
||||
.thenAnswer((_) => Future<ProcessResult>.value(exitsFail));
|
||||
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
|
||||
expect(signingConfigs, isNull);
|
||||
},
|
||||
|
@ -69,15 +69,21 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('No valid code signing certificates shows instructions', () async {
|
||||
when(mockProcessManager.runSync(<String>['which', 'security']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(<String>['which', 'openssl']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(
|
||||
argThat(contains('find-identity')),
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'security'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'openssl'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
argThat(contains('find-identity')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenReturn(exitsHappy);
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
|
||||
|
||||
Map<String, String> signingConfigs;
|
||||
|
@ -95,39 +101,45 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('Test single identity and certificate organization works', () async {
|
||||
when(mockProcessManager.runSync(<String>['which', 'security']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(<String>['which', 'openssl']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(
|
||||
argThat(contains('find-identity')),
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'security'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'openssl'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
argThat(contains('find-identity')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenReturn(ProcessResult(
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(
|
||||
1, // pid
|
||||
0, // exitCode
|
||||
'''
|
||||
1) 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 "iPhone Developer: Profile 1 (1111AAAA11)"
|
||||
1 valid identities found''',
|
||||
'',
|
||||
));
|
||||
when(mockProcessManager.runSync(
|
||||
)));
|
||||
when(mockProcessManager.run(
|
||||
<String>['security', 'find-certificate', '-c', '1111AAAA11', '-p'],
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenReturn(ProcessResult(
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(
|
||||
1, // pid
|
||||
0, // exitCode
|
||||
'This is a mock certificate',
|
||||
'',
|
||||
));
|
||||
)));
|
||||
|
||||
final MockProcess mockProcess = MockProcess();
|
||||
final MockStdIn mockStdIn = MockStdIn();
|
||||
final MockStream mockStdErr = MockStream();
|
||||
|
||||
when(mockProcessManager.start(
|
||||
argThat(contains('openssl')),
|
||||
argThat(contains('openssl')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenAnswer((Invocation invocation) => Future<Process>.value(mockProcess));
|
||||
|
@ -156,39 +168,45 @@ void main() {
|
|||
|
||||
|
||||
testUsingContext('Test single identity (Catalina format) and certificate organization works', () async {
|
||||
when(mockProcessManager.runSync(<String>['which', 'security']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(<String>['which', 'openssl']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(
|
||||
argThat(contains('find-identity')),
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'security'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'openssl'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
argThat(contains('find-identity')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenReturn(ProcessResult(
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(
|
||||
1, // pid
|
||||
0, // exitCode
|
||||
'''
|
||||
1) 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 "Apple Development: Profile 1 (1111AAAA11)"
|
||||
1 valid identities found''',
|
||||
'',
|
||||
));
|
||||
when(mockProcessManager.runSync(
|
||||
)));
|
||||
when(mockProcessManager.run(
|
||||
<String>['security', 'find-certificate', '-c', '1111AAAA11', '-p'],
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenReturn(ProcessResult(
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(
|
||||
1, // pid
|
||||
0, // exitCode
|
||||
'This is a mock certificate',
|
||||
'',
|
||||
));
|
||||
)));
|
||||
|
||||
final MockProcess mockProcess = MockProcess();
|
||||
final MockStdIn mockStdIn = MockStdIn();
|
||||
final MockStream mockStdErr = MockStream();
|
||||
|
||||
when(mockProcessManager.start(
|
||||
argThat(contains('openssl')),
|
||||
argThat(contains('openssl')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenAnswer((Invocation invocation) => Future<Process>.value(mockProcess));
|
||||
|
@ -222,15 +240,21 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('Test multiple identity and certificate organization works', () async {
|
||||
when(mockProcessManager.runSync(<String>['which', 'security']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(<String>['which', 'openssl']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(
|
||||
argThat(contains('find-identity')),
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'security'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'openssl'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
argThat(contains('find-identity')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenReturn(ProcessResult(
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(
|
||||
1, // pid
|
||||
0, // exitCode
|
||||
'''
|
||||
|
@ -239,26 +263,26 @@ void main() {
|
|||
3) 5bf1fd927dfb8679496a2e6cf00cbe50c1c87145 "iPhone Developer: Profile 3 (3333CCCC33)"
|
||||
3 valid identities found''',
|
||||
'',
|
||||
));
|
||||
)));
|
||||
mockTerminalStdInStream =
|
||||
Stream<String>.fromFuture(Future<String>.value('3'));
|
||||
when(mockProcessManager.runSync(
|
||||
when(mockProcessManager.run(
|
||||
<String>['security', 'find-certificate', '-c', '3333CCCC33', '-p'],
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenReturn(ProcessResult(
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(
|
||||
1, // pid
|
||||
0, // exitCode
|
||||
'This is a mock certificate',
|
||||
'',
|
||||
));
|
||||
)));
|
||||
|
||||
final MockProcess mockOpenSslProcess = MockProcess();
|
||||
final MockStdIn mockOpenSslStdIn = MockStdIn();
|
||||
final MockStream mockOpenSslStdErr = MockStream();
|
||||
|
||||
when(mockProcessManager.start(
|
||||
argThat(contains('openssl')),
|
||||
argThat(contains('openssl')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenAnswer((Invocation invocation) => Future<Process>.value(mockOpenSslProcess));
|
||||
|
@ -298,15 +322,21 @@ void main() {
|
|||
|
||||
testUsingContext('Test multiple identity in machine mode works', () async {
|
||||
testTerminal.usesTerminalUi = false;
|
||||
when(mockProcessManager.runSync(<String>['which', 'security']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(<String>['which', 'openssl']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(
|
||||
argThat(contains('find-identity')),
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'security'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'openssl'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
argThat(contains('find-identity')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenReturn(ProcessResult(
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(
|
||||
1, // pid
|
||||
0, // exitCode
|
||||
'''
|
||||
|
@ -315,26 +345,26 @@ void main() {
|
|||
3) 5bf1fd927dfb8679496a2e6cf00cbe50c1c87145 "iPhone Developer: Profile 3 (3333CCCC33)"
|
||||
3 valid identities found''',
|
||||
'',
|
||||
));
|
||||
)));
|
||||
mockTerminalStdInStream =
|
||||
Stream<String>.fromFuture(Future<String>.error(Exception('Cannot read from StdIn')));
|
||||
when(mockProcessManager.runSync(
|
||||
when(mockProcessManager.run(
|
||||
<String>['security', 'find-certificate', '-c', '1111AAAA11', '-p'],
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenReturn(ProcessResult(
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(
|
||||
1, // pid
|
||||
0, // exitCode
|
||||
'This is a mock certificate',
|
||||
'',
|
||||
));
|
||||
)));
|
||||
|
||||
final MockProcess mockOpenSslProcess = MockProcess();
|
||||
final MockStdIn mockOpenSslStdIn = MockStdIn();
|
||||
final MockStream mockOpenSslStdErr = MockStream();
|
||||
|
||||
when(mockProcessManager.start(
|
||||
argThat(contains('openssl')),
|
||||
argThat(contains('openssl')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenAnswer((Invocation invocation) => Future<Process>.value(mockOpenSslProcess));
|
||||
|
@ -367,15 +397,21 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('Test saved certificate used', () async {
|
||||
when(mockProcessManager.runSync(<String>['which', 'security']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(<String>['which', 'openssl']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(
|
||||
argThat(contains('find-identity')),
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'security'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'openssl'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
argThat(contains('find-identity')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenReturn(ProcessResult(
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(
|
||||
1, // pid
|
||||
0, // exitCode
|
||||
'''
|
||||
|
@ -384,24 +420,24 @@ void main() {
|
|||
3) 5bf1fd927dfb8679496a2e6cf00cbe50c1c87145 "iPhone Developer: Profile 3 (3333CCCC33)"
|
||||
3 valid identities found''',
|
||||
'',
|
||||
));
|
||||
when(mockProcessManager.runSync(
|
||||
)));
|
||||
when(mockProcessManager.run(
|
||||
<String>['security', 'find-certificate', '-c', '3333CCCC33', '-p'],
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenReturn(ProcessResult(
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(
|
||||
1, // pid
|
||||
0, // exitCode
|
||||
'This is a mock certificate',
|
||||
'',
|
||||
));
|
||||
)));
|
||||
|
||||
final MockProcess mockOpenSslProcess = MockProcess();
|
||||
final MockStdIn mockOpenSslStdIn = MockStdIn();
|
||||
final MockStream mockOpenSslStdErr = MockStream();
|
||||
|
||||
when(mockProcessManager.start(
|
||||
argThat(contains('openssl')),
|
||||
argThat(contains('openssl')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenAnswer((Invocation invocation) => Future<Process>.value(mockOpenSslProcess));
|
||||
|
@ -438,15 +474,21 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('Test invalid saved certificate shows error and prompts again', () async {
|
||||
when(mockProcessManager.runSync(<String>['which', 'security']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(<String>['which', 'openssl']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(
|
||||
argThat(contains('find-identity')),
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'security'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'openssl'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
argThat(contains('find-identity')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenReturn(ProcessResult(
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(
|
||||
1, // pid
|
||||
0, // exitCode
|
||||
'''
|
||||
|
@ -455,19 +497,19 @@ void main() {
|
|||
3) 5bf1fd927dfb8679496a2e6cf00cbe50c1c87145 "iPhone Developer: Profile 3 (3333CCCC33)"
|
||||
3 valid identities found''',
|
||||
'',
|
||||
));
|
||||
)));
|
||||
mockTerminalStdInStream =
|
||||
Stream<String>.fromFuture(Future<String>.value('3'));
|
||||
when(mockProcessManager.runSync(
|
||||
when(mockProcessManager.run(
|
||||
<String>['security', 'find-certificate', '-c', '3333CCCC33', '-p'],
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenReturn(ProcessResult(
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(
|
||||
1, // pid
|
||||
0, // exitCode
|
||||
'This is a mock certificate',
|
||||
'',
|
||||
));
|
||||
)));
|
||||
|
||||
|
||||
final MockProcess mockOpenSslProcess = MockProcess();
|
||||
|
@ -475,7 +517,7 @@ void main() {
|
|||
final MockStream mockOpenSslStdErr = MockStream();
|
||||
|
||||
when(mockProcessManager.start(
|
||||
argThat(contains('openssl')),
|
||||
argThat(contains('openssl')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenAnswer((Invocation invocation) => Future<Process>.value(mockOpenSslProcess));
|
||||
|
@ -511,15 +553,23 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('find-identity failure', () async {
|
||||
when(mockProcessManager.runSync(<String>['which', 'security']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(<String>['which', 'openssl']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(
|
||||
argThat(contains('find-identity')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenReturn(ProcessResult(0, 1, '', ''));
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'security'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'openssl'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
argThat(contains('find-identity')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(
|
||||
ProcessResult(0, 1, '', '')
|
||||
));
|
||||
|
||||
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
|
||||
expect(signingConfigs, isNull);
|
||||
|
@ -531,15 +581,21 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('find-certificate failure', () async {
|
||||
when(mockProcessManager.runSync(<String>['which', 'security']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(<String>['which', 'openssl']))
|
||||
.thenReturn(exitsHappy);
|
||||
when(mockProcessManager.runSync(
|
||||
argThat(contains('find-identity')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenReturn(ProcessResult(
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'security'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
<String>['which', 'openssl'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
|
||||
when(mockProcessManager.run(
|
||||
argThat(contains('find-identity')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(
|
||||
1, // pid
|
||||
0, // exitCode
|
||||
'''
|
||||
|
@ -548,14 +604,16 @@ void main() {
|
|||
3) 5bf1fd927dfb8679496a2e6cf00cbe50c1c87145 "iPhone Developer: Profile 3 (3333CCCC33)"
|
||||
3 valid identities found''',
|
||||
'',
|
||||
));
|
||||
)));
|
||||
mockTerminalStdInStream =
|
||||
Stream<String>.fromFuture(Future<String>.value('3'));
|
||||
when(mockProcessManager.runSync(
|
||||
when(mockProcessManager.run(
|
||||
<String>['security', 'find-certificate', '-c', '3333CCCC33', '-p'],
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenReturn(ProcessResult(1, 1, '', '' ));
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(
|
||||
ProcessResult(1, 1, '', '' ))
|
||||
);
|
||||
|
||||
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
|
||||
expect(signingConfigs, isNull);
|
||||
|
|
|
@ -142,10 +142,13 @@ void main() {
|
|||
final MockDirectory directory = MockDirectory();
|
||||
when(mockFileSystem.directory(bundlePath)).thenReturn(directory);
|
||||
when(directory.existsSync()).thenReturn(true);
|
||||
when(mockProcessManager.run(installArgs, environment: env))
|
||||
.thenAnswer(
|
||||
(_) => Future<ProcessResult>.value(ProcessResult(1, 0, '', ''))
|
||||
);
|
||||
when(mockProcessManager.run(
|
||||
installArgs,
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: env
|
||||
)).thenAnswer(
|
||||
(_) => Future<ProcessResult>.value(ProcessResult(1, 0, '', ''))
|
||||
);
|
||||
|
||||
when(mockIMobileDevice.getInfoForDevice(any, 'CPUArchitecture'))
|
||||
.thenAnswer((_) => Future<String>.value('arm64'));
|
||||
|
@ -259,6 +262,21 @@ void main() {
|
|||
return Future<ProcessResult>.value(ProcessResult(0, 0, '', ''));
|
||||
});
|
||||
|
||||
when(mockProcessManager.run(
|
||||
argThat(contains('find-identity')),
|
||||
environment: anyNamed('environment'),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
)).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(
|
||||
1, // pid
|
||||
0, // exitCode
|
||||
'''
|
||||
1) 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 "iPhone Developer: Profile 1 (1111AAAA11)"
|
||||
2) da4b9237bacccdf19c0760cab7aec4a8359010b0 "iPhone Developer: Profile 2 (2222BBBB22)"
|
||||
3) 5bf1fd927dfb8679496a2e6cf00cbe50c1c87145 "iPhone Developer: Profile 3 (3333CCCC33)"
|
||||
3 valid identities found''',
|
||||
'',
|
||||
)));
|
||||
|
||||
// Deploy works.
|
||||
when(mockIosDeploy.runApp(
|
||||
deviceId: anyNamed('deviceId'),
|
||||
|
|
|
@ -146,7 +146,10 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingOsxContext('build settings is empty when xcodebuild failed to get the build settings', () {
|
||||
when(mockProcessManager.runSync(argThat(contains(xcodebuild))))
|
||||
when(mockProcessManager.runSync(
|
||||
argThat(contains(xcodebuild)),
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment')))
|
||||
.thenReturn(ProcessResult(0, 1, '', ''));
|
||||
expect(xcodeProjectInterpreter.getBuildSettings('', ''), const <String, String>{});
|
||||
});
|
||||
|
|
|
@ -69,18 +69,23 @@ void main() {
|
|||
final String finalResponse =
|
||||
json.encode(<Map<String, dynamic>>[response]);
|
||||
when<String>(result.stdout).thenReturn(finalResponse);
|
||||
when<String>(result.stderr).thenReturn('');
|
||||
final List<String> requirementArguments = requiredComponents == null
|
||||
? <String>[]
|
||||
: <String>['-requires', ...requiredComponents];
|
||||
when(mockProcessManager.runSync(<String>[
|
||||
vswherePath,
|
||||
'-format',
|
||||
'json',
|
||||
'-utf8',
|
||||
'-latest',
|
||||
...?additionalArguments,
|
||||
...?requirementArguments,
|
||||
])).thenAnswer((Invocation invocation) {
|
||||
when(mockProcessManager.runSync(
|
||||
<String>[
|
||||
vswherePath,
|
||||
'-format',
|
||||
'json',
|
||||
'-utf8',
|
||||
'-latest',
|
||||
...?additionalArguments,
|
||||
...?requirementArguments,
|
||||
],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((Invocation invocation) {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
@ -111,8 +116,11 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('isInstalled returns false when vswhere is missing', () {
|
||||
when(mockProcessManager.runSync(any))
|
||||
.thenThrow(const ProcessException('vswhere', <String>[]));
|
||||
when(mockProcessManager.runSync(
|
||||
any,
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenThrow(const ProcessException('vswhere', <String>[]));
|
||||
|
||||
visualStudio = VisualStudio();
|
||||
expect(visualStudio.isInstalled, false);
|
||||
|
@ -123,8 +131,11 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('vcvarsPath returns null when vswhere is missing', () {
|
||||
when(mockProcessManager.runSync(any))
|
||||
.thenThrow(const ProcessException('vswhere', <String>[]));
|
||||
when(mockProcessManager.runSync(
|
||||
any,
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenThrow(const ProcessException('vswhere', <String>[]));
|
||||
|
||||
visualStudio = VisualStudio();
|
||||
expect(visualStudio.vcvarsPath, isNull);
|
||||
|
@ -135,13 +146,24 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('isInstalled returns false when vswhere returns non-zero', () {
|
||||
when(mockProcessManager.runSync(any))
|
||||
.thenThrow(const ProcessException('vswhere', <String>[]));
|
||||
|
||||
when(mockProcessManager.runSync(
|
||||
any,
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenThrow(const ProcessException('vswhere', <String>[]));
|
||||
|
||||
final MockProcessResult result = MockProcessResult();
|
||||
when(result.exitCode).thenReturn(1);
|
||||
when(mockProcessManager.runSync(any)).thenAnswer((Invocation invocation) {
|
||||
when(mockProcessManager.runSync(
|
||||
any,
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((Invocation invocation) {
|
||||
return result;
|
||||
});
|
||||
when<String>(result.stdout).thenReturn('');
|
||||
when<String>(result.stderr).thenReturn('');
|
||||
|
||||
visualStudio = VisualStudio();
|
||||
expect(visualStudio.isInstalled, false);
|
||||
|
|
|
@ -26,18 +26,23 @@ void main() {
|
|||
|
||||
when(notWindows.isWindows).thenReturn(false);
|
||||
when(notWindows.environment).thenReturn(const <String, String>{});
|
||||
when(mockProcessManager.runSync(<String>[
|
||||
'powershell', '-script="Get-CimInstance Win32_Process"'
|
||||
])).thenAnswer((Invocation invocation) {
|
||||
when(mockProcessManager.runSync(
|
||||
<String>['powershell', '-script="Get-CimInstance Win32_Process"'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((Invocation invocation) {
|
||||
// The flutter tool process is returned as output to the powershell script
|
||||
final MockProcessResult result = MockProcessResult();
|
||||
when(result.exitCode).thenReturn(0);
|
||||
when<String>(result.stdout).thenReturn('$pid $flutterToolBinary');
|
||||
when<String>(result.stderr).thenReturn('');
|
||||
return result;
|
||||
});
|
||||
when(mockProcessManager.run(<String>[
|
||||
'Taskkill', '/PID', '$pid', '/F'
|
||||
])).thenThrow(Exception('Flutter tool process has been killed'));
|
||||
when(mockProcessManager.run(
|
||||
<String>['Taskkill', '/PID', '$pid', '/F'],
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenThrow(Exception('Flutter tool process has been killed'));
|
||||
|
||||
testUsingContext('defaults', () async {
|
||||
final PrebuiltWindowsApp windowsApp = PrebuiltWindowsApp(executable: 'foo');
|
||||
|
|
Loading…
Reference in a new issue