| // Copyright 2024, the Dart project authors. Please see the AUTHORS file |
| // for details. 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:async"; |
| import 'dart:io'; |
| |
| typedef ProcessRunner = Future<ProcessResult> |
| Function(String executable, List<String> arguments, {bool runInShell}); |
| |
| /// Wraps a function in a [Zone] with an alternative [ProcessRunner]. |
| /// |
| /// All [Command.run] calls in the zone will use the alternative runner. |
| /// |
| /// Returns the return value of the function [body]. |
| R overrideRun<R>(ProcessRunner run, R Function() body) { |
| return runZoned(body, zoneValues: {#_run: run}); |
| } |
| |
| /// A command to run an [executable] with the given [arguments]. |
| class Command { |
| final String executable; |
| final List<String> arguments; |
| final bool runInShell; |
| |
| const Command(this.executable, this.arguments, {this.runInShell = false}); |
| |
| /// Runs an [executable] with the given [arguments]. |
| /// |
| /// If running in a zone created by [overrideRun], it will use an alternative |
| /// [ProcessRunner]. Otherwise, it defaults to [Process.run]. |
| Future<ProcessResult> run({bool raise = false}) async { |
| final result = await (Zone.current[#_run] as ProcessRunner? ?? |
| Process.run)(executable, arguments, runInShell: runInShell); |
| if (raise && result.exitCode != 0) { |
| throw ProcessException( |
| executable, |
| arguments, |
| 'Command exited ${result.exitCode}: ' |
| '$executable ${arguments.join(' ')}\n' |
| '${result.stdout}\n${result.stderr}', |
| result.exitCode); |
| } |
| return result; |
| } |
| } |