Chromium Code Reviews| 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 |