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

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
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' show LibraryInfo, CheckerResults, LibraryUnit;
29 import 'src/options.dart'; 32 import 'src/options.dart';
30 import 'src/report.dart'; 33 import 'src/report.dart';
31 import 'src/utils.dart'; 34 import 'src/utils.dart';
32 35
33 /// Sets up the type checker logger to print a span that highlights error 36 /// Sets up the type checker logger to print a span that highlights error
34 /// messages. 37 /// messages.
35 StreamSubscription setupLogger(Level level, printFn) { 38 StreamSubscription setupLogger(Level level, printFn) {
36 Logger.root.level = level; 39 Logger.root.level = level;
37 return Logger.root.onRecord.listen((LogRecord rec) { 40 return Logger.root.onRecord.listen((LogRecord rec) {
38 printFn('${rec.level.name.toLowerCase()}: ${rec.message}'); 41 printFn('${rec.level.name.toLowerCase()}: ${rec.message}');
39 }); 42 });
40 } 43 }
41 44
45 abstract class CompilerContext {
Jennifer Messerly 2015/05/13 23:21:16 these 4 things get passed around together a lot
vsm 2015/05/14 22:02:08 "Context" is a bit overloaded here with CompilerCo
Jennifer Messerly 2015/05/14 23:42:21 yeah, the idea was to be our equivalent of Analysi
46 CompilerOptions get options;
47 AnalysisContext get context;
48 TypeRules get rules;
49 Uri get entryPointUri;
50 }
51
42 /// Encapsulates the logic to do a one-off compilation or a partial compilation 52 /// Encapsulates the logic to do a one-off compilation or a partial compilation
43 /// when the compiler is run as a development server. 53 /// when the compiler is run as a development server.
44 class Compiler { 54 class Compiler implements CompilerContext {
45 final CompilerOptions _options; 55 final CompilerOptions options;
46 final TypeResolver _resolver; 56 final AnalysisContext context;
47 final CheckerReporter _reporter; 57 final CheckerReporter _reporter;
48 final TypeRules _rules; 58 final TypeRules rules;
49 final CodeChecker _checker; 59 final CodeChecker _checker;
50 final SourceGraph _graph;
51 final SourceNode _entryNode; 60 final SourceNode _entryNode;
52 List<LibraryInfo> _libraries = <LibraryInfo>[]; 61 List<LibraryInfo> _libraries = <LibraryInfo>[];
53 final List<CodeGenerator> _generators; 62 final List _generators = <CodeGenerator>[];
vsm 2015/05/14 22:02:08 Could drop the lhs type.
Jennifer Messerly 2015/05/14 23:42:21 Done.
54 final bool _hashing; 63 bool _hashing;
55 bool _failure = false; 64 bool _failure = false;
56 65
57 factory Compiler(CompilerOptions options, 66 factory Compiler(CompilerOptions options,
58 [TypeResolver resolver, CheckerReporter reporter]) { 67 [TypeResolver resolver, CheckerReporter reporter]) {
59 if (resolver == null) { 68 if (resolver == null) {
60 resolver = options.useMockSdk 69 resolver = options.useMockSdk
61 ? new TypeResolver.fromMock(mockSdkSources, options) 70 ? new TypeResolver.fromMock(mockSdkSources, options)
62 : new TypeResolver.fromDir(options.dartSdkPath, options); 71 : new TypeResolver.fromDir(options.dartSdkPath, options);
63 } 72 }
73 var context = resolver.context;
64 74
65 if (reporter == null) { 75 if (reporter == null) {
66 reporter = options.dumpInfo 76 reporter = options.dumpInfo
67 ? new SummaryReporter() 77 ? new SummaryReporter(context)
68 : new LogReporter(options.useColors); 78 : new LogReporter(context, useColors: options.useColors);
69 } 79 }
70 var graph = new SourceGraph(resolver.context, reporter, options); 80 var graph = new SourceGraph(context, reporter, options);
71 var rules = 81 var rules = new RestrictedRules(context.typeProvider, options: options);
72 new RestrictedRules(resolver.context.typeProvider, options: options);
73 var checker = new CodeChecker(rules, reporter, options); 82 var checker = new CodeChecker(rules, reporter, options);
74 var inputFile = options.entryPointFile; 83 var inputFile = options.entryPointFile;
75 var uri = inputFile.startsWith('dart:') || inputFile.startsWith('package:') 84 var uri = inputFile.startsWith('dart:') || inputFile.startsWith('package:')
76 ? Uri.parse(inputFile) 85 ? Uri.parse(inputFile)
77 : new Uri.file(path.absolute(inputFile)); 86 : new Uri.file(path.absolute(inputFile));
78 var entryNode = graph.nodeFromUri(uri); 87 var entryNode = graph.nodeFromUri(uri);
79 88
80 var outputDir = options.outputDir; 89 return new Compiler._(
81 var generators = <CodeGenerator>[]; 90 options, context, reporter, rules, checker, entryNode);
82 if (options.dumpSrcDir != null) {
83 generators.add(new EmptyDartGenerator(
84 options.dumpSrcDir, entryNode.uri, rules, options));
85 }
86 if (outputDir != null) {
87 generators.add(options.outputDart
88 ? new DartGenerator(outputDir, entryNode.uri, rules, options)
89 : new JSGenerator(outputDir, entryNode.uri, rules, options));
90 }
91 return new Compiler._(options, resolver, reporter, rules, checker, graph,
92 entryNode, generators,
93 // TODO(sigmund): refactor to support hashing of the dart output?
94 options.enableHashing && generators.length == 1 && !options.outputDart);
95 } 91 }
96 92
97 Compiler._(this._options, this._resolver, this._reporter, this._rules, 93 Compiler._(this.options, this.context, this._reporter, this.rules,
98 this._checker, this._graph, this._entryNode, this._generators, 94 this._checker, this._entryNode) {
99 this._hashing); 95 if (options.dumpSrcDir != null) {
96 _generators.add(new EmptyDartGenerator(this));
97 }
98 if (options.outputDir != null) {
99 _generators.add(
100 options.outputDart ? new DartGenerator(this) : new JSGenerator(this));
101 }
102 // TODO(sigmund): refactor to support hashing of the dart output?
103 _hashing =
104 options.enableHashing && _generators.length == 1 && !options.outputDart;
105 }
106
107 Uri get entryPointUri => _entryNode.uri;
100 108
101 bool _buildSource(SourceNode node) { 109 bool _buildSource(SourceNode node) {
102 if (node is HtmlSourceNode) { 110 if (node is HtmlSourceNode) {
103 _buildHtmlFile(node); 111 _buildHtmlFile(node);
104 } else if (node is DartSourceNode) { 112 } else if (node is DartSourceNode) {
105 _buildDartLibrary(node); 113 _buildDartLibrary(node);
106 } else if (node is ResourceSourceNode) { 114 } else if (node is ResourceSourceNode) {
107 _buildResourceFile(node); 115 _buildResourceFile(node);
108 } else { 116 } else {
109 assert(false); // should not get a build request on PartSourceNode 117 assert(false); // should not get a build request on PartSourceNode
110 } 118 }
111 119
112 // TODO(sigmund): don't always return true. Use summarization to better 120 // TODO(sigmund): don't always return true. Use summarization to better
113 // determine when rebuilding is needed. 121 // determine when rebuilding is needed.
114 return true; 122 return true;
115 } 123 }
116 124
117 void _buildHtmlFile(HtmlSourceNode node) { 125 void _buildHtmlFile(HtmlSourceNode node) {
118 if (_options.outputDir == null) return; 126 if (options.outputDir == null) return;
119 var uri = node.source.uri; 127 var uri = node.source.uri;
120 _reporter.enterHtml(uri); 128 _reporter.enterHtml(uri);
121 var output = generateEntryHtml(node, _options); 129 var output = generateEntryHtml(node, options);
122 if (output == null) { 130 if (output == null) {
123 _failure = true; 131 _failure = true;
124 return; 132 return;
125 } 133 }
126 _reporter.leaveHtml(); 134 _reporter.leaveHtml();
127 var filename = path.basename(node.uri.path); 135 var filename = path.basename(node.uri.path);
128 String outputFile = path.join(_options.outputDir, filename); 136 String outputFile = path.join(options.outputDir, filename);
129 new File(outputFile).writeAsStringSync(output); 137 new File(outputFile).writeAsStringSync(output);
130 138
131 if (_options.outputDart) return; 139 if (options.outputDart) return;
132 } 140 }
133 141
134 void _buildResourceFile(ResourceSourceNode node) { 142 void _buildResourceFile(ResourceSourceNode node) {
135 // ResourceSourceNodes files that just need to be copied over to the output 143 // ResourceSourceNodes files that just need to be copied over to the output
136 // location. These can be external dependencies or pieces of the 144 // location. These can be external dependencies or pieces of the
137 // dev_compiler runtime. 145 // dev_compiler runtime.
138 if (_options.outputDir == null || _options.outputDart) return; 146 if (options.outputDir == null || options.outputDart) return;
139 var filepath = resourceOutputPath(node.uri, _entryNode.uri); 147 var filepath = resourceOutputPath(node.uri, _entryNode.uri);
140 assert(filepath != null); 148 assert(filepath != null);
141 filepath = path.join(_options.outputDir, filepath); 149 filepath = path.join(options.outputDir, filepath);
142 var dir = path.dirname(filepath); 150 var dir = path.dirname(filepath);
143 new Directory(dir).createSync(recursive: true); 151 new Directory(dir).createSync(recursive: true);
144 new File.fromUri(node.source.uri).copySync(filepath); 152 new File.fromUri(node.source.uri).copySync(filepath);
145 if (_hashing) node.cachingHash = computeHashFromFile(filepath); 153 if (_hashing) node.cachingHash = computeHashFromFile(filepath);
146 } 154 }
147 155
148 bool _isEntry(DartSourceNode node) { 156 bool _isEntry(DartSourceNode node) {
149 if (_entryNode is DartSourceNode) return _entryNode == node; 157 if (_entryNode is DartSourceNode) return _entryNode == node;
150 return (_entryNode as HtmlSourceNode).scripts.contains(node); 158 return (_entryNode as HtmlSourceNode).scripts.contains(node);
151 } 159 }
152 160
153 void _buildDartLibrary(DartSourceNode node) { 161 void _buildDartLibrary(DartSourceNode node) {
154 var source = node.source; 162 var source = node.source;
155 // TODO(sigmund): find out from analyzer team if there is a better way 163 // TODO(sigmund): find out from analyzer team if there is a better way
156 _resolver.context.applyChanges(new ChangeSet()..changedSource(source)); 164 context.applyChanges(new ChangeSet()..changedSource(source));
157 var entryUnit = _resolver.context.resolveCompilationUnit2(source, source); 165 var entryUnit = context.resolveCompilationUnit2(source, source);
158 var lib = entryUnit.element.enclosingElement; 166 var lib = entryUnit.element.enclosingElement;
159 if (!_options.checkSdk && lib.isInSdk) return; 167 if (!options.checkSdk && lib.isInSdk) return;
160 var current = node.info; 168 var current = node.info;
161 if (current != null) { 169 if (current != null) {
162 assert(current.library == lib); 170 assert(current.library == lib);
163 } else { 171 } else {
164 node.info = current = new LibraryInfo(lib, _isEntry(node)); 172 node.info = current = new LibraryInfo(lib, _isEntry(node));
165 } 173 }
166 _reporter.enterLibrary(source.uri); 174 _reporter.enterLibrary(source.uri);
167 _libraries.add(current); 175 _libraries.add(current);
168 _rules.currentLibraryInfo = current; 176 rules.currentLibraryInfo = current;
169 177
170 var resolvedParts = node.parts 178 var resolvedParts = node.parts
171 .map((p) => _resolver.context.resolveCompilationUnit2(p.source, source)) 179 .map((p) => context.resolveCompilationUnit2(p.source, source))
172 .toList(growable: false); 180 .toList(growable: false);
173 var libraryUnit = new LibraryUnit(entryUnit, resolvedParts); 181 var libraryUnit = new LibraryUnit(entryUnit, resolvedParts);
174 bool failureInLib = false; 182 bool failureInLib = false;
175 for (var unit in libraryUnit.libraryThenParts) { 183 for (var unit in libraryUnit.libraryThenParts) {
176 var unitSource = unit.element.source; 184 var unitSource = unit.element.source;
177 _reporter.enterSource(unitSource); 185 _reporter.enterCompilationUnit(unit);
178 // TODO(sigmund): integrate analyzer errors with static-info (issue #6). 186 // TODO(sigmund): integrate analyzer errors with static-info (issue #6).
179 failureInLib = _resolver.logErrors(unitSource, _reporter) || failureInLib; 187 failureInLib = logErrors(unitSource) || failureInLib;
180 _checker.visitCompilationUnit(unit); 188 _checker.visitCompilationUnit(unit);
181 if (_checker.failure) failureInLib = true; 189 if (_checker.failure) failureInLib = true;
182 _reporter.leaveSource(); 190 _reporter.leaveCompilationUnit();
183 } 191 }
184 if (failureInLib) { 192 if (failureInLib) {
185 _failure = true; 193 _failure = true;
186 if (!_options.forceCompile) return; 194 if (!options.forceCompile) return;
187 } 195 }
188 196
189 for (var cg in _generators) { 197 for (var cg in _generators) {
190 var hash = cg.generateLibrary(libraryUnit, current); 198 var hash = cg.generateLibrary(libraryUnit, current);
191 if (_hashing) node.cachingHash = hash; 199 if (_hashing) node.cachingHash = hash;
192 } 200 }
193 _reporter.leaveLibrary(); 201 _reporter.leaveLibrary();
194 } 202 }
195 203
204 /// Log any errors encountered when resolving [source] and return whether any
205 /// errors were found.
206 bool logErrors(Source source) {
Jennifer Messerly 2015/05/13 23:21:16 moved from "TypeResolver"
207 List<analyzer.AnalysisError> errors = context.getErrors(source).errors;
208 bool failure = false;
209 if (errors.isNotEmpty) {
210 for (var error in errors) {
211 var message = new AnalyzerError.from(error);
212 if (message.level == Level.SEVERE) failure = true;
213 _reporter.log(message);
214 }
215 }
216 return failure;
217 }
218
196 CheckerResults run() { 219 CheckerResults run() {
197 var clock = new Stopwatch()..start(); 220 var clock = new Stopwatch()..start();
198 221
199 // TODO(sigmund): we are missing a couple failures here. The 222 // TODO(sigmund): we are missing a couple failures here. The
200 // dependency_graph now detects broken imports or unsupported features 223 // dependency_graph now detects broken imports or unsupported features
201 // like more than one script tag (see .severe messages in 224 // like more than one script tag (see .severe messages in
202 // dependency_graph.dart). Such failures should be reported back 225 // dependency_graph.dart). Such failures should be reported back
203 // here so we can mark failure=true in the CheckerResutls. 226 // here so we can mark failure=true in the CheckerResutls.
204 rebuild(_entryNode, _graph, _buildSource); 227 rebuild(_entryNode, _buildSource);
205 _dumpInfoIfRequested(); 228 _dumpInfoIfRequested();
206 clock.stop(); 229 clock.stop();
207 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2); 230 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2);
208 _log.fine('Compiled ${_libraries.length} libraries in ${time} s\n'); 231 _log.fine('Compiled ${_libraries.length} libraries in ${time} s\n');
209 return new CheckerResults( 232 return new CheckerResults(
210 _libraries, _rules, _failure || _options.forceCompile); 233 _libraries, rules, _failure || options.forceCompile);
211 } 234 }
212 235
213 void _runAgain() { 236 void _runAgain() {
214 var clock = new Stopwatch()..start(); 237 var clock = new Stopwatch()..start();
215 _libraries = <LibraryInfo>[]; 238 _libraries = <LibraryInfo>[];
216 int changed = 0; 239 int changed = 0;
217 240
218 // TODO(sigmund): propagate failures here (see TODO in run). 241 // TODO(sigmund): propagate failures here (see TODO in run).
219 rebuild(_entryNode, _graph, (n) { 242 rebuild(_entryNode, (n) {
220 changed++; 243 changed++;
221 return _buildSource(n); 244 return _buildSource(n);
222 }); 245 });
223 clock.stop(); 246 clock.stop();
224 if (changed > 0) _dumpInfoIfRequested(); 247 if (changed > 0) _dumpInfoIfRequested();
225 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2); 248 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2);
226 _log.fine("Compiled ${changed} libraries in ${time} s\n"); 249 _log.fine("Compiled ${changed} libraries in ${time} s\n");
227 } 250 }
228 251
229 _dumpInfoIfRequested() { 252 _dumpInfoIfRequested() {
230 if (!_options.dumpInfo || _reporter is! SummaryReporter) return; 253 if (!options.dumpInfo || _reporter is! SummaryReporter) return;
231 var result = (_reporter as SummaryReporter).result; 254 var result = (_reporter as SummaryReporter).result;
232 if (!_options.serverMode) print(summaryToString(result)); 255 if (!options.serverMode) print(summaryToString(result));
233 var filepath = _options.serverMode 256 var filepath = options.serverMode
234 ? path.join(_options.outputDir, 'messages.json') 257 ? path.join(options.outputDir, 'messages.json')
235 : _options.dumpInfoFile; 258 : options.dumpInfoFile;
236 if (filepath == null) return; 259 if (filepath == null) return;
237 new File(filepath).writeAsStringSync(JSON.encode(result.toJsonMap())); 260 new File(filepath).writeAsStringSync(JSON.encode(result.toJsonMap()));
238 } 261 }
239 } 262 }
240 263
241 class CompilerServer { 264 class CompilerServer {
242 final Compiler compiler; 265 final Compiler compiler;
243 final String outDir; 266 final String outDir;
244 final String host; 267 final String host;
245 final int port; 268 final int port;
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
306 // Note: the cache-control header should be enough, but this doesn't hurt 329 // Note: the cache-control header should be enough, but this doesn't hurt
307 // and can help renew the policy after it expires. 330 // and can help renew the policy after it expires.
308 headers['ETag'] = segments[1]; 331 headers['ETag'] = segments[1];
309 } 332 }
310 return response.change(headers: headers); 333 return response.change(headers: headers);
311 }; 334 };
312 } 335 }
313 336
314 final _log = new Logger('dev_compiler'); 337 final _log = new Logger('dev_compiler');
315 final _earlyErrorResult = new CheckerResults(const [], null, true); 338 final _earlyErrorResult = new CheckerResults(const [], null, true);
339
340 class AnalyzerError extends Message {
vsm 2015/05/14 22:02:08 Perhaps this would fit better in info.dart where w
Jennifer Messerly 2015/05/14 23:42:21 good idea, done!
341 factory AnalyzerError.from(analyzer.AnalysisError error) {
342 var severity = error.errorCode.type.severity;
343 var isError = severity == analyzer.ErrorSeverity.ERROR;
344 var level = isError ? Level.SEVERE : Level.WARNING;
345 int begin = error.offset;
346 int end = begin + error.length;
347 return new AnalyzerError(error.message, level, begin, end);
348 }
349
350 const AnalyzerError(String message, Level level, int begin, int end)
351 : super('[from analyzer]: $message', level, begin, end);
352 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698