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 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
181 log.fine("Ensuring directory $path exists."); | 181 log.fine("Ensuring directory $path exists."); |
182 if (path == '.') return new Future.immediate(new Directory('.')); | 182 if (path == '.') return new Future.immediate(new Directory('.')); |
183 | 183 |
184 return dirExists(path).then((exists) { | 184 return dirExists(path).then((exists) { |
185 if (exists) { | 185 if (exists) { |
186 log.fine("Directory $path already exists."); | 186 log.fine("Directory $path already exists."); |
187 return new Future.immediate(new Directory(path)); | 187 return new Future.immediate(new Directory(path)); |
188 } | 188 } |
189 | 189 |
190 return ensureDir(dirname(path)).then((_) { | 190 return ensureDir(dirname(path)).then((_) { |
191 return createDir(path) | 191 return createDir(path).catchError((asyncError) { |
192 .catchError((error) { | 192 var error = getRealError(asyncError); |
193 if (error is! DirectoryIOException) return false; | 193 if (error is! DirectoryIOException) throw asyncError; |
194 // Error 17 means the directory already exists (or 183 on Windows). | 194 // Error 17 means the directory already exists (or 183 on Windows). |
195 if (error.osError.errorCode != 17 && | 195 if (error.osError.errorCode != 17 && |
196 error.osError.errorCode != 183) { | 196 error.osError.errorCode != 183) { |
197 log.fine("Got 'already exists' error when creating directory."); | 197 log.fine("Got 'already exists' error when creating directory."); |
Bob Nystrom
2013/01/09 16:49:23
This log is wrong. This "if" is for when the error
| |
198 return false; | 198 throw asyncError; |
199 } | 199 } |
200 | 200 |
201 return _getDirectory(path); | 201 return _getDirectory(path); |
202 }); | 202 }); |
203 }); | 203 }); |
204 }); | 204 }); |
205 } | 205 } |
206 | 206 |
207 /// Creates a temp directory whose name will be based on [dir] with a unique | 207 /// Creates a temp directory whose name will be based on [dir] with a unique |
208 /// suffix appended to it. If [dir] is not provided, a temp directory will be | 208 /// suffix appended to it. If [dir] is not provided, a temp directory will be |
209 /// created in a platform-dependent temporary location. Returns a [Future] that | 209 /// created in a platform-dependent temporary location. Returns a [Future] that |
210 /// completes when the directory is created. | 210 /// completes when the directory is created. |
211 Future<Directory> createTempDir([dir = '']) { | 211 Future<Directory> createTempDir([dir = '']) { |
212 dir = _getDirectory(dir); | 212 dir = _getDirectory(dir); |
(...skipping 342 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
555 } | 555 } |
556 | 556 |
557 var completer = new Completer<String>(); | 557 var completer = new Completer<String>(); |
558 var buffer = new StringBuffer(); | 558 var buffer = new StringBuffer(); |
559 stream.onClosed = () => completer.complete(buffer.toString()); | 559 stream.onClosed = () => completer.complete(buffer.toString()); |
560 stream.onData = () => buffer.add(stream.read()); | 560 stream.onData = () => buffer.add(stream.read()); |
561 stream.onError = (e) => completer.completeError(e, stackTrace); | 561 stream.onError = (e) => completer.completeError(e, stackTrace); |
562 return completer.future; | 562 return completer.future; |
563 } | 563 } |
564 | 564 |
565 /// Wrap an InputStream in a ListInputStream. This eagerly drains the [source] | 565 /// Wraps [stream] in a single-subscription [ByteStream] that emits the same |
566 /// input stream. This is useful for spawned processes which will not exit until | 566 /// data. |
567 /// their output streams have been drained. | 567 Stream<List<int>> wrapInputStream(InputStream stream) { |
Bob Nystrom
2013/01/09 16:49:23
Type the return as a ByteStream?
Also, can we hav
| |
568 /// TODO(rnystrom): We should use this logic anywhere we spawn a process. | 568 if (stream.closed) return new StreamController.singleSubscription()..close(); |
Bob Nystrom
2013/01/09 16:49:23
The duplicate constructor is a bit gross. How abou
| |
569 InputStream wrapInputStream(InputStream source) { | 569 |
570 var sink = new ListInputStream(); | 570 var controller = new StreamController.singleSubscription(); |
571 pipeInputToInput(source, sink); | 571 stream.onClosed = controller.close; |
572 return sink; | 572 stream.onData = () => controller.add(stream.read()); |
573 stream.onError = (e) => controller.signalError(new AsyncError(e)); | |
574 return controller.stream; | |
575 } | |
576 | |
577 // TODO(nweiz): remove this ASAP | |
Bob Nystrom
2013/01/09 16:49:23
Tracking bug?
| |
578 /// Wraps [stream] in an [InputStream]. | |
579 InputStream wrapByteStream(Stream<List<int>> stream) { | |
Bob Nystrom
2013/01/09 16:49:23
Maybe these method names should be more explicit.
| |
580 var inputStream = new ListInputStream(); | |
581 stream.listen((chunk) => inputStream.write(chunk), | |
582 onDone: inputStream.markEndOfStream); | |
583 return inputStream; | |
584 } | |
585 | |
586 /// Wraps [stream] in a [StreamConsumer] so that [Stream]s can by piped into it | |
587 /// using [Stream.pipe]. | |
588 StreamConsumer<List<int>, dynamic> wrapOutputStream(OutputStream stream) => | |
Bob Nystrom
2013/01/09 16:49:23
"toStreamConsumer" ?
| |
589 new _OutputStreamConsumer(stream); | |
590 | |
591 /// A [StreamConsumer] that pipes data into an [OutputStream]. | |
592 class _OutputStreamConsumer implements StreamConsumer<List<int>, dynamic> { | |
593 final OutputStream _outputStream; | |
594 | |
595 _OutputStreamConsumer(this._outputStream) | |
596 : super(); | |
Bob Nystrom
2013/01/09 16:49:23
Unnecessary, especially since you aren't extending
| |
597 | |
598 Future consume(Stream<List<int>> stream) { | |
599 // TODO(nweiz): we have to manually keep track of whether or not the | |
600 // completer has completed since the output stream could signal an error | |
601 // after close() has been called but before it has shut down internally. See | |
602 // the following TODO. | |
603 var completed = false; | |
604 var completer = new Completer(); | |
605 stream.listen((data) => _outputStream.write(data), onDone: () { | |
606 _outputStream.close(); | |
607 // TODO(nweiz): wait until _outputStream.onClosed is called once issue | |
608 // 7761 is fixed. | |
609 if (!completed) completer.complete(null); | |
610 completed = true; | |
611 }); | |
612 | |
613 _outputStream.onError = (e) { | |
614 if (!completed) completer.completeError(e); | |
Bob Nystrom
2013/01/09 16:49:23
Want to do that little hack to grab a stack trace
| |
615 completed = true; | |
616 }; | |
617 | |
618 return completer.future; | |
619 } | |
573 } | 620 } |
574 | 621 |
575 /// Spawns and runs the process located at [executable], passing in [args]. | 622 /// Spawns and runs the process located at [executable], passing in [args]. |
576 /// Returns a [Future] that will complete with the results of the process after | 623 /// Returns a [Future] that will complete with the results of the process after |
577 /// it has ended. | 624 /// it has ended. |
578 /// | 625 /// |
579 /// The spawned process will inherit its parent's environment variables. If | 626 /// The spawned process will inherit its parent's environment variables. If |
580 /// [environment] is provided, that will be used to augment (not replace) the | 627 /// [environment] is provided, that will be used to augment (not replace) the |
581 /// the inherited variables. | 628 /// the inherited variables. |
582 Future<PubProcessResult> runProcess(String executable, List<String> args, | 629 Future<PubProcessResult> runProcess(String executable, List<String> args, |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
618 final InputStream stdout; | 665 final InputStream stdout; |
619 | 666 |
620 OutputStream get stdin => _process.stdin; | 667 OutputStream get stdin => _process.stdin; |
621 | 668 |
622 void set onExit(void callback(int exitCode)) { | 669 void set onExit(void callback(int exitCode)) { |
623 _process.onExit = callback; | 670 _process.onExit = callback; |
624 } | 671 } |
625 | 672 |
626 _WrappedProcess(Process process) | 673 _WrappedProcess(Process process) |
627 : _process = process, | 674 : _process = process, |
628 stderr = wrapInputStream(process.stderr), | 675 stderr = _wrapInputStream(process.stderr), |
629 stdout = wrapInputStream(process.stdout); | 676 stdout = _wrapInputStream(process.stdout); |
630 | 677 |
631 bool kill([ProcessSignal signal = ProcessSignal.SIGTERM]) => | 678 bool kill([ProcessSignal signal = ProcessSignal.SIGTERM]) => |
632 _process.kill(signal); | 679 _process.kill(signal); |
680 | |
681 /// Wrap an InputStream in a ListInputStream. This eagerly drains the [source] | |
682 /// input stream. This is useful for spawned processes which will not exit unt il | |
Bob Nystrom
2013/01/09 16:49:23
Long line.
| |
683 /// their output streams have been drained. | |
684 /// TODO(rnystrom): We should use this logic anywhere we spawn a process. | |
685 static InputStream _wrapInputStream(InputStream source) { | |
686 var sink = new ListInputStream(); | |
687 pipeInputToInput(source, sink); | |
688 return sink; | |
689 } | |
633 } | 690 } |
634 | 691 |
635 /// Calls [fn] with appropriately modified arguments. [fn] should have the same | 692 /// Calls [fn] with appropriately modified arguments. [fn] should have the same |
636 /// signature as [Process.start], except that the returned [Future] may have a | 693 /// signature as [Process.start], except that the returned [Future] may have a |
637 /// type other than [Process]. | 694 /// type other than [Process]. |
638 Future _doProcess(Function fn, String executable, List<String> args, workingDir, | 695 Future _doProcess(Function fn, String executable, List<String> args, workingDir, |
639 Map<String, String> environment) { | 696 Map<String, String> environment) { |
640 // TODO(rnystrom): Should dart:io just handle this? | 697 // TODO(rnystrom): Should dart:io just handle this? |
641 // Spawning a process on Windows will not look for the executable in the | 698 // Spawning a process on Windows will not look for the executable in the |
642 // system path. So, if executable looks like it needs that (i.e. it doesn't | 699 // system path. So, if executable looks like it needs that (i.e. it doesn't |
(...skipping 28 matching lines...) Expand all Loading... | |
671 /// Note that timing out will not cancel the asynchronous operation behind | 728 /// Note that timing out will not cancel the asynchronous operation behind |
672 /// [input]. | 729 /// [input]. |
673 Future timeout(Future input, int milliseconds, String description) { | 730 Future timeout(Future input, int milliseconds, String description) { |
674 bool completed = false; | 731 bool completed = false; |
675 var completer = new Completer(); | 732 var completer = new Completer(); |
676 var timer = new Timer(milliseconds, (_) { | 733 var timer = new Timer(milliseconds, (_) { |
677 completed = true; | 734 completed = true; |
678 completer.completeError(new TimeoutException( | 735 completer.completeError(new TimeoutException( |
679 'Timed out while $description.')); | 736 'Timed out while $description.')); |
680 }); | 737 }); |
681 input | 738 input.then((value) { |
682 .then((value) { | 739 if (completed) return; |
683 if (completed) return; | 740 timer.cancel(); |
684 timer.cancel(); | 741 completer.complete(value); |
685 completer.complete(value); | 742 }).catchError((e) { |
686 }) | 743 if (completed) return; |
687 .catchError((e) { | 744 timer.cancel(); |
688 if (completed) return; | 745 completer.completeError(e.error, e.stackTrace); |
689 timer.cancel(); | 746 }); |
690 completer.completeError(e.error, e.stackTrace); | |
691 }); | |
692 return completer.future; | 747 return completer.future; |
693 } | 748 } |
694 | 749 |
695 /// Creates a temporary directory and passes its path to [fn]. Once the [Future] | 750 /// Creates a temporary directory and passes its path to [fn]. Once the [Future] |
696 /// returned by [fn] completes, the temporary directory and all its contents | 751 /// returned by [fn] completes, the temporary directory and all its contents |
697 /// will be deleted. | 752 /// will be deleted. |
698 Future withTempDir(Future fn(String path)) { | 753 Future withTempDir(Future fn(String path)) { |
699 var tempDir; | 754 var tempDir; |
700 var future = createTempDir().then((dir) { | 755 return asyncWhenComplete(createTempDir().then((dir) { |
701 tempDir = dir; | 756 tempDir = dir; |
702 return fn(tempDir.path); | 757 return fn(tempDir.path); |
758 }), () { | |
759 log.fine('Cleaning up temp directory ${tempDir.path}.'); | |
760 return deleteDir(tempDir); | |
703 }); | 761 }); |
704 future.catchError((_) {}).then((_) { | |
705 log.fine('Cleaning up temp directory ${tempDir.path}.'); | |
706 deleteDir(tempDir); | |
707 }); | |
708 return future; | |
709 } | 762 } |
710 | 763 |
711 /// Tests whether or not the git command-line app is available for use. | 764 /// Tests whether or not the git command-line app is available for use. |
712 Future<bool> get isGitInstalled { | 765 Future<bool> get isGitInstalled { |
713 if (_isGitInstalledCache != null) { | 766 if (_isGitInstalledCache != null) { |
714 // TODO(rnystrom): The sleep is to pump the message queue. Can use | 767 // TODO(rnystrom): The sleep is to pump the message queue. Can use |
715 // Future.immediate() when #3356 is fixed. | 768 // Future.immediate() when #3356 is fixed. |
716 return sleep(0).then((_) => _isGitInstalledCache); | 769 return sleep(0).then((_) => _isGitInstalledCache); |
717 } | 770 } |
718 | 771 |
(...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
967 Directory _getDirectory(entry) { | 1020 Directory _getDirectory(entry) { |
968 if (entry is Directory) return entry; | 1021 if (entry is Directory) return entry; |
969 return new Directory(entry); | 1022 return new Directory(entry); |
970 } | 1023 } |
971 | 1024 |
972 /// Gets a [Uri] for [uri], which can either already be one, or be a [String]. | 1025 /// Gets a [Uri] for [uri], which can either already be one, or be a [String]. |
973 Uri _getUri(uri) { | 1026 Uri _getUri(uri) { |
974 if (uri is Uri) return uri; | 1027 if (uri is Uri) return uri; |
975 return new Uri.fromString(uri); | 1028 return new Uri.fromString(uri); |
976 } | 1029 } |
OLD | NEW |