process_runner 3.0.0
process_runner: ^3.0.0 copied to clipboard
A process invocation astraction for Dart that manages a multiprocess queue.
example/main.dart
// Copyright 2020 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.
// This example shows how to send a bunch of jobs to ProcessPool for processing.
//
// This example program is actually pretty useful even if you don't use
// process_runner for your Dart project. It can speed up processing of a bunch
// of single-threaded CPU-intensive commands by a multple of the number of
// processor cores you have (modulo being disk/network bound, of course).
import 'dart:io';
import 'package:args/args.dart';
import 'package:process_runner/process_runner.dart';
// This only works for escaped spaces and things in double or single quotes.
// This is just an example, modify to meet your own requirements.
List<String> splitIntoArgs(String args) {
bool inQuote = false;
bool inEscape = false;
String quoteMatch = '';
final List<String> result = <String>[];
final List<String> currentArg = <String>[];
for (int i = 0; i < args.length; ++i) {
final String char = args[i];
if (inEscape) {
switch (char) {
case 'n':
currentArg.add('\n');
break;
case 't':
currentArg.add('\t');
break;
case 'r':
currentArg.add('\r');
break;
case 'b':
currentArg.add('\b');
break;
default:
currentArg.add(char);
break;
}
inEscape = false;
continue;
}
if (char == ' ' && !inQuote) {
result.add(currentArg.join(''));
currentArg.clear();
continue;
}
if (char == r'\') {
inEscape = true;
continue;
}
if (inQuote) {
if (char == quoteMatch) {
inQuote = false;
quoteMatch = '';
} else {
currentArg.add(char);
}
continue;
}
if (char == '"' || char == '"') {
inQuote = !inQuote;
quoteMatch = args[i];
continue;
}
currentArg.add(char);
}
if (currentArg.isNotEmpty) {
result.add(currentArg.join(''));
}
return result;
}
Future<void> main(List<String> args) async {
final ArgParser parser = ArgParser();
parser.addFlag('help', help: 'Print help.');
parser.addFlag('report', help: 'Print progress on the jobs while running.', defaultsTo: false);
parser.addOption('workers',
abbr: 'w',
help: 'Specify the number of workers jobs to run simultanously. Defaults '
'to the number of processors on the machine.');
parser.addOption('workingDirectory',
abbr: 'd', help: 'Specify the working directory to run on', defaultsTo: '.');
parser.addMultiOption('cmd',
abbr: 'c',
help: 'Specify a command to add to the commands to be run. Entire '
'command must be quoted by the shell. Commands specified with this '
'option run before those specified with --cmdFile');
parser.addOption('file',
abbr: 'f',
help: 'Specify the name of a file to read commands from, one per line, as '
'they would appear on the command line, with spaces escaped or '
'quoted. Specify "-" to read from stdin.',
defaultsTo: '-');
final ArgResults options = parser.parse(args);
if (options['help'] as bool) {
print('main.dart [flags]');
print(parser.usage);
exit(0);
}
// Collect the commands to be run from the command file.
final String commandFile = options['file'] as String;
List<String> fileCommands = <String>[];
if (commandFile != null) {
// Read from stdin if the --file option is set to '-'.
if (commandFile == '-') {
String line = stdin.readLineSync();
while (line != null) {
fileCommands.add(line);
line = stdin.readLineSync();
}
} else {
// Read the commands from a file.
final File cmdFile = File(commandFile);
if (!cmdFile.existsSync()) {
print('Command file "$commandFile" doesn\'t exist.');
exit(1);
}
fileCommands = cmdFile.readAsLinesSync();
}
}
// Collect all the commands, both from the input file, and from the command
// line. The command line commands come first (although they could all be
// executed simultaneously, depending on the number of workers, and number of
// commands).
final List<String> commands = <String>[
...options['cmd'] as List<String>,
...fileCommands,
];
// Split each command entry into a list of strings, taking into account some
// simple quoting and escaping.
final List<List<String>> splitCommands = commands.map<List<String>>(splitIntoArgs).toList();
// If the numWorkers is set to null, then the ProcessPool will automatically
// select the number of processes based on how many CPU cores the machine has.
final int numWorkers = int.tryParse(options['workers'] as String ?? '');
final bool printReport = (options['report'] as bool) ?? false;
final Directory workingDirectory = Directory((options['workingDirectory'] as String) ?? '.');
final ProcessPool pool = ProcessPool(
numWorkers: numWorkers,
printReport: printReport ? ProcessPool.defaultPrintReport : null,
);
final List<WorkerJob> jobs = splitCommands.map<WorkerJob>((List<String> command) {
return WorkerJob(command, workingDirectory: workingDirectory);
}).toList();
await for (final WorkerJob done in pool.startWorkers(jobs)) {
if (printReport) {
print('\nFinished job ${done.name}');
}
}
}