Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(847)

Side by Side Diff: pkg/front_end/example/incremental_reload/run.dart

Issue 2928483005: Add an incremental reloader example and a utility tool to trigger a reload by (Closed)
Patch Set: CL comments Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
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.
4
5 /// Example that illustrates how to use the incremental compiler and trigger a
6 /// hot-reload on the VM after recompiling the application.
7 ///
8 /// This example resembles the `run` command in flutter-tools. It creates an
9 /// interactive command-line program that waits for the user to tap a key to
10 /// trigger a recompile and reload.
11 ///
12 /// The following instructions assume a linux checkout of the SDK:
13 /// * Build the SDK
14 ///
15 /// ```
16 /// ./tools/build.py -m release
17 /// ```
18 ///
19 /// * On one terminal (terminal A), start this script and point it to an
20 /// example program "foo.dart" and keep the job running. A good example
21 /// program would do something periodically, so you can see the effect
22 /// of a hot-reload while the app is running.
23 ///
24 /// ```
25 /// out/ReleaseX64/dart pkg/front_end/example/incremental_reload/run.dart foo .dart out.dill
26 /// ```
27 ///
28 /// * Trigger an initial compile of the program by hitting the "c" key in
29 /// terminal A.
30 ///
31 /// * On another terminal (terminal B), start the program on the VM, with the
32 /// service-protocol enabled and provide the precompiled platform libraries:
33 ///
34 /// ```
35 /// out/ReleaseX64/dart --enable-vm-service --platform=out/ReleaseX64/patched _sdk/platform.dill out.dill
36 /// ```
37 ///
38 /// * Modify the orginal program
39 ///
40 /// * In terminal A, hit the "r" key to trigger a recompile and hot reload.
41 ///
42 /// * See the changed program in terminal B
43 library front_end.example.incremental_reload.run;
44
45 import 'dart:io';
46 import 'dart:async';
47 import 'dart:convert' show ASCII;
48
49 import 'package:front_end/src/vm/reload.dart';
50
51 import 'compiler_with_invalidation.dart';
52
53 VmReloader reloader = new VmReloader();
54 AnsiTerminal terminal = new AnsiTerminal();
55
56 main(List<String> args) async {
57 if (args.length <= 1) {
58 print('usage: dart incremental_compile.dart input.dart out.dill');
59 exit(1);
60 }
61
62 var compiler = await createIncrementalCompiler(args[0]);
63 var outputUri = Uri.base.resolve(args[1]);
64
65 showHeader();
66 listenOnKeyPress(compiler, outputUri)
67 .whenComplete(() => reloader.disconnect());
68 }
69
70 /// Implements the interactive UI by listening for input keys from the user.
71 Future listenOnKeyPress(IncrementalCompiler compiler, Uri outputUri) {
72 var completer = new Completer();
73 terminal.singleCharMode = true;
74 StreamSubscription subscription;
75 subscription = terminal.onCharInput.listen((String char) async {
76 try {
77 CompilationResult compilationResult;
78 ReloadResult reloadResult;
79 switch (char) {
80 case 'r':
81 compilationResult = await rebuild(compiler, outputUri);
82 if (!compilationResult.errorSeen &&
83 compilationResult.program != null &&
84 compilationResult.program.libraries.isNotEmpty) {
85 reloadResult = await reload(outputUri);
86 }
87 break;
88 case 'c':
89 compilationResult = await rebuild(compiler, outputUri);
90 break;
91 case 'l':
92 reloadResult = await reload(outputUri);
93 break;
94 case 'q':
95 terminal.singleCharMode = false;
96 print('');
97 subscription.cancel();
98 completer.complete(null);
99 break;
100 default:
101 break;
102 }
103 if (compilationResult != null || reloadResult != null) {
104 reportStats(compilationResult, reloadResult, outputUri);
105 }
106 } catch (e) {
107 terminal.singleCharMode = false;
108 subscription.cancel();
109 completer.completeError(null);
110 rethrow;
111 }
112 }, onError: (e) {
113 terminal.singleCharMode = false;
114 subscription.cancel();
115 completer.completeError(null);
116 });
117
118 return completer.future;
119 }
120
121 /// Request a reload and gather timing metrics.
122 Future<ReloadResult> reload(outputUri) async {
123 var result = new ReloadResult();
124 var reloadTimer = new Stopwatch()..start();
125 var reloadResult = await reloader.reload(outputUri);
126 reloadTimer.stop();
127 result.reloadTime = reloadTimer.elapsedMilliseconds;
128 result.errorSeen = false;
129 result.errorDetails;
130 if (!reloadResult['success']) {
131 result.errorSeen = true;
132 result.errorDetails = reloadResult['details']['notices'].first['message'];
133 }
134 return result;
135 }
136
137 /// Results from requesting a hot reload.
138 class ReloadResult {
139 /// How long it took to do the hot-reload in the VM.
140 int reloadTime = 0;
141
142 /// Whether we saw errors during compilation or reload.
143 bool errorSeen = false;
144
145 /// Error message when [errorSeen] is true.
146 String errorDetails;
147 }
148
149 /// This script shows stats about each reload on the terminal in a table form.
150 /// This function prints out the header of such table.
151 showHeader() {
152 print(terminal.bolden('Press a key to trigger a command:'));
153 print(terminal.bolden(' r: incremental compile + reload'));
154 print(terminal.bolden(' c: incremental compile w/o reload'));
155 print(terminal.bolden(' l: reload w/o recompile'));
156 print(terminal.bolden(' q: quit'));
157 print(terminal.bolden(
158 '# Files Files % ------- Time ------------------------- Binar y\n'
159 ' Modified Sent Total Check Compile Reload Total Avg Size '));
160 }
161
162 /// Whether to show stats as a single line (override metrics on each request)
163 const bool singleLine = false;
164
165 var total = 0;
166 var iter = 0;
167 var timeSum = 0;
168 var lastLine = 0;
169
170 /// Show stats about a recompile and reload.
171 reportStats(CompilationResult compilationResult, ReloadResult reloadResult,
172 Uri outputUri) {
173 compilationResult ??= new CompilationResult();
174 reloadResult ??= new ReloadResult();
175 int changed = compilationResult.changed;
176 int updated = compilationResult.program?.libraries?.length ?? 0;
177 int totalFiles = compilationResult.totalFiles;
178 int invalidateTime = compilationResult.invalidateTime;
179 int compileTime = compilationResult.compileTime;
180 int reloadTime = reloadResult.reloadTime;
181 bool errorSeen = compilationResult.errorSeen || reloadResult.errorSeen;
182 String errorDetails =
183 compilationResult.errorDetails ?? reloadResult.errorDetails;
184
185 var totalTime = invalidateTime + compileTime + reloadTime;
186 timeSum += totalTime;
187 total++;
188 iter++;
189 var avgTime = (timeSum / total).truncate();
190 var size = new File.fromUri(outputUri).statSync().size;
191
192 var percent = (100 * updated / totalFiles).toStringAsFixed(0);
193 var line = '${_padl(iter, 3)}: '
194 '${_padl(changed, 8)} ${_padl(updated, 5)} ${_padl(percent, 4)}% '
195 '${_padl(invalidateTime, 5)} ms '
196 '${_padl(compileTime, 5)} ms '
197 '${_padl(reloadTime, 5)} ms '
198 '${_padl(totalTime, 5)} ms '
199 '${_padl(avgTime, 5)} ms '
200 '${_padl(size, 5)}b';
201 var len = line.length;
202 if (singleLine) stdout.write('\r');
203 stdout.write((errorSeen) ? terminal.red(line) : terminal.green(line));
204 if (!singleLine) stdout.write('\n');
205 if (errorSeen) {
206 if (!singleLine) errorDetails = ' error: $errorDetails\n';
207 len += errorDetails.length;
208 stdout.write(errorDetails);
209 }
210 if (singleLine) {
211 var diff = " " * (lastLine - len);
212 stdout.write(diff);
213 }
214 lastLine = len;
215 }
216
217 _padl(x, n) {
218 var s = '$x';
219 return ' ' * (n - s.length) + s;
220 }
221
222 /// Helper to control an ANSI terminal (adapted from flutter_tools)
223 class AnsiTerminal {
224 static const String _bold = '\u001B[1m';
225 static const String _green = '\u001B[32m';
226 static const String _red = '\u001B[31m';
227 static const String _reset = '\u001B[0m';
228 static const String _clear = '\u001B[2J\u001B[H';
229
230 static const int _ENXIO = 6;
231 static const int _ENOTTY = 25;
232 static const int _ENETRESET = 102;
233 static const int _INVALID_HANDLE = 6;
234
235 /// Setting the line mode can throw for some terminals (with "Operation not
236 /// supported on socket"), but the error can be safely ignored.
237 static const List<int> _lineModeIgnorableErrors = const <int>[
238 _ENXIO,
239 _ENOTTY,
240 _ENETRESET,
241 _INVALID_HANDLE,
242 ];
243
244 String bolden(String message) => wrap(message, _bold);
245 String green(String message) => wrap(message, _green);
246 String red(String message) => wrap(message, _red);
247
248 String wrap(String message, String escape) {
249 final StringBuffer buffer = new StringBuffer();
250 for (String line in message.split('\n'))
251 buffer.writeln('$escape$line$_reset');
252 final String result = buffer.toString();
253 // avoid introducing a new newline to the emboldened text
254 return (!message.endsWith('\n') && result.endsWith('\n'))
255 ? result.substring(0, result.length - 1)
256 : result;
257 }
258
259 String clearScreen() => _clear;
260
261 set singleCharMode(bool value) {
262 // TODO(goderbauer): instead of trying to set lineMode and then catching
263 // [_ENOTTY] or [_INVALID_HANDLE], we should check beforehand if stdin is
264 // connected to a terminal or not.
265 // (Requires https://github.com/dart-lang/sdk/issues/29083 to be resolved.)
266 try {
267 // The order of setting lineMode and echoMode is important on Windows.
268 if (value) {
269 stdin.echoMode = false;
270 stdin.lineMode = false;
271 } else {
272 stdin.lineMode = true;
273 stdin.echoMode = true;
274 }
275 } on StdinException catch (error) {
276 if (!_lineModeIgnorableErrors.contains(error.osError?.errorCode)) rethrow;
277 }
278 }
279
280 /// Return keystrokes from the console.
281 ///
282 /// Useful when the console is in [singleCharMode].
283 Stream<String> get onCharInput => stdin.transform(ASCII.decoder);
284 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698