| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:convert'; |
| import 'dart:io' as io; |
| |
| import 'package:file/file.dart'; |
| |
| import 'core.dart'; |
| |
| /// A class used to run processes. |
| /// |
| /// We use this instead of directly running the process so it can be overridden |
| /// in tests. |
| class ProcessRunner { |
| /// Creates a new process runner. |
| const ProcessRunner(); |
| |
| /// Run the [executable] with [args] and stream output to stderr and stdout. |
| /// |
| /// The current working directory of [executable] can be overridden by |
| /// passing [workingDir]. |
| /// |
| /// If [exitOnError] is set to `true`, then this will throw an error if |
| /// the [executable] terminates with a non-zero exit code. |
| /// |
| /// Returns the exit code of the [executable]. |
| Future<int> runAndStream( |
| String executable, |
| List<String> args, { |
| Directory? workingDir, |
| Map<String, String>? environment, |
| bool exitOnError = false, |
| }) async { |
| print( |
| 'Running command: "$executable ${args.join(' ')}" in ${workingDir?.path ?? io.Directory.current.path}'); |
| final io.Process process = await io.Process.start(executable, args, |
| workingDirectory: workingDir?.path, environment: environment); |
| await Future.wait(<Future<dynamic>>[ |
| io.stdout.addStream(process.stdout), |
| io.stderr.addStream(process.stderr), |
| ]); |
| if (exitOnError && await process.exitCode != 0) { |
| final String error = |
| _getErrorString(executable, args, workingDir: workingDir); |
| print('$error See above for details.'); |
| throw ToolExit(await process.exitCode); |
| } |
| return process.exitCode; |
| } |
| |
| /// Run the [executable] with [args]. |
| /// |
| /// The current working directory of [executable] can be overridden by |
| /// passing [workingDir]. |
| /// |
| /// If [exitOnError] is set to `true`, then this will throw an error if |
| /// the [executable] terminates with a non-zero exit code. |
| /// Defaults to `false`. |
| /// |
| /// If [logOnError] is set to `true`, it will print a formatted message about the error. |
| /// Defaults to `false` |
| /// |
| /// Returns the [io.ProcessResult] of the [executable]. |
| Future<io.ProcessResult> run( |
| String executable, |
| List<String> args, { |
| Directory? workingDir, |
| Map<String, String>? environment, |
| bool exitOnError = false, |
| bool logOnError = false, |
| Encoding stdoutEncoding = io.systemEncoding, |
| Encoding stderrEncoding = io.systemEncoding, |
| }) async { |
| final io.ProcessResult result = await io.Process.run(executable, args, |
| workingDirectory: workingDir?.path, |
| environment: environment, |
| stdoutEncoding: stdoutEncoding, |
| stderrEncoding: stderrEncoding); |
| if (result.exitCode != 0) { |
| if (logOnError) { |
| final String error = |
| _getErrorString(executable, args, workingDir: workingDir); |
| print('$error Stderr:\n${result.stdout}'); |
| } |
| if (exitOnError) { |
| throw ToolExit(result.exitCode); |
| } |
| } |
| return result; |
| } |
| |
| /// Starts the [executable] with [args]. |
| /// |
| /// The current working directory of [executable] can be overridden by |
| /// passing [workingDir]. |
| /// |
| /// Returns the started [io.Process]. |
| Future<io.Process> start(String executable, List<String> args, |
| {Directory? workingDirectory}) async { |
| final io.Process process = await io.Process.start(executable, args, |
| workingDirectory: workingDirectory?.path); |
| return process; |
| } |
| |
| String _getErrorString(String executable, List<String> args, |
| {Directory? workingDir}) { |
| final String workdir = workingDir == null ? '' : ' in ${workingDir.path}'; |
| return 'ERROR: Unable to execute "$executable ${args.join(' ')}"$workdir.'; |
| } |
| } |