OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 pub.io; | 6 library pub.io; |
7 | 7 |
8 import 'dart:async'; | 8 import 'dart:async'; |
9 import 'dart:collection'; | 9 import 'dart:collection'; |
10 import 'dart:convert'; | 10 import 'dart:convert'; |
11 import 'dart:io'; | 11 import 'dart:io'; |
12 | 12 |
13 import 'package:path/path.dart' as path; | 13 import 'package:path/path.dart' as path; |
14 import 'package:http/http.dart' show ByteStream; | 14 import 'package:http/http.dart' show ByteStream; |
| 15 import 'package:stack_trace/stack_trace.dart'; |
| 16 |
15 import 'error_group.dart'; | 17 import 'error_group.dart'; |
16 import 'log.dart' as log; | 18 import 'log.dart' as log; |
17 import 'sdk.dart' as sdk; | 19 import 'sdk.dart' as sdk; |
18 import 'utils.dart'; | 20 import 'utils.dart'; |
19 | 21 |
20 export 'package:http/http.dart' show ByteStream; | 22 export 'package:http/http.dart' show ByteStream; |
21 | 23 |
22 /// Returns whether or not [entry] is nested somewhere within [dir]. This just | 24 /// Returns whether or not [entry] is nested somewhere within [dir]. This just |
23 /// performs a path comparison; it doesn't look at the actual filesystem. | 25 /// performs a path comparison; it doesn't look at the actual filesystem. |
24 bool isBeneath(String entry, String dir) { | 26 bool isBeneath(String entry, String dir) { |
(...skipping 430 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
455 /// true. | 457 /// true. |
456 /// | 458 /// |
457 /// When an error occurs on [stream], that error is passed to [sink]. If | 459 /// When an error occurs on [stream], that error is passed to [sink]. If |
458 /// [cancelOnError] is true, [Future] will be completed successfully and no | 460 /// [cancelOnError] is true, [Future] will be completed successfully and no |
459 /// more data or errors will be piped from [stream] to [sink]. If | 461 /// more data or errors will be piped from [stream] to [sink]. If |
460 /// [cancelOnError] and [closeSink] are both true, [sink] will then be | 462 /// [cancelOnError] and [closeSink] are both true, [sink] will then be |
461 /// closed. | 463 /// closed. |
462 Future store(Stream stream, EventSink sink, | 464 Future store(Stream stream, EventSink sink, |
463 {bool cancelOnError: true, bool closeSink: true}) { | 465 {bool cancelOnError: true, bool closeSink: true}) { |
464 var completer = new Completer(); | 466 var completer = new Completer(); |
465 stream.listen(sink.add, | 467 stream.listen(sink.add, onError: (e, stackTrace) { |
466 onError: (e, [stackTrace]) { | 468 sink.addError(e, stackTrace); |
467 // TODO(floitsch): Sink.addError without stack trace. | 469 if (cancelOnError) { |
468 sink.addError(e); | 470 completer.complete(); |
469 if (cancelOnError) { | 471 if (closeSink) sink.close(); |
470 completer.complete(); | 472 } |
471 if (closeSink) sink.close(); | 473 }, onDone: () { |
472 } | 474 if (closeSink) sink.close(); |
473 }, | 475 completer.complete(); |
474 onDone: () { | 476 }, cancelOnError: cancelOnError); |
475 if (closeSink) sink.close(); | |
476 completer.complete(); | |
477 }, cancelOnError: cancelOnError); | |
478 return completer.future; | 477 return completer.future; |
479 } | 478 } |
480 | 479 |
481 /// Spawns and runs the process located at [executable], passing in [args]. | 480 /// Spawns and runs the process located at [executable], passing in [args]. |
482 /// Returns a [Future] that will complete with the results of the process after | 481 /// Returns a [Future] that will complete with the results of the process after |
483 /// it has ended. | 482 /// it has ended. |
484 /// | 483 /// |
485 /// The spawned process will inherit its parent's environment variables. If | 484 /// The spawned process will inherit its parent's environment variables. If |
486 /// [environment] is provided, that will be used to augment (not replace) the | 485 /// [environment] is provided, that will be used to augment (not replace) the |
487 /// the inherited variables. | 486 /// the inherited variables. |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
627 /// However, if [milliseconds] pass before [input] has completed, it completes | 626 /// However, if [milliseconds] pass before [input] has completed, it completes |
628 /// with a [TimeoutException] with [description] (which should be a fragment | 627 /// with a [TimeoutException] with [description] (which should be a fragment |
629 /// describing the action that timed out). | 628 /// describing the action that timed out). |
630 /// | 629 /// |
631 /// Note that timing out will not cancel the asynchronous operation behind | 630 /// Note that timing out will not cancel the asynchronous operation behind |
632 /// [input]. | 631 /// [input]. |
633 Future timeout(Future input, int milliseconds, String description) { | 632 Future timeout(Future input, int milliseconds, String description) { |
634 var completer = new Completer(); | 633 var completer = new Completer(); |
635 var timer = new Timer(new Duration(milliseconds: milliseconds), () { | 634 var timer = new Timer(new Duration(milliseconds: milliseconds), () { |
636 completer.completeError(new TimeoutException( | 635 completer.completeError(new TimeoutException( |
637 'Timed out while $description.')); | 636 'Timed out while $description.'), |
| 637 new Trace.current()); |
638 }); | 638 }); |
639 input.then((value) { | 639 input.then((value) { |
640 if (completer.isCompleted) return; | 640 if (completer.isCompleted) return; |
641 timer.cancel(); | 641 timer.cancel(); |
642 completer.complete(value); | 642 completer.complete(value); |
643 }).catchError((e) { | 643 }).catchError((e, stackTrace) { |
644 if (completer.isCompleted) return; | 644 if (completer.isCompleted) return; |
645 timer.cancel(); | 645 timer.cancel(); |
646 completer.completeError(e); | 646 completer.completeError(e, stackTrace); |
647 }); | 647 }); |
648 return completer.future; | 648 return completer.future; |
649 } | 649 } |
650 | 650 |
651 /// Creates a temporary directory and passes its path to [fn]. Once the [Future] | 651 /// Creates a temporary directory and passes its path to [fn]. Once the [Future] |
652 /// returned by [fn] completes, the temporary directory and all its contents | 652 /// returned by [fn] completes, the temporary directory and all its contents |
653 /// will be deleted. [fn] can also return `null`, in which case the temporary | 653 /// will be deleted. [fn] can also return `null`, in which case the temporary |
654 /// directory is deleted immediately afterwards. | 654 /// directory is deleted immediately afterwards. |
655 /// | 655 /// |
656 /// Returns a future that completes to the value that the future returned from | 656 /// Returns a future that completes to the value that the future returned from |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
768 }).toList(); | 768 }).toList(); |
769 | 769 |
770 if (Platform.operatingSystem != "windows") { | 770 if (Platform.operatingSystem != "windows") { |
771 var args = ["--create", "--gzip", "--directory", baseDir]; | 771 var args = ["--create", "--gzip", "--directory", baseDir]; |
772 args.addAll(contents); | 772 args.addAll(contents); |
773 // TODO(nweiz): It's possible that enough command-line arguments will make | 773 // TODO(nweiz): It's possible that enough command-line arguments will make |
774 // the process choke, so at some point we should save the arguments to a | 774 // the process choke, so at some point we should save the arguments to a |
775 // file and pass them in via --files-from for tar and -i@filename for 7zip. | 775 // file and pass them in via --files-from for tar and -i@filename for 7zip. |
776 startProcess("tar", args).then((process) { | 776 startProcess("tar", args).then((process) { |
777 store(process.stdout, controller); | 777 store(process.stdout, controller); |
778 }).catchError((e) { | 778 }).catchError((e, stackTrace) { |
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 startProcess succeeds. | 780 // above will only be reached if startProcess succeeds. |
781 controller.addError(e); | 781 controller.addError(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 withTempDir((tempDir) { | 787 withTempDir((tempDir) { |
788 // Create the tar file. | 788 // Create the tar file. |
789 var tarFile = path.join(tempDir, "intermediate.tar"); | 789 var tarFile = path.join(tempDir, "intermediate.tar"); |
790 var args = ["a", "-w$baseDir", tarFile]; | 790 var args = ["a", "-w$baseDir", tarFile]; |
791 args.addAll(contents.map((entry) => '-i!$entry')); | 791 args.addAll(contents.map((entry) => '-i!$entry')); |
792 | 792 |
793 // We're passing 'baseDir' both as '-w' and setting it as the working | 793 // We're passing 'baseDir' both as '-w' and setting it as the working |
794 // directory explicitly here intentionally. The former ensures that the | 794 // directory explicitly here intentionally. The former ensures that the |
795 // files added to the archive have the correct relative path in the archive. | 795 // files added to the archive have the correct relative path in the archive. |
796 // The latter enables relative paths in the "-i" args to be resolved. | 796 // The latter enables relative paths in the "-i" args to be resolved. |
797 return runProcess(pathTo7zip, args, workingDir: baseDir).then((_) { | 797 return runProcess(pathTo7zip, args, workingDir: baseDir).then((_) { |
798 // GZIP it. 7zip doesn't support doing both as a single operation. Send | 798 // GZIP it. 7zip doesn't support doing both as a single operation. Send |
799 // the output to stdout. | 799 // the output to stdout. |
800 args = ["a", "unused", "-tgzip", "-so", tarFile]; | 800 args = ["a", "unused", "-tgzip", "-so", tarFile]; |
801 return startProcess(pathTo7zip, args); | 801 return startProcess(pathTo7zip, args); |
802 }).then((process) { | 802 }).then((process) { |
803 // Ignore 7zip's stderr. 7zip writes its normal output to stderr. We don't | 803 // Ignore 7zip's stderr. 7zip writes its normal output to stderr. We don't |
804 // want to show that since it's meaningless. | 804 // want to show that since it's meaningless. |
805 // | 805 // |
806 // TODO(rnystrom): Should log the stderr and display it if an actual error | 806 // TODO(rnystrom): Should log the stderr and display it if an actual error |
807 // occurs. | 807 // occurs. |
808 return store(process.stdout, controller); | 808 return store(process.stdout, controller); |
809 }); | 809 }); |
810 }).catchError((e) { | 810 }).catchError((e, stackTrace) { |
811 // We don't have to worry about double-signaling here, since the store() | 811 // We don't have to worry about double-signaling here, since the store() |
812 // above will only be reached if everything succeeds. | 812 // above will only be reached if everything succeeds. |
813 controller.addError(e); | 813 controller.addError(e, stackTrace); |
814 controller.close(); | 814 controller.close(); |
815 }); | 815 }); |
816 return new ByteStream(controller.stream); | 816 return new ByteStream(controller.stream); |
817 } | 817 } |
818 | 818 |
819 /// Exception thrown when an operation times out. | 819 /// Exception thrown when an operation times out. |
820 class TimeoutException implements Exception { | 820 class TimeoutException implements Exception { |
821 final String message; | 821 final String message; |
822 | 822 |
823 const TimeoutException(this.message); | 823 const TimeoutException(this.message); |
(...skipping 10 matching lines...) Expand all Loading... |
834 const PubProcessResult(this.stdout, this.stderr, this.exitCode); | 834 const PubProcessResult(this.stdout, this.stderr, this.exitCode); |
835 | 835 |
836 bool get success => exitCode == 0; | 836 bool get success => exitCode == 0; |
837 } | 837 } |
838 | 838 |
839 /// Gets a [Uri] for [uri], which can either already be one, or be a [String]. | 839 /// Gets a [Uri] for [uri], which can either already be one, or be a [String]. |
840 Uri _getUri(uri) { | 840 Uri _getUri(uri) { |
841 if (uri is Uri) return uri; | 841 if (uri is Uri) return uri; |
842 return Uri.parse(uri); | 842 return Uri.parse(uri); |
843 } | 843 } |
OLD | NEW |