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

Side by Side Diff: lib/devc.dart

Issue 1141013002: Fixes #179 -- compile error if editing files during server mode (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 7 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
« no previous file with comments | « bin/edit_files.dart ('k') | lib/src/checker/resolver.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 /// Command line tool to run the checker on a Dart program. 5 /// Command line tool to run the checker on a Dart program.
6 library dev_compiler.devc; 6 library dev_compiler.devc;
7 7
8 import 'dart:async'; 8 import 'dart:async';
9 import 'dart:convert'; 9 import 'dart:convert';
10 import 'dart:io'; 10 import 'dart:io';
11 11
12 import 'package:analyzer/src/generated/engine.dart' show ChangeSet; 12 import 'package:analyzer/src/generated/error.dart' as analyzer;
13 import 'package:analyzer/src/generated/engine.dart'
14 show AnalysisContext, ChangeSet;
15 import 'package:analyzer/src/generated/source.dart' show Source;
13 import 'package:logging/logging.dart' show Level, Logger, LogRecord; 16 import 'package:logging/logging.dart' show Level, Logger, LogRecord;
14 import 'package:path/path.dart' as path; 17 import 'package:path/path.dart' as path;
15 import 'package:shelf/shelf.dart' as shelf; 18 import 'package:shelf/shelf.dart' as shelf;
16 import 'package:shelf/shelf_io.dart' as shelf; 19 import 'package:shelf/shelf_io.dart' as shelf;
17 import 'package:shelf_static/shelf_static.dart' as shelf_static; 20 import 'package:shelf_static/shelf_static.dart' as shelf_static;
18 21
19 import 'src/checker/checker.dart'; 22 import 'src/checker/checker.dart';
20 import 'src/checker/dart_sdk.dart' show mockSdkSources; 23 import 'src/checker/dart_sdk.dart' show mockSdkSources;
21 import 'src/checker/resolver.dart'; 24 import 'src/checker/resolver.dart';
22 import 'src/checker/rules.dart'; 25 import 'src/checker/rules.dart';
23 import 'src/codegen/code_generator.dart' show CodeGenerator; 26 import 'src/codegen/code_generator.dart' show CodeGenerator;
24 import 'src/codegen/dart_codegen.dart'; 27 import 'src/codegen/dart_codegen.dart';
25 import 'src/codegen/html_codegen.dart'; 28 import 'src/codegen/html_codegen.dart';
26 import 'src/codegen/js_codegen.dart'; 29 import 'src/codegen/js_codegen.dart';
27 import 'src/dependency_graph.dart'; 30 import 'src/dependency_graph.dart';
28 import 'src/info.dart' show LibraryInfo, CheckerResults, LibraryUnit; 31 import 'src/info.dart'
32 show AnalyzerError, CheckerResults, LibraryInfo, LibraryUnit;
29 import 'src/options.dart'; 33 import 'src/options.dart';
30 import 'src/report.dart'; 34 import 'src/report.dart';
31 import 'src/utils.dart'; 35 import 'src/utils.dart';
32 36
33 /// Sets up the type checker logger to print a span that highlights error 37 /// Sets up the type checker logger to print a span that highlights error
34 /// messages. 38 /// messages.
35 StreamSubscription setupLogger(Level level, printFn) { 39 StreamSubscription setupLogger(Level level, printFn) {
36 Logger.root.level = level; 40 Logger.root.level = level;
37 return Logger.root.onRecord.listen((LogRecord rec) { 41 return Logger.root.onRecord.listen((LogRecord rec) {
38 printFn('${rec.level.name.toLowerCase()}: ${rec.message}'); 42 printFn('${rec.level.name.toLowerCase()}: ${rec.message}');
39 }); 43 });
40 } 44 }
41 45
46 abstract class AbstractCompiler {
47 CompilerOptions get options;
48 AnalysisContext get context;
49 TypeRules get rules;
50 Uri get entryPointUri;
51 }
52
42 /// Encapsulates the logic to do a one-off compilation or a partial compilation 53 /// Encapsulates the logic to do a one-off compilation or a partial compilation
43 /// when the compiler is run as a development server. 54 /// when the compiler is run as a development server.
44 class Compiler { 55 class Compiler implements AbstractCompiler {
45 final CompilerOptions _options; 56 final CompilerOptions options;
46 final TypeResolver _resolver; 57 final AnalysisContext context;
47 final CheckerReporter _reporter; 58 final CheckerReporter _reporter;
48 final TypeRules _rules; 59 final TypeRules rules;
49 final CodeChecker _checker; 60 final CodeChecker _checker;
50 final SourceGraph _graph;
51 final SourceNode _entryNode; 61 final SourceNode _entryNode;
52 List<LibraryInfo> _libraries = <LibraryInfo>[]; 62 List<LibraryInfo> _libraries = <LibraryInfo>[];
53 final List<CodeGenerator> _generators; 63 final _generators = <CodeGenerator>[];
54 final bool _hashing; 64 bool _hashing;
55 bool _failure = false; 65 bool _failure = false;
56 66
57 factory Compiler(CompilerOptions options, 67 factory Compiler(CompilerOptions options,
58 {TypeResolver resolver, CheckerReporter reporter}) { 68 {TypeResolver resolver, CheckerReporter reporter}) {
59 if (resolver == null) { 69 if (resolver == null) {
60 resolver = options.useMockSdk 70 resolver = options.useMockSdk
61 ? new TypeResolver.fromMock(mockSdkSources, options) 71 ? new TypeResolver.fromMock(mockSdkSources, options)
62 : new TypeResolver.fromDir(options.dartSdkPath, options); 72 : new TypeResolver.fromDir(options.dartSdkPath, options);
63 } 73 }
74 var context = resolver.context;
64 75
65 if (reporter == null) { 76 if (reporter == null) {
66 reporter = options.dumpInfo 77 reporter = options.dumpInfo
67 ? new SummaryReporter(options.logLevel) 78 ? new SummaryReporter(context, options.logLevel)
68 : new LogReporter(options.useColors); 79 : new LogReporter(context, useColors: options.useColors);
69 } 80 }
70 var graph = new SourceGraph(resolver.context, reporter, options); 81 var graph = new SourceGraph(context, reporter, options);
71 var rules = 82 var rules = new RestrictedRules(context.typeProvider, options: options);
72 new RestrictedRules(resolver.context.typeProvider, options: options);
73 var checker = new CodeChecker(rules, reporter, options); 83 var checker = new CodeChecker(rules, reporter, options);
74 84
75 var inputFile = options.entryPointFile; 85 var inputFile = options.entryPointFile;
76 var inputUri = inputFile.startsWith('dart:') || 86 var inputUri = inputFile.startsWith('dart:') ||
77 inputFile.startsWith('package:') 87 inputFile.startsWith('package:')
78 ? Uri.parse(inputFile) 88 ? Uri.parse(inputFile)
79 : new Uri.file(path.absolute(options.useImplicitHtml 89 : new Uri.file(path.absolute(options.useImplicitHtml
80 ? ResolverOptions.implicitHtmlFile 90 ? ResolverOptions.implicitHtmlFile
81 : inputFile)); 91 : inputFile));
82 var entryNode = graph.nodeFromUri(inputUri); 92 var entryNode = graph.nodeFromUri(inputUri);
83 var outputDir = options.outputDir; 93
84 var generators = <CodeGenerator>[]; 94 return new Compiler._(
85 if (options.dumpSrcDir != null) { 95 options, context, reporter, rules, checker, entryNode);
86 generators.add(new EmptyDartGenerator(
87 options.dumpSrcDir, entryNode.uri, rules, options));
88 }
89 if (outputDir != null) {
90 generators.add(options.outputDart
91 ? new DartGenerator(outputDir, entryNode.uri, rules, options)
92 : new JSGenerator(outputDir, entryNode.uri, rules, options));
93 }
94 return new Compiler._(options, resolver, reporter, rules, checker, graph,
95 entryNode, generators,
96 // TODO(sigmund): refactor to support hashing of the dart output?
97 options.enableHashing && generators.length == 1 && !options.outputDart);
98 } 96 }
99 97
100 Compiler._(this._options, this._resolver, this._reporter, this._rules, 98 Compiler._(this.options, this.context, this._reporter, this.rules,
101 this._checker, this._graph, this._entryNode, this._generators, 99 this._checker, this._entryNode) {
102 this._hashing); 100 if (options.dumpSrcDir != null) {
101 _generators.add(new EmptyDartGenerator(this));
102 }
103 if (options.outputDir != null) {
104 _generators.add(
105 options.outputDart ? new DartGenerator(this) : new JSGenerator(this));
106 }
107 // TODO(sigmund): refactor to support hashing of the dart output?
108 _hashing =
109 options.enableHashing && _generators.length == 1 && !options.outputDart;
110 }
111
112 Uri get entryPointUri => _entryNode.uri;
103 113
104 bool _buildSource(SourceNode node) { 114 bool _buildSource(SourceNode node) {
105 if (node is HtmlSourceNode) { 115 if (node is HtmlSourceNode) {
106 _buildHtmlFile(node); 116 _buildHtmlFile(node);
107 } else if (node is DartSourceNode) { 117 } else if (node is DartSourceNode) {
108 _buildDartLibrary(node); 118 _buildDartLibrary(node);
109 } else if (node is ResourceSourceNode) { 119 } else if (node is ResourceSourceNode) {
110 _buildResourceFile(node); 120 _buildResourceFile(node);
111 } else { 121 } else {
112 assert(false); // should not get a build request on PartSourceNode 122 assert(false); // should not get a build request on PartSourceNode
113 } 123 }
114 124
115 // TODO(sigmund): don't always return true. Use summarization to better 125 // TODO(sigmund): don't always return true. Use summarization to better
116 // determine when rebuilding is needed. 126 // determine when rebuilding is needed.
117 return true; 127 return true;
118 } 128 }
119 129
120 void _buildHtmlFile(HtmlSourceNode node) { 130 void _buildHtmlFile(HtmlSourceNode node) {
121 if (_options.outputDir == null) return; 131 if (options.outputDir == null) return;
122 var uri = node.source.uri; 132 var uri = node.source.uri;
123 _reporter.enterHtml(uri); 133 _reporter.enterHtml(uri);
124 var output = generateEntryHtml(node, _options); 134 var output = generateEntryHtml(node, options);
125 if (output == null) { 135 if (output == null) {
126 _failure = true; 136 _failure = true;
127 return; 137 return;
128 } 138 }
129 _reporter.leaveHtml(); 139 _reporter.leaveHtml();
130 var filename = path.basename(node.uri.path); 140 var filename = path.basename(node.uri.path);
131 String outputFile = path.join(_options.outputDir, filename); 141 String outputFile = path.join(options.outputDir, filename);
132 new File(outputFile).writeAsStringSync(output); 142 new File(outputFile).writeAsStringSync(output);
133 143
134 if (_options.outputDart) return; 144 if (options.outputDart) return;
135 } 145 }
136 146
137 void _buildResourceFile(ResourceSourceNode node) { 147 void _buildResourceFile(ResourceSourceNode node) {
138 // ResourceSourceNodes files that just need to be copied over to the output 148 // ResourceSourceNodes files that just need to be copied over to the output
139 // location. These can be external dependencies or pieces of the 149 // location. These can be external dependencies or pieces of the
140 // dev_compiler runtime. 150 // dev_compiler runtime.
141 if (_options.outputDir == null || _options.outputDart) return; 151 if (options.outputDir == null || options.outputDart) return;
142 var filepath = resourceOutputPath(node.uri, _entryNode.uri); 152 var filepath = resourceOutputPath(node.uri, _entryNode.uri);
143 assert(filepath != null); 153 assert(filepath != null);
144 filepath = path.join(_options.outputDir, filepath); 154 filepath = path.join(options.outputDir, filepath);
145 var dir = path.dirname(filepath); 155 var dir = path.dirname(filepath);
146 new Directory(dir).createSync(recursive: true); 156 new Directory(dir).createSync(recursive: true);
147 new File.fromUri(node.source.uri).copySync(filepath); 157 new File.fromUri(node.source.uri).copySync(filepath);
148 if (_hashing) node.cachingHash = computeHashFromFile(filepath); 158 if (_hashing) node.cachingHash = computeHashFromFile(filepath);
149 } 159 }
150 160
151 bool _isEntry(DartSourceNode node) { 161 bool _isEntry(DartSourceNode node) {
152 if (_entryNode is DartSourceNode) return _entryNode == node; 162 if (_entryNode is DartSourceNode) return _entryNode == node;
153 return (_entryNode as HtmlSourceNode).scripts.contains(node); 163 return (_entryNode as HtmlSourceNode).scripts.contains(node);
154 } 164 }
155 165
156 void _buildDartLibrary(DartSourceNode node) { 166 void _buildDartLibrary(DartSourceNode node) {
157 var source = node.source; 167 var source = node.source;
158 // TODO(sigmund): find out from analyzer team if there is a better way 168 // TODO(sigmund): find out from analyzer team if there is a better way
159 _resolver.context.applyChanges(new ChangeSet()..changedSource(source)); 169 context.applyChanges(new ChangeSet()..changedSource(source));
160 var entryUnit = _resolver.context.resolveCompilationUnit2(source, source); 170 var entryUnit = context.resolveCompilationUnit2(source, source);
161 var lib = entryUnit.element.enclosingElement; 171 var lib = entryUnit.element.enclosingElement;
162 if (!_options.checkSdk && lib.isInSdk) return; 172 if (!options.checkSdk && lib.isInSdk) return;
163 var current = node.info; 173 var current = node.info;
164 if (current != null) { 174 if (current != null) {
165 assert(current.library == lib); 175 assert(current.library == lib);
166 } else { 176 } else {
167 node.info = current = new LibraryInfo(lib, _isEntry(node)); 177 node.info = current = new LibraryInfo(lib, _isEntry(node));
168 } 178 }
169 _reporter.enterLibrary(source.uri); 179 _reporter.enterLibrary(source.uri);
170 _libraries.add(current); 180 _libraries.add(current);
171 _rules.currentLibraryInfo = current; 181 rules.currentLibraryInfo = current;
172 182
173 var resolvedParts = node.parts 183 var resolvedParts = node.parts
174 .map((p) => _resolver.context.resolveCompilationUnit2(p.source, source)) 184 .map((p) => context.resolveCompilationUnit2(p.source, source))
175 .toList(growable: false); 185 .toList(growable: false);
176 var libraryUnit = new LibraryUnit(entryUnit, resolvedParts); 186 var libraryUnit = new LibraryUnit(entryUnit, resolvedParts);
177 bool failureInLib = false; 187 bool failureInLib = false;
178 for (var unit in libraryUnit.libraryThenParts) { 188 for (var unit in libraryUnit.libraryThenParts) {
179 var unitSource = unit.element.source; 189 var unitSource = unit.element.source;
180 _reporter.enterSource(unitSource); 190 _reporter.enterCompilationUnit(unit);
181 // TODO(sigmund): integrate analyzer errors with static-info (issue #6). 191 // TODO(sigmund): integrate analyzer errors with static-info (issue #6).
182 failureInLib = _resolver.logErrors(unitSource, _reporter) || failureInLib; 192 failureInLib = logErrors(unitSource) || failureInLib;
183 _checker.visitCompilationUnit(unit); 193 _checker.visitCompilationUnit(unit);
184 if (_checker.failure) failureInLib = true; 194 if (_checker.failure) failureInLib = true;
185 _reporter.leaveSource(); 195 _reporter.leaveCompilationUnit();
186 } 196 }
187 if (failureInLib) { 197 if (failureInLib) {
188 _failure = true; 198 _failure = true;
189 if (!_options.forceCompile) return; 199 if (!options.forceCompile) return;
190 } 200 }
191 201
192 for (var cg in _generators) { 202 for (var cg in _generators) {
193 var hash = cg.generateLibrary(libraryUnit, current); 203 var hash = cg.generateLibrary(libraryUnit, current);
194 if (_hashing) node.cachingHash = hash; 204 if (_hashing) node.cachingHash = hash;
195 } 205 }
196 _reporter.leaveLibrary(); 206 _reporter.leaveLibrary();
197 } 207 }
198 208
209 /// Log any errors encountered when resolving [source] and return whether any
210 /// errors were found.
211 bool logErrors(Source source) {
212 List<analyzer.AnalysisError> errors = context.getErrors(source).errors;
213 bool failure = false;
214 if (errors.isNotEmpty) {
215 for (var error in errors) {
216 var message = new AnalyzerError.from(error);
217 if (message.level == Level.SEVERE) failure = true;
218 _reporter.log(message);
219 }
220 }
221 return failure;
222 }
223
199 CheckerResults run() { 224 CheckerResults run() {
200 var clock = new Stopwatch()..start(); 225 var clock = new Stopwatch()..start();
201 226
202 // TODO(sigmund): we are missing a couple failures here. The 227 // TODO(sigmund): we are missing a couple failures here. The
203 // dependency_graph now detects broken imports or unsupported features 228 // dependency_graph now detects broken imports or unsupported features
204 // like more than one script tag (see .severe messages in 229 // like more than one script tag (see .severe messages in
205 // dependency_graph.dart). Such failures should be reported back 230 // dependency_graph.dart). Such failures should be reported back
206 // here so we can mark failure=true in the CheckerResutls. 231 // here so we can mark failure=true in the CheckerResutls.
207 rebuild(_entryNode, _graph, _buildSource); 232 rebuild(_entryNode, _buildSource);
208 _dumpInfoIfRequested(); 233 _dumpInfoIfRequested();
209 clock.stop(); 234 clock.stop();
210 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2); 235 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2);
211 _log.fine('Compiled ${_libraries.length} libraries in ${time} s\n'); 236 _log.fine('Compiled ${_libraries.length} libraries in ${time} s\n');
212 return new CheckerResults( 237 return new CheckerResults(
213 _libraries, _rules, _failure || _options.forceCompile); 238 _libraries, rules, _failure || options.forceCompile);
214 } 239 }
215 240
216 void _runAgain() { 241 void _runAgain() {
217 var clock = new Stopwatch()..start(); 242 var clock = new Stopwatch()..start();
218 _libraries = <LibraryInfo>[]; 243 _libraries = <LibraryInfo>[];
219 int changed = 0; 244 int changed = 0;
220 245
221 // TODO(sigmund): propagate failures here (see TODO in run). 246 // TODO(sigmund): propagate failures here (see TODO in run).
222 rebuild(_entryNode, _graph, (n) { 247 rebuild(_entryNode, (n) {
223 changed++; 248 changed++;
224 return _buildSource(n); 249 return _buildSource(n);
225 }); 250 });
226 clock.stop(); 251 clock.stop();
227 if (changed > 0) _dumpInfoIfRequested(); 252 if (changed > 0) _dumpInfoIfRequested();
228 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2); 253 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2);
229 _log.fine("Compiled ${changed} libraries in ${time} s\n"); 254 _log.fine("Compiled ${changed} libraries in ${time} s\n");
230 } 255 }
231 256
232 _dumpInfoIfRequested() { 257 _dumpInfoIfRequested() {
233 if (!_options.dumpInfo || _reporter is! SummaryReporter) return; 258 if (!options.dumpInfo || _reporter is! SummaryReporter) return;
234 var result = (_reporter as SummaryReporter).result; 259 var result = (_reporter as SummaryReporter).result;
235 if (!_options.serverMode) print(summaryToString(result)); 260 if (!options.serverMode) print(summaryToString(result));
236 var filepath = _options.serverMode 261 var filepath = options.serverMode
237 ? path.join(_options.outputDir, 'messages.json') 262 ? path.join(options.outputDir, 'messages.json')
238 : _options.dumpInfoFile; 263 : options.dumpInfoFile;
239 if (filepath == null) return; 264 if (filepath == null) return;
240 new File(filepath).writeAsStringSync(JSON.encode(result.toJsonMap())); 265 new File(filepath).writeAsStringSync(JSON.encode(result.toJsonMap()));
241 } 266 }
242 } 267 }
243 268
244 class CompilerServer { 269 class CompilerServer {
245 final Compiler compiler; 270 final Compiler compiler;
246 final String outDir; 271 final String outDir;
247 final String host; 272 final String host;
248 final int port; 273 final int port;
(...skipping 16 matching lines...) Expand all
265 } 290 }
266 var port = options.port; 291 var port = options.port;
267 var host = options.host; 292 var host = options.host;
268 var compiler = new Compiler(options); 293 var compiler = new Compiler(options);
269 return new CompilerServer._(compiler, outDir, host, port, entryPath); 294 return new CompilerServer._(compiler, outDir, host, port, entryPath);
270 } 295 }
271 296
272 CompilerServer._( 297 CompilerServer._(
273 Compiler compiler, this.outDir, this.host, this.port, String entryPath) 298 Compiler compiler, this.outDir, this.host, this.port, String entryPath)
274 : this.compiler = compiler, 299 : this.compiler = compiler,
275 this._entryPath = compiler._options.useImplicitHtml 300 this._entryPath = compiler.options.useImplicitHtml
276 ? ResolverOptions.implicitHtmlFile 301 ? ResolverOptions.implicitHtmlFile
277 : entryPath; 302 : entryPath;
278 303
279 Future start() async { 304 Future start() async {
280 // Create output directory if needed. shelf_static will fail otherwise. 305 // Create output directory if needed. shelf_static will fail otherwise.
281 var out = new Directory(outDir); 306 var out = new Directory(outDir);
282 if (!await out.exists()) await out.create(recursive: true); 307 if (!await out.exists()) await out.create(recursive: true);
283 308
284 var handler = const shelf.Pipeline() 309 var handler = const shelf.Pipeline()
285 .addMiddleware(rebuildAndCache) 310 .addMiddleware(rebuildAndCache)
(...skipping 22 matching lines...) Expand all
308 // Note: the cache-control header should be enough, but this doesn't hurt 333 // Note: the cache-control header should be enough, but this doesn't hurt
309 // and can help renew the policy after it expires. 334 // and can help renew the policy after it expires.
310 headers['ETag'] = hash; 335 headers['ETag'] = hash;
311 } 336 }
312 return response.change(headers: headers); 337 return response.change(headers: headers);
313 }; 338 };
314 } 339 }
315 340
316 final _log = new Logger('dev_compiler'); 341 final _log = new Logger('dev_compiler');
317 final _earlyErrorResult = new CheckerResults(const [], null, true); 342 final _earlyErrorResult = new CheckerResults(const [], null, true);
OLDNEW
« no previous file with comments | « bin/edit_files.dart ('k') | lib/src/checker/resolver.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698