| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /// Helper functionality to make working with IO easier. | 5 /// Helper functionality to make working with IO easier. |
| 6 library io; | 6 library io; |
| 7 | 7 |
| 8 import 'dart:async'; | 8 import 'dart:async'; |
| 9 import 'dart:io'; | 9 import 'dart:io'; |
| 10 import 'dart:isolate'; | 10 import 'dart:isolate'; |
| (...skipping 334 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 345 } | 345 } |
| 346 | 346 |
| 347 return path.normalize(path.join(utilDir, 'pub', target)); | 347 return path.normalize(path.join(utilDir, 'pub', target)); |
| 348 } | 348 } |
| 349 | 349 |
| 350 // TODO(nweiz): add a ByteSink wrapper to make writing strings to stdout/stderr | 350 // TODO(nweiz): add a ByteSink wrapper to make writing strings to stdout/stderr |
| 351 // nicer. | 351 // nicer. |
| 352 | 352 |
| 353 /// A sink that writes to standard output. Errors piped to this stream will be | 353 /// A sink that writes to standard output. Errors piped to this stream will be |
| 354 /// surfaced to the top-level error handler. | 354 /// surfaced to the top-level error handler. |
| 355 final StreamSink<List<int>> stdoutSink = _wrapStdio(stdout, "stdout"); | 355 final EventSink<List<int>> stdoutSink = _wrapStdio(stdout, "stdout"); |
| 356 | 356 |
| 357 /// A sink that writes to standard error. Errors piped to this stream will be | 357 /// A sink that writes to standard error. Errors piped to this stream will be |
| 358 /// surfaced to the top-level error handler. | 358 /// surfaced to the top-level error handler. |
| 359 final StreamSink<List<int>> stderrSink = _wrapStdio(stderr, "stderr"); | 359 final EventSink<List<int>> stderrSink = _wrapStdio(stderr, "stderr"); |
| 360 | 360 |
| 361 /// Wrap the standard output or error [stream] in a [StreamSink]. Any errors are | 361 /// Wrap the standard output or error [stream] in a [EventSink]. Any errors are |
| 362 /// logged, and then the program is terminated. [name] is used for debugging. | 362 /// logged, and then the program is terminated. [name] is used for debugging. |
| 363 StreamSink<List<int>> _wrapStdio(IOSink sink, String name) { | 363 EventSink<List<int>> _wrapStdio(IOSink sink, String name) { |
| 364 var pair = consumerToSink(sink); | 364 var pair = consumerToSink(sink); |
| 365 pair.last.catchError((e) { | 365 pair.last.catchError((e) { |
| 366 // This log may or may not work, depending on how the stream failed. Not | 366 // This log may or may not work, depending on how the stream failed. Not |
| 367 // much we can do about that. | 367 // much we can do about that. |
| 368 log.error("Error writing to $name: $e"); | 368 log.error("Error writing to $name: $e"); |
| 369 exit(exit_codes.IO); | 369 exit(exit_codes.IO); |
| 370 }); | 370 }); |
| 371 return pair.first; | 371 return pair.first; |
| 372 } | 372 } |
| 373 | 373 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 387 return streamFirst(stdinLines) | 387 return streamFirst(stdinLines) |
| 388 .then((line) => new RegExp(r"^[yY]").hasMatch(line)); | 388 .then((line) => new RegExp(r"^[yY]").hasMatch(line)); |
| 389 } | 389 } |
| 390 | 390 |
| 391 /// Reads and discards all output from [stream]. Returns a [Future] that | 391 /// Reads and discards all output from [stream]. Returns a [Future] that |
| 392 /// completes when the stream is closed. | 392 /// completes when the stream is closed. |
| 393 Future drainStream(Stream stream) { | 393 Future drainStream(Stream stream) { |
| 394 return stream.reduce(null, (x, y) {}); | 394 return stream.reduce(null, (x, y) {}); |
| 395 } | 395 } |
| 396 | 396 |
| 397 /// Returns a [StreamSink] that pipes all data to [consumer] and a [Future] that | 397 /// Returns a [EventSink] that pipes all data to [consumer] and a [Future] that |
| 398 /// will succeed when [StreamSink] is closed or fail with any errors that occur | 398 /// will succeed when [EventSink] is closed or fail with any errors that occur |
| 399 /// while writing. | 399 /// while writing. |
| 400 Pair<StreamSink, Future> consumerToSink(StreamConsumer consumer) { | 400 Pair<EventSink, Future> consumerToSink(StreamConsumer consumer) { |
| 401 var controller = new StreamController(); | 401 var controller = new StreamController(); |
| 402 var done = controller.stream.pipe(consumer); | 402 var done = controller.stream.pipe(consumer); |
| 403 return new Pair<StreamSink, Future>(controller.sink, done); | 403 return new Pair<EventSink, Future>(controller.sink, done); |
| 404 } | 404 } |
| 405 | 405 |
| 406 // TODO(nweiz): remove this when issue 7786 is fixed. | 406 // TODO(nweiz): remove this when issue 7786 is fixed. |
| 407 /// Pipes all data and errors from [stream] into [sink]. When [stream] is done, | 407 /// Pipes all data and errors from [stream] into [sink]. When [stream] is done, |
| 408 /// the returned [Future] is completed and [sink] is closed if [closeSink] is | 408 /// the returned [Future] is completed and [sink] is closed if [closeSink] is |
| 409 /// true. | 409 /// true. |
| 410 /// | 410 /// |
| 411 /// When an error occurs on [stream], that error is passed to [sink]. If | 411 /// When an error occurs on [stream], that error is passed to [sink]. If |
| 412 /// [unsubscribeOnError] is true, [Future] will be completed successfully and no | 412 /// [unsubscribeOnError] is true, [Future] will be completed successfully and no |
| 413 /// more data or errors will be piped from [stream] to [sink]. If | 413 /// more data or errors will be piped from [stream] to [sink]. If |
| 414 /// [unsubscribeOnError] and [closeSink] are both true, [sink] will then be | 414 /// [unsubscribeOnError] and [closeSink] are both true, [sink] will then be |
| 415 /// closed. | 415 /// closed. |
| 416 Future store(Stream stream, StreamSink sink, | 416 Future store(Stream stream, EventSink sink, |
| 417 {bool unsubscribeOnError: true, closeSink: true}) { | 417 {bool unsubscribeOnError: true, closeSink: true}) { |
| 418 var completer = new Completer(); | 418 var completer = new Completer(); |
| 419 stream.listen(sink.add, | 419 stream.listen(sink.add, |
| 420 onError: (e) { | 420 onError: (e) { |
| 421 sink.signalError(e); | 421 sink.addError(e); |
| 422 if (unsubscribeOnError) { | 422 if (unsubscribeOnError) { |
| 423 completer.complete(); | 423 completer.complete(); |
| 424 if (closeSink) sink.close(); | 424 if (closeSink) sink.close(); |
| 425 } | 425 } |
| 426 }, | 426 }, |
| 427 onDone: () { | 427 onDone: () { |
| 428 if (closeSink) sink.close(); | 428 if (closeSink) sink.close(); |
| 429 completer.complete(); | 429 completer.complete(); |
| 430 }, unsubscribeOnError: unsubscribeOnError); | 430 }, unsubscribeOnError: unsubscribeOnError); |
| 431 return completer.future; | 431 return completer.future; |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 468 {workingDir, Map<String, String> environment}) => | 468 {workingDir, Map<String, String> environment}) => |
| 469 _doProcess(Process.start, executable, args, workingDir, environment) | 469 _doProcess(Process.start, executable, args, workingDir, environment) |
| 470 .then((process) => new PubProcess(process)); | 470 .then((process) => new PubProcess(process)); |
| 471 | 471 |
| 472 /// A wrapper around [Process] that exposes `dart:async`-style APIs. | 472 /// A wrapper around [Process] that exposes `dart:async`-style APIs. |
| 473 class PubProcess { | 473 class PubProcess { |
| 474 /// The underlying `dart:io` [Process]. | 474 /// The underlying `dart:io` [Process]. |
| 475 final Process _process; | 475 final Process _process; |
| 476 | 476 |
| 477 /// The mutable field for [stdin]. | 477 /// The mutable field for [stdin]. |
| 478 StreamSink<List<int>> _stdin; | 478 EventSink<List<int>> _stdin; |
| 479 | 479 |
| 480 /// The mutable field for [stdinClosed]. | 480 /// The mutable field for [stdinClosed]. |
| 481 Future _stdinClosed; | 481 Future _stdinClosed; |
| 482 | 482 |
| 483 /// The mutable field for [stdout]. | 483 /// The mutable field for [stdout]. |
| 484 ByteStream _stdout; | 484 ByteStream _stdout; |
| 485 | 485 |
| 486 /// The mutable field for [stderr]. | 486 /// The mutable field for [stderr]. |
| 487 ByteStream _stderr; | 487 ByteStream _stderr; |
| 488 | 488 |
| 489 /// The mutable field for [exitCode]. | 489 /// The mutable field for [exitCode]. |
| 490 Future<int> _exitCode; | 490 Future<int> _exitCode; |
| 491 | 491 |
| 492 /// The sink used for passing data to the process's standard input stream. | 492 /// The sink used for passing data to the process's standard input stream. |
| 493 /// Errors on this stream are surfaced through [stdinClosed], [stdout], | 493 /// Errors on this stream are surfaced through [stdinClosed], [stdout], |
| 494 /// [stderr], and [exitCode], which are all members of an [ErrorGroup]. | 494 /// [stderr], and [exitCode], which are all members of an [ErrorGroup]. |
| 495 StreamSink<List<int>> get stdin => _stdin; | 495 EventSink<List<int>> get stdin => _stdin; |
| 496 | 496 |
| 497 // TODO(nweiz): write some more sophisticated Future machinery so that this | 497 // TODO(nweiz): write some more sophisticated Future machinery so that this |
| 498 // doesn't surface errors from the other streams/futures, but still passes its | 498 // doesn't surface errors from the other streams/futures, but still passes its |
| 499 // unhandled errors to them. Right now it's impossible to recover from a stdin | 499 // unhandled errors to them. Right now it's impossible to recover from a stdin |
| 500 // error and continue interacting with the process. | 500 // error and continue interacting with the process. |
| 501 /// A [Future] that completes when [stdin] is closed, either by the user or by | 501 /// A [Future] that completes when [stdin] is closed, either by the user or by |
| 502 /// the process itself. | 502 /// the process itself. |
| 503 /// | 503 /// |
| 504 /// This is in an [ErrorGroup] with [stdout], [stderr], and [exitCode], so any | 504 /// This is in an [ErrorGroup] with [stdout], [stderr], and [exitCode], so any |
| 505 /// error in process will be passed to it, but won't reach the top-level error | 505 /// error in process will be passed to it, but won't reach the top-level error |
| (...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 734 var args = ["--create", "--gzip", "--directory", baseDir]; | 734 var args = ["--create", "--gzip", "--directory", baseDir]; |
| 735 args.addAll(contents); | 735 args.addAll(contents); |
| 736 // TODO(nweiz): It's possible that enough command-line arguments will make | 736 // TODO(nweiz): It's possible that enough command-line arguments will make |
| 737 // the process choke, so at some point we should save the arguments to a | 737 // the process choke, so at some point we should save the arguments to a |
| 738 // file and pass them in via --files-from for tar and -i@filename for 7zip. | 738 // file and pass them in via --files-from for tar and -i@filename for 7zip. |
| 739 startProcess("tar", args).then((process) { | 739 startProcess("tar", args).then((process) { |
| 740 store(process.stdout, controller); | 740 store(process.stdout, controller); |
| 741 }).catchError((e) { | 741 }).catchError((e) { |
| 742 // We don't have to worry about double-signaling here, since the store() | 742 // We don't have to worry about double-signaling here, since the store() |
| 743 // above will only be reached if startProcess succeeds. | 743 // above will only be reached if startProcess succeeds. |
| 744 controller.signalError(e.error, e.stackTrace); | 744 controller.addError(e.error, e.stackTrace); |
| 745 controller.close(); | 745 controller.close(); |
| 746 }); | 746 }); |
| 747 return new ByteStream(controller.stream); | 747 return new ByteStream(controller.stream); |
| 748 } | 748 } |
| 749 | 749 |
| 750 withTempDir((tempDir) { | 750 withTempDir((tempDir) { |
| 751 // Create the tar file. | 751 // Create the tar file. |
| 752 var tarFile = path.join(tempDir, "intermediate.tar"); | 752 var tarFile = path.join(tempDir, "intermediate.tar"); |
| 753 var args = ["a", "-w$baseDir", tarFile]; | 753 var args = ["a", "-w$baseDir", tarFile]; |
| 754 args.addAll(contents.map((entry) => '-i!"$entry"')); | 754 args.addAll(contents.map((entry) => '-i!"$entry"')); |
| (...skipping 16 matching lines...) Expand all Loading... |
| 771 // Ignore 7zip's stderr. 7zip writes its normal output to stderr. We don't | 771 // Ignore 7zip's stderr. 7zip writes its normal output to stderr. We don't |
| 772 // want to show that since it's meaningless. | 772 // want to show that since it's meaningless. |
| 773 // | 773 // |
| 774 // TODO(rnystrom): Should log the stderr and display it if an actual error | 774 // TODO(rnystrom): Should log the stderr and display it if an actual error |
| 775 // occurs. | 775 // occurs. |
| 776 return store(process.stdout, controller); | 776 return store(process.stdout, controller); |
| 777 }); | 777 }); |
| 778 }).catchError((e) { | 778 }).catchError((e) { |
| 779 // We don't have to worry about double-signaling here, since the store() | 779 // We don't have to worry about double-signaling here, since the store() |
| 780 // above will only be reached if everything succeeds. | 780 // above will only be reached if everything succeeds. |
| 781 controller.signalError(e.error, e.stackTrace); | 781 controller.addError(e.error, e.stackTrace); |
| 782 controller.close(); | 782 controller.close(); |
| 783 }); | 783 }); |
| 784 return new ByteStream(controller.stream); | 784 return new ByteStream(controller.stream); |
| 785 } | 785 } |
| 786 | 786 |
| 787 /// Exception thrown when an operation times out. | 787 /// Exception thrown when an operation times out. |
| 788 class TimeoutException implements Exception { | 788 class TimeoutException implements Exception { |
| 789 final String message; | 789 final String message; |
| 790 | 790 |
| 791 const TimeoutException(this.message); | 791 const TimeoutException(this.message); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 802 const PubProcessResult(this.stdout, this.stderr, this.exitCode); | 802 const PubProcessResult(this.stdout, this.stderr, this.exitCode); |
| 803 | 803 |
| 804 bool get success => exitCode == 0; | 804 bool get success => exitCode == 0; |
| 805 } | 805 } |
| 806 | 806 |
| 807 /// Gets a [Uri] for [uri], which can either already be one, or be a [String]. | 807 /// Gets a [Uri] for [uri], which can either already be one, or be a [String]. |
| 808 Uri _getUri(uri) { | 808 Uri _getUri(uri) { |
| 809 if (uri is Uri) return uri; | 809 if (uri is Uri) return uri; |
| 810 return Uri.parse(uri); | 810 return Uri.parse(uri); |
| 811 } | 811 } |
| OLD | NEW |