[flutter_tool] process.dart cleanup (#39899)

This commit is contained in:
Zachary Anderson 2019-09-11 18:20:42 -07:00 committed by GitHub
parent 3712ea63d8
commit 73c10e8ced
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 1277 additions and 765 deletions

View file

@ -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,
);
}
}

View file

@ -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) {

View file

@ -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;

View file

@ -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];

View file

@ -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,
);

View file

@ -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,

View file

@ -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;
}

View file

@ -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,
);
}
}

View file

@ -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()}';
}

View file

@ -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');
}
}
}

View file

@ -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();

View file

@ -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: ',

View file

@ -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,

View file

@ -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;
}

View file

@ -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(

View file

@ -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',
],

View file

@ -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',

View file

@ -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(),

View file

@ -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.

View file

@ -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;
}

View file

@ -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

View file

@ -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;

View file

@ -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,

View file

@ -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,

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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(),

View file

@ -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;

View file

@ -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,
);
}
}

View file

@ -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>{};
}

View file

@ -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;

View file

@ -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());
}
}

View file

@ -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;

View file

@ -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}');

View file

@ -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,

View file

@ -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

View file

@ -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,

View file

@ -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;
}

View file

@ -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,

View file

@ -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 {}

View file

@ -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);

View file

@ -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'),

View file

@ -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>{});
});

View file

@ -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);

View file

@ -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');