OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 library test.util.compiler_pool; | 5 library test.util.compiler_pool; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:collection'; | 8 import 'dart:convert'; |
9 import 'dart:io'; | 9 import 'dart:io'; |
10 | 10 |
11 import 'package:path/path.dart' as p; | 11 import 'package:path/path.dart' as p; |
12 import 'package:pool/pool.dart'; | 12 import 'package:pool/pool.dart'; |
13 | 13 |
14 import '../../util/async_thunk.dart'; | 14 import '../../util/async_thunk.dart'; |
15 import '../../util/io.dart'; | 15 import '../../util/io.dart'; |
16 import '../../utils.dart'; | |
17 import '../load_exception.dart'; | 16 import '../load_exception.dart'; |
18 | 17 |
19 /// A pool of `dart2js` instances. | 18 /// A pool of `dart2js` instances. |
20 /// | 19 /// |
21 /// This limits the number of compiler instances running concurrently. It also | 20 /// This limits the number of compiler instances running concurrently. |
22 /// ensures that their output doesn't intermingle; only one instance is | |
23 /// "visible" (that is, having its output printed) at a time, and the other | |
24 /// instances' output is buffered until it's their turn to be visible. | |
25 class CompilerPool { | 21 class CompilerPool { |
26 /// The internal pool that controls the number of process running at once. | 22 /// The internal pool that controls the number of process running at once. |
27 final Pool _pool; | 23 final Pool _pool; |
28 | 24 |
29 /// Whether to enable colors on dart2js. | 25 /// Whether to enable colors on dart2js. |
30 final bool _color; | 26 final bool _color; |
31 | 27 |
32 /// The currently-active compilers. | 28 /// The currently-active dart2js processes. |
33 /// | 29 final _processes = new Set<Process>(); |
34 /// The first one is the only visible the compiler; the rest will become | |
35 /// visible in queue order. Note that some of these processes may actually | |
36 /// have already exited; they're kept around so that their output can be | |
37 /// emitted once they become visible. | |
38 final _compilers = new Queue<_Compiler>(); | |
39 | 30 |
40 /// Whether [close] has been called. | 31 /// Whether [close] has been called. |
41 bool get _closed => _closeThunk.hasRun; | 32 bool get _closed => _closeThunk.hasRun; |
42 | 33 |
43 /// The thunk for running [close] exactly once. | 34 /// The thunk for running [close] exactly once. |
44 final _closeThunk = new AsyncThunk(); | 35 final _closeThunk = new AsyncThunk(); |
45 | 36 |
46 /// Creates a compiler pool that runs up to [concurrency] instances of | 37 /// Creates a compiler pool that runs up to [concurrency] instances of |
47 /// `dart2js` at once. | 38 /// `dart2js` at once. |
48 /// | 39 /// |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
82 | 73 |
83 var args = ["--checked", wrapperPath, "--out=$jsPath", "--show-package-w
arnings"]; | 74 var args = ["--checked", wrapperPath, "--out=$jsPath", "--show-package-w
arnings"]; |
84 | 75 |
85 if (packageRoot != null) { | 76 if (packageRoot != null) { |
86 args.add("--package-root=${p.toUri(p.absolute(packageRoot))}"); | 77 args.add("--package-root=${p.toUri(p.absolute(packageRoot))}"); |
87 } | 78 } |
88 | 79 |
89 if (_color) args.add("--enable-diagnostic-colors"); | 80 if (_color) args.add("--enable-diagnostic-colors"); |
90 | 81 |
91 var process = await Process.start(dart2jsPath, args); | 82 var process = await Process.start(dart2jsPath, args); |
92 var compiler = new _Compiler(dartPath, process); | 83 if (_closed) { |
| 84 process.kill(); |
| 85 return; |
| 86 } |
93 | 87 |
94 if (_compilers.isEmpty) _showProcess(compiler); | 88 _processes.add(process); |
95 _compilers.add(compiler); | |
96 | 89 |
97 await compiler.onDone; | 90 /// Wait until the process is entirely done to print out any output. |
| 91 /// This can produce a little extra time for users to wait with no |
| 92 /// update, but it also avoids some really nasty-looking interleaved |
| 93 /// output. Write both stdout and stderr to the same buffer in case |
| 94 /// they're intended to be printed in order. |
| 95 var buffer = new StringBuffer(); |
| 96 |
| 97 await Future.wait([ |
| 98 _printOutputStream(process.stdout, buffer), |
| 99 _printOutputStream(process.stderr, buffer), |
| 100 ]); |
| 101 |
| 102 var exitCode = await process.exitCode; |
| 103 _processes.remove(process); |
| 104 if (_closed) return; |
| 105 |
| 106 if (buffer.isNotEmpty) print(buffer); |
| 107 |
| 108 if (exitCode != 0) { |
| 109 throw new LoadException(dartPath, "dart2js failed."); |
| 110 } |
98 }); | 111 }); |
99 }); | 112 }); |
100 } | 113 } |
101 | 114 |
102 /// Mark [compiler] as the visible instance. | 115 /// Sanitizes the bytes emitted by [stream], converts them to text, and writes |
103 /// | 116 /// them to [buffer]. |
104 /// This prints all [compiler]'s standard output and error. | 117 Future _printOutputStream(Stream<List<int>> stream, StringBuffer buffer) { |
105 void _showProcess(_Compiler compiler) { | 118 return sanitizeForWindows(stream) |
106 print("Compiling ${compiler.path}..."); | 119 .listen((data) => buffer.write(UTF8.decode(data))).asFuture(); |
107 | |
108 invoke(() async { | |
109 try { | |
110 // We wait for stdout and stderr to close and for exitCode to fire to | |
111 // ensure that we're done printing everything about one process before | |
112 // we start the next. | |
113 await Future.wait([ | |
114 sanitizeForWindows(compiler.process.stdout).listen(stdout.add) | |
115 .asFuture(), | |
116 sanitizeForWindows(compiler.process.stderr).listen(stderr.add) | |
117 .asFuture(), | |
118 compiler.process.exitCode.then((exitCode) { | |
119 if (exitCode == 0 || _closed) return; | |
120 throw new LoadException(compiler.path, "dart2js failed."); | |
121 }) | |
122 ]); | |
123 | |
124 if (_closed) return; | |
125 compiler.onDoneCompleter.complete(); | |
126 } catch (error, stackTrace) { | |
127 if (_closed) return; | |
128 compiler.onDoneCompleter.completeError(error, stackTrace); | |
129 } | |
130 | |
131 _compilers.removeFirst(); | |
132 if (_compilers.isEmpty) return; | |
133 | |
134 var next = _compilers.first; | |
135 | |
136 // Wait a bit before printing the next process in case the current one | |
137 // threw an error that needs to be printed. | |
138 Timer.run(() => _showProcess(next)); | |
139 }); | |
140 } | 120 } |
141 | 121 |
142 /// Closes the compiler pool. | 122 /// Closes the compiler pool. |
143 /// | 123 /// |
144 /// This kills all currently-running compilers and ensures that no more will | 124 /// This kills all currently-running compilers and ensures that no more will |
145 /// be started. It returns a [Future] that completes once all the compilers | 125 /// be started. It returns a [Future] that completes once all the compilers |
146 /// have been killed and all resources released. | 126 /// have been killed and all resources released. |
147 Future close() { | 127 Future close() { |
148 return _closeThunk.run(() async { | 128 return _closeThunk.run(() async { |
149 await Future.wait(_compilers.map((compiler) async { | 129 await Future.wait(_processes.map((process) async { |
150 compiler.process.kill(); | 130 process.kill(); |
151 await compiler.process.exitCode; | 131 await process.exitCode; |
152 compiler.onDoneCompleter.complete(); | |
153 })); | 132 })); |
154 | |
155 _compilers.clear(); | |
156 }); | 133 }); |
157 } | 134 } |
158 } | 135 } |
159 | |
160 /// A running instance of `dart2js`. | |
161 class _Compiler { | |
162 /// The path of the Dart file being compiled. | |
163 final String path; | |
164 | |
165 /// The underlying process. | |
166 final Process process; | |
167 | |
168 /// A future that will complete once this instance has finished running and | |
169 /// all its output has been printed. | |
170 Future get onDone => onDoneCompleter.future; | |
171 final onDoneCompleter = new Completer(); | |
172 | |
173 _Compiler(this.path, this.process); | |
174 } | |
OLD | NEW |