Fix ProcessManager, align better with dart:io (dart-lang/io#19) * Fix a bug, add named constructors. * Some refactors. * Dartfmt. * Fix example.
diff --git a/pkgs/io/CHANGELOG.md b/pkgs/io/CHANGELOG.md index b369f92..a0e6bf1 100644 --- a/pkgs/io/CHANGELOG.md +++ b/pkgs/io/CHANGELOG.md
@@ -1,4 +1,22 @@ -## 0.1.0 +## 0.3.0 + +- **BREAKING CHANGE**: The `arguments` argument to `ProcessManager.spawn` is + now positional (not named) and required. This makes it more similar to the + built-in `Process.start`, and easier to use as a drop in replacement: + +```dart +processManager.spawn('dart', ['--version']); +``` + +- Fixed a bug where processes created from `ProcessManager.spawn` could not + have their `stdout`/`stderr` read through their respective getters (a runtime + error was always thrown). + +- Added `ProcessMangaer#spawnBackground`, which does not forward `stdin`. + +- Added `ProcessManager#spawnDetached`, which does not forward any I/O. + +## 0.2.0 - Initial commit of... - `FutureOr<bool> String isExecutable(path)`.
diff --git a/pkgs/io/README.md b/pkgs/io/README.md index bbb3a1f..3d541a7 100644 --- a/pkgs/io/README.md +++ b/pkgs/io/README.md
@@ -2,6 +2,15 @@ [](https://travis-ci.org/dart-lang/io) +## Contributing + +> **NOTE**: Due to the changing nature of the Dart SDK (towards 2.0.0), running +> `dartfmt` requires the local executable: + +```sh +$ pub run dart_style:format +``` + ## Usage - `io.dart` ### Files
diff --git a/pkgs/io/example/spawn_process.dart b/pkgs/io/example/spawn_process.dart index d865138..7654110 100644 --- a/pkgs/io/example/spawn_process.dart +++ b/pkgs/io/example/spawn_process.dart
@@ -12,19 +12,19 @@ // Runs dartfmt --version and outputs the result via stdout. print('Running dartfmt --version'); - var spawn = await manager.spawn('dartfmt', arguments: ['--version']); + var spawn = await manager.spawn('dartfmt', ['--version']); await spawn.exitCode; // Runs dartfmt -n . and outputs the result via stdout. print('Running dartfmt -n .'); - spawn = await manager.spawn('dartfmt', arguments: ['-n', '.']); + spawn = await manager.spawn('dartfmt', ['-n', '.']); await spawn.exitCode; // Runs pub publish. Upon hitting a blocking stdin state, you may directly // output to the processes's stdin via your own, similar to how a bash or // shell script would spawn a process. print('Running pub publish'); - spawn = await manager.spawn('pub', arguments: ['publish']); + spawn = await manager.spawn('pub', ['publish']); await spawn.exitCode; // Closes stdin for the entire program.
diff --git a/pkgs/io/lib/src/process_manager.dart b/pkgs/io/lib/src/process_manager.dart index 496c17d..5a796d8 100644 --- a/pkgs/io/lib/src/process_manager.dart +++ b/pkgs/io/lib/src/process_manager.dart
@@ -52,12 +52,44 @@ /// /// Returns a future that completes with a handle to the spawned process. Future<io.Process> spawn( - String executable, { - Iterable<String> arguments: const [], - }) async { + String executable, + Iterable<String> arguments, + ) async { final process = io.Process.start(executable, arguments.toList()); return new _ForwardingSpawn(await process, _stdin, _stdout, _stderr); } + + /// Spawns a process by invoking [executable] with [arguments]. + /// + /// This is _similar_ to [io.Process.start], but `stdout` and `stderr` is + /// forwarded/routed between the process and host, similar to how a shell + /// script works. + /// + /// Returns a future that completes with a handle to the spawned process. + Future<io.Process> spawnBackground( + String executable, + Iterable<String> arguments, + ) async { + final process = io.Process.start(executable, arguments.toList()); + return new _ForwardingSpawn( + await process, + const Stream.empty(), + _stdout, + _stderr, + ); + } + + /// Spawns a process by invoking [executable] with [arguments]. + /// + /// This is _identical to [io.Process.start] (no forwarding of I/O). + /// + /// Returns a future that completes with a handle to the spawned process. + Future<io.Process> spawnDetached( + String executable, + Iterable<String> arguments, + ) async { + return io.Process.start(executable, arguments.toList()); + } } /// A process instance created and managed through [ProcessManager]. @@ -99,6 +131,8 @@ final StreamSubscription _stdInSub; final StreamSubscription _stdOutSub; final StreamSubscription _stdErrSub; + final StreamController<List<int>> _stdOut; + final StreamController<List<int>> _stdErr; factory _ForwardingSpawn( io.Process delegate, @@ -106,14 +140,24 @@ io.IOSink stdout, io.IOSink stderr, ) { + final stdoutSelf = new StreamController<List<int>>(); + final stderrSelf = new StreamController<List<int>>(); final stdInSub = stdin.listen(delegate.stdin.add); - final stdOutSub = delegate.stdout.listen(stdout.add); - final stdErrSub = delegate.stderr.listen(stderr.add); + final stdOutSub = delegate.stdout.listen((event) { + stdout.add(event); + stdoutSelf.add(event); + }); + final stdErrSub = delegate.stderr.listen((event) { + stderr.add(event); + stderrSelf.add(event); + }); return new _ForwardingSpawn._delegate( delegate, stdInSub, stdOutSub, stdErrSub, + stdoutSelf, + stderrSelf, ); } @@ -122,6 +166,8 @@ this._stdInSub, this._stdOutSub, this._stdErrSub, + this._stdOut, + this._stdErr, ) : super._(delegate); @@ -132,6 +178,12 @@ _stdErrSub.cancel(); super._onClosed(); } + + @override + Stream<List<int>> get stdout => _stdOut.stream; + + @override + Stream<List<int>> get stderr => _stdErr.stream; } class _UnixProcessManager extends ProcessManager {
diff --git a/pkgs/io/pubspec.yaml b/pkgs/io/pubspec.yaml index 2175d5d..5b8d7d7 100644 --- a/pkgs/io/pubspec.yaml +++ b/pkgs/io/pubspec.yaml
@@ -1,7 +1,7 @@ name: io description: > Utilities for the Dart VM Runtime. -version: 0.2.0 +version: 0.2.1 author: Dart Team <misc@dartlang.org> homepage: https://github.com/dart-lang/io @@ -12,5 +12,6 @@ meta: ^1.0.2 dev_dependencies: + dart_style: ^1.0.7 path: ^1.0.0 test: ^0.12.0
diff --git a/pkgs/io/test/process_manager_test.dart b/pkgs/io/test/process_manager_test.dart index 4ad49d7..88e63af 100644 --- a/pkgs/io/test/process_manager_test.dart +++ b/pkgs/io/test/process_manager_test.dart
@@ -42,7 +42,7 @@ test('should output Hello from another process [via stdout]', () async { final spawn = await processManager.spawn( 'dart', - arguments: [p.join('test', '_files', 'stdout_hello.dart')], + [p.join('test', '_files', 'stdout_hello.dart')], ); await spawn.exitCode; expect(stdoutLog, ['Hello']); @@ -51,7 +51,7 @@ test('should output Hello from another process [via stderr]', () async { final spawn = await processManager.spawn( 'dart', - arguments: [p.join('test', '_files', 'stderr_hello.dart')], + [p.join('test', '_files', 'stderr_hello.dart')], ); await spawn.exitCode; expect(stderrLog, ['Hello']); @@ -60,13 +60,29 @@ test('should forward stdin to another process', () async { final spawn = await processManager.spawn( 'dart', - arguments: [p.join('test', '_files', 'stdin_echo.dart')], + [p.join('test', '_files', 'stdin_echo.dart')], ); spawn.stdin.writeln('Ping'); await spawn.exitCode; - // TODO: https://github.com/dart-lang/sdk/issues/30119. - // expect(stdoutLog, ['You said: Ping', '\n']); expect(stdoutLog.join(''), contains('You said: Ping')); }); + + group('should return a Process where', () { + test('.stdout is readable', () async { + final spawn = await processManager.spawn( + 'dart', + [p.join('test', '_files', 'stdout_hello.dart')], + ); + expect(await spawn.stdout.transform(UTF8.decoder).first, 'Hello'); + }); + + test('.stderr is readable', () async { + final spawn = await processManager.spawn( + 'dart', + [p.join('test', '_files', 'stderr_hello.dart')], + ); + expect(await spawn.stderr.transform(UTF8.decoder).first, 'Hello'); + }); + }); }); }