| 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 |