| 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 import 'dart:async'; | 6 import 'dart:async'; |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 import 'dart:convert'; | 8 import 'dart:convert'; |
| 9 import 'dart:io'; | 9 import 'dart:io'; |
| 10 | 10 |
| 11 import 'package:async/async.dart'; |
| 11 import 'package:path/path.dart' as path; | 12 import 'package:path/path.dart' as path; |
| 12 import 'package:pool/pool.dart'; | 13 import 'package:pool/pool.dart'; |
| 13 import 'package:http/http.dart' show ByteStream; | 14 import 'package:http/http.dart' show ByteStream; |
| 14 import 'package:http_multi_server/http_multi_server.dart'; | 15 import 'package:http_multi_server/http_multi_server.dart'; |
| 15 import 'package:stack_trace/stack_trace.dart'; | 16 import 'package:stack_trace/stack_trace.dart'; |
| 16 | 17 |
| 17 import 'exit_codes.dart' as exit_codes; | 18 import 'exit_codes.dart' as exit_codes; |
| 18 import 'exceptions.dart'; | 19 import 'exceptions.dart'; |
| 19 import 'error_group.dart'; | 20 import 'error_group.dart'; |
| 20 import 'log.dart' as log; | 21 import 'log.dart' as log; |
| (...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 187 } | 188 } |
| 188 | 189 |
| 189 /// Writes [stream] to a new file at path [file]. | 190 /// Writes [stream] to a new file at path [file]. |
| 190 /// | 191 /// |
| 191 /// Replaces any file already at that path. Completes when the file is done | 192 /// Replaces any file already at that path. Completes when the file is done |
| 192 /// being written. | 193 /// being written. |
| 193 Future<String> createFileFromStream(Stream<List<int>> stream, String file) { | 194 Future<String> createFileFromStream(Stream<List<int>> stream, String file) { |
| 194 // TODO(nweiz): remove extra logging when we figure out the windows bot issue. | 195 // TODO(nweiz): remove extra logging when we figure out the windows bot issue. |
| 195 log.io("Creating $file from stream."); | 196 log.io("Creating $file from stream."); |
| 196 | 197 |
| 197 return _descriptorPool.withResource(() { | 198 return _descriptorPool.withResource/*<Future<String>>*/(() async { |
| 198 return stream.pipe(new File(file).openWrite()).then((_) { | 199 await stream.pipe(new File(file).openWrite()); |
| 199 log.fine("Created $file from stream."); | 200 log.fine("Created $file from stream."); |
| 200 return file; | 201 return file; |
| 201 }); | |
| 202 }); | 202 }); |
| 203 } | 203 } |
| 204 | 204 |
| 205 /// Copies all files in [files] to the directory [destination]. | 205 /// Copies all files in [files] to the directory [destination]. |
| 206 /// | 206 /// |
| 207 /// Their locations in [destination] will be determined by their relative | 207 /// Their locations in [destination] will be determined by their relative |
| 208 /// location to [baseDir]. Any existing files at those paths will be | 208 /// location to [baseDir]. Any existing files at those paths will be |
| 209 /// overwritten. | 209 /// overwritten. |
| 210 void copyFiles(Iterable<String> files, String baseDir, String destination) { | 210 void copyFiles(Iterable<String> files, String baseDir, String destination) { |
| 211 for (var file in files) { | 211 for (var file in files) { |
| (...skipping 420 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 632 Future flushThenExit(int status) { | 632 Future flushThenExit(int status) { |
| 633 return Future.wait([ | 633 return Future.wait([ |
| 634 stdout.close(), | 634 stdout.close(), |
| 635 stderr.close() | 635 stderr.close() |
| 636 ]).then((_) => exit(status)); | 636 ]).then((_) => exit(status)); |
| 637 } | 637 } |
| 638 | 638 |
| 639 /// Returns a [EventSink] that pipes all data to [consumer] and a [Future] that | 639 /// Returns a [EventSink] that pipes all data to [consumer] and a [Future] that |
| 640 /// will succeed when [EventSink] is closed or fail with any errors that occur | 640 /// will succeed when [EventSink] is closed or fail with any errors that occur |
| 641 /// while writing. | 641 /// while writing. |
| 642 Pair<EventSink, Future> consumerToSink(StreamConsumer consumer) { | 642 Pair<EventSink/*<T>*/, Future> consumerToSink/*<T>*/( |
| 643 var controller = new StreamController(sync: true); | 643 StreamConsumer/*<T>*/ consumer) { |
| 644 var controller = new StreamController/*<T>*/(sync: true); |
| 644 var done = controller.stream.pipe(consumer); | 645 var done = controller.stream.pipe(consumer); |
| 645 return new Pair<EventSink, Future>(controller.sink, done); | 646 return new Pair(controller.sink, done); |
| 646 } | 647 } |
| 647 | 648 |
| 648 // TODO(nweiz): remove this when issue 7786 is fixed. | 649 // TODO(nweiz): remove this when issue 7786 is fixed. |
| 649 /// Pipes all data and errors from [stream] into [sink]. | 650 /// Pipes all data and errors from [stream] into [sink]. |
| 650 /// | 651 /// |
| 651 /// When [stream] is done, the returned [Future] is completed and [sink] is | 652 /// When [stream] is done, the returned [Future] is completed and [sink] is |
| 652 /// closed if [closeSink] is true. | 653 /// closed if [closeSink] is true. |
| 653 /// | 654 /// |
| 654 /// When an error occurs on [stream], that error is passed to [sink]. If | 655 /// When an error occurs on [stream], that error is passed to [sink]. If |
| 655 /// [cancelOnError] is true, [Future] will be completed successfully and no | 656 /// [cancelOnError] is true, [Future] will be completed successfully and no |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 794 | 795 |
| 795 /// Creates a new [PubProcess] wrapping [process]. | 796 /// Creates a new [PubProcess] wrapping [process]. |
| 796 PubProcess(Process process) | 797 PubProcess(Process process) |
| 797 : _process = process { | 798 : _process = process { |
| 798 var errorGroup = new ErrorGroup(); | 799 var errorGroup = new ErrorGroup(); |
| 799 | 800 |
| 800 var pair = consumerToSink(process.stdin); | 801 var pair = consumerToSink(process.stdin); |
| 801 _stdin = pair.first; | 802 _stdin = pair.first; |
| 802 _stdinClosed = errorGroup.registerFuture(pair.last); | 803 _stdinClosed = errorGroup.registerFuture(pair.last); |
| 803 | 804 |
| 804 _stdout = new ByteStream( | 805 _stdout = new ByteStream(errorGroup.registerStream(process.stdout)); |
| 805 errorGroup.registerStream(process.stdout)); | 806 _stderr = new ByteStream(errorGroup.registerStream(process.stderr)); |
| 806 _stderr = new ByteStream( | |
| 807 errorGroup.registerStream(process.stderr)); | |
| 808 | 807 |
| 809 var exitCodeCompleter = new Completer(); | 808 var exitCodeCompleter = new Completer<int>(); |
| 810 _exitCode = errorGroup.registerFuture(exitCodeCompleter.future); | 809 _exitCode = errorGroup.registerFuture(exitCodeCompleter.future); |
| 811 _process.exitCode.then((code) => exitCodeCompleter.complete(code)); | 810 _process.exitCode.then((code) => exitCodeCompleter.complete(code)); |
| 812 } | 811 } |
| 813 | 812 |
| 814 /// Sends [signal] to the underlying process. | 813 /// Sends [signal] to the underlying process. |
| 815 bool kill([ProcessSignal signal = ProcessSignal.SIGTERM]) => | 814 bool kill([ProcessSignal signal = ProcessSignal.SIGTERM]) => |
| 816 _process.kill(signal); | 815 _process.kill(signal); |
| 817 } | 816 } |
| 818 | 817 |
| 819 /// Calls [fn] with appropriately modified arguments. | 818 /// Calls [fn] with appropriately modified arguments. |
| 820 /// | 819 /// |
| 821 /// [fn] should have the same signature as [Process.start], except that the | 820 /// [fn] should have the same signature as [Process.start], except that the |
| 822 /// returned value may have any return type. | 821 /// returned value may have any return type. |
| 823 _doProcess(Function fn, String executable, List<String> args, | 822 _doProcess(Function fn, String executable, List<String> args, |
| 824 {String workingDir, Map<String, String> environment, | 823 {String workingDir, Map<String, String> environment, |
| 825 bool runInShell: false}) { | 824 bool runInShell: false}) { |
| 826 // TODO(rnystrom): Should dart:io just handle this? | 825 // TODO(rnystrom): Should dart:io just handle this? |
| 827 // Spawning a process on Windows will not look for the executable in the | 826 // Spawning a process on Windows will not look for the executable in the |
| 828 // system path. So, if executable looks like it needs that (i.e. it doesn't | 827 // system path. So, if executable looks like it needs that (i.e. it doesn't |
| 829 // have any path separators in it), then spawn it through a shell. | 828 // have any path separators in it), then spawn it through a shell. |
| 830 if ((Platform.operatingSystem == "windows") && | 829 if ((Platform.operatingSystem == "windows") && |
| 831 (executable.indexOf('\\') == -1)) { | 830 (executable.indexOf('\\') == -1)) { |
| 832 args = flatten(["/c", executable, args]); | 831 args = ["/c", executable]..addAll(args); |
| 833 executable = "cmd"; | 832 executable = "cmd"; |
| 834 } | 833 } |
| 835 | 834 |
| 836 log.process(executable, args, workingDir == null ? '.' : workingDir); | 835 log.process(executable, args, workingDir == null ? '.' : workingDir); |
| 837 | 836 |
| 838 return fn(executable, args, | 837 return fn(executable, args, |
| 839 workingDirectory: workingDir, | 838 workingDirectory: workingDir, |
| 840 environment: environment, | 839 environment: environment, |
| 841 runInShell: runInShell); | 840 runInShell: runInShell); |
| 842 } | 841 } |
| 843 | 842 |
| 844 /// Updates [path]'s modification time. | 843 /// Updates [path]'s modification time. |
| 845 void touch(String path) { | 844 void touch(String path) { |
| 846 var file = new File(path).openSync(mode: FileMode.APPEND); | 845 var file = new File(path).openSync(mode: FileMode.APPEND); |
| 847 var originalLength = file.lengthSync(); | 846 var originalLength = file.lengthSync(); |
| 848 file.writeByteSync(0); | 847 file.writeByteSync(0); |
| 849 file.truncateSync(originalLength); | 848 file.truncateSync(originalLength); |
| 850 file.closeSync(); | 849 file.closeSync(); |
| 851 } | 850 } |
| 852 | 851 |
| 853 /// Creates a temporary directory and passes its path to [fn]. | 852 /// Creates a temporary directory and passes its path to [fn]. |
| 854 /// | 853 /// |
| 855 /// Once the [Future] returned by [fn] completes, the temporary directory and | 854 /// Once the [Future] returned by [fn] completes, the temporary directory and |
| 856 /// all its contents are deleted. [fn] can also return `null`, in which case | 855 /// all its contents are deleted. [fn] can also return `null`, in which case |
| 857 /// the temporary directory is deleted immediately afterwards. | 856 /// the temporary directory is deleted immediately afterwards. |
| 858 /// | 857 /// |
| 859 /// Returns a future that completes to the value that the future returned from | 858 /// Returns a future that completes to the value that the future returned from |
| 860 /// [fn] completes to. | 859 /// [fn] completes to. |
| 861 Future withTempDir(Future fn(String path)) { | 860 Future/*<T>*/ withTempDir/*<T>*/(Future/*<T>*/ fn(String path)) async { |
| 862 return new Future.sync(() { | 861 var tempDir = createSystemTempDir(); |
| 863 var tempDir = createSystemTempDir(); | 862 try { |
| 864 return new Future.sync(() => fn(tempDir)) | 863 return await fn(tempDir); |
| 865 .whenComplete(() => deleteEntry(tempDir)); | 864 } finally { |
| 866 }); | 865 deleteEntry(tempDir); |
| 866 } |
| 867 } | 867 } |
| 868 | 868 |
| 869 /// Binds an [HttpServer] to [host] and [port]. | 869 /// Binds an [HttpServer] to [host] and [port]. |
| 870 /// | 870 /// |
| 871 /// If [host] is "localhost", this will automatically listen on both the IPv4 | 871 /// If [host] is "localhost", this will automatically listen on both the IPv4 |
| 872 /// and IPv6 loopback addresses. | 872 /// and IPv6 loopback addresses. |
| 873 Future<HttpServer> bindServer(String host, int port) async { | 873 Future<HttpServer> bindServer(String host, int port) async { |
| 874 var server = host == 'localhost' | 874 var server = host == 'localhost' |
| 875 ? await HttpMultiServer.loopback(port) | 875 ? await HttpMultiServer.loopback(port) |
| 876 : await HttpServer.bind(host, port); | 876 : await HttpServer.bind(host, port); |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 988 }); | 988 }); |
| 989 } | 989 } |
| 990 | 990 |
| 991 /// Create a .tar.gz archive from a list of entries. | 991 /// Create a .tar.gz archive from a list of entries. |
| 992 /// | 992 /// |
| 993 /// Each entry can be a [String], [Directory], or [File] object. The root of | 993 /// Each entry can be a [String], [Directory], or [File] object. The root of |
| 994 /// the archive is considered to be [baseDir], which defaults to the current | 994 /// the archive is considered to be [baseDir], which defaults to the current |
| 995 /// working directory. | 995 /// working directory. |
| 996 /// | 996 /// |
| 997 /// Returns a [ByteStream] that emits the contents of the archive. | 997 /// Returns a [ByteStream] that emits the contents of the archive. |
| 998 ByteStream createTarGz(List contents, {baseDir}) { | 998 ByteStream createTarGz(List contents, {String baseDir}) { |
| 999 return new ByteStream(futureStream(new Future.sync(() async { | 999 return new ByteStream(StreamCompleter.fromFuture(new Future.sync(() async { |
| 1000 var buffer = new StringBuffer(); | 1000 var buffer = new StringBuffer(); |
| 1001 buffer.write('Creating .tag.gz stream containing:\n'); | 1001 buffer.write('Creating .tag.gz stream containing:\n'); |
| 1002 contents.forEach((file) => buffer.write('$file\n')); | 1002 contents.forEach((file) => buffer.write('$file\n')); |
| 1003 log.fine(buffer.toString()); | 1003 log.fine(buffer.toString()); |
| 1004 | 1004 |
| 1005 if (baseDir == null) baseDir = path.current; | 1005 if (baseDir == null) baseDir = path.current; |
| 1006 baseDir = path.absolute(baseDir); | 1006 baseDir = path.absolute(baseDir); |
| 1007 contents = contents.map((entry) { | 1007 contents = contents.map((entry) { |
| 1008 entry = path.absolute(entry); | 1008 entry = path.absolute(entry); |
| 1009 if (!path.isWithin(baseDir, entry)) { | 1009 if (!path.isWithin(baseDir, entry)) { |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1076 | 1076 |
| 1077 // TODO(rnystrom): Remove this and change to returning one string. | 1077 // TODO(rnystrom): Remove this and change to returning one string. |
| 1078 static List<String> _toLines(String output) { | 1078 static List<String> _toLines(String output) { |
| 1079 var lines = splitLines(output); | 1079 var lines = splitLines(output); |
| 1080 if (!lines.isEmpty && lines.last == "") lines.removeLast(); | 1080 if (!lines.isEmpty && lines.last == "") lines.removeLast(); |
| 1081 return lines; | 1081 return lines; |
| 1082 } | 1082 } |
| 1083 | 1083 |
| 1084 bool get success => exitCode == exit_codes.SUCCESS; | 1084 bool get success => exitCode == exit_codes.SUCCESS; |
| 1085 } | 1085 } |
| OLD | NEW |