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 /// 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'; |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
46 AnalysisContext get context; | 46 AnalysisContext get context; |
47 TypeRules get rules; | 47 TypeRules get rules; |
48 Uri get entryPointUri; | 48 Uri get entryPointUri; |
49 } | 49 } |
50 | 50 |
51 /// Encapsulates the logic to do a one-off compilation or a partial compilation | 51 /// Encapsulates the logic to do a one-off compilation or a partial compilation |
52 /// when the compiler is run as a development server. | 52 /// when the compiler is run as a development server. |
53 class Compiler implements AbstractCompiler { | 53 class Compiler implements AbstractCompiler { |
54 final CompilerOptions options; | 54 final CompilerOptions options; |
55 final AnalysisContext context; | 55 final AnalysisContext context; |
56 final CheckerReporter _reporter; | 56 final CompilerReporter _reporter; |
57 final TypeRules rules; | 57 final TypeRules rules; |
58 final CodeChecker _checker; | 58 final CodeChecker _checker; |
59 final SourceNode _entryNode; | 59 final SourceNode _entryNode; |
60 List<LibraryInfo> _libraries = <LibraryInfo>[]; | 60 List<LibraryInfo> _libraries = <LibraryInfo>[]; |
61 final _generators = <CodeGenerator>[]; | 61 final _generators = <CodeGenerator>[]; |
62 bool _hashing; | 62 bool _hashing; |
63 bool _failure = false; | 63 bool _failure = false; |
64 | 64 |
65 factory Compiler(CompilerOptions options, | 65 factory Compiler(CompilerOptions options, |
66 {AnalysisContext context, CheckerReporter reporter}) { | 66 {AnalysisContext context, CompilerReporter reporter}) { |
67 if (context == null) context = createAnalysisContext(options); | 67 var strongOpts = options.strongOptions; |
| 68 var sourceOpts = options.sourceOptions; |
| 69 if (context == null) { |
| 70 context = createAnalysisContextWithSources(strongOpts, sourceOpts); |
| 71 } |
68 | 72 |
69 if (reporter == null) { | 73 if (reporter == null) { |
70 reporter = options.dumpInfo | 74 reporter = options.dumpInfo |
71 ? new SummaryReporter(context, options.logLevel) | 75 ? new SummaryReporter(context, options.logLevel) |
72 : new LogReporter(context, useColors: options.useColors); | 76 : new LogReporter(context, useColors: options.useColors); |
73 } | 77 } |
74 var graph = new SourceGraph(context, reporter, options); | 78 var graph = new SourceGraph(context, reporter, options); |
75 var rules = new RestrictedRules(context.typeProvider, options: options); | 79 var rules = new RestrictedRules(context.typeProvider, |
76 var checker = new CodeChecker(rules, reporter, options); | 80 options: options.strongOptions); |
| 81 var checker = new CodeChecker(rules, reporter, strongOpts); |
77 | 82 |
78 var inputFile = options.entryPointFile; | 83 var inputFile = sourceOpts.entryPointFile; |
79 var inputUri = inputFile.startsWith('dart:') || | 84 var inputUri = inputFile.startsWith('dart:') || |
80 inputFile.startsWith('package:') | 85 inputFile.startsWith('package:') |
81 ? Uri.parse(inputFile) | 86 ? Uri.parse(inputFile) |
82 : new Uri.file(path.absolute(options.useImplicitHtml | 87 : new Uri.file(path.absolute(sourceOpts.useImplicitHtml |
83 ? ResolverOptions.implicitHtmlFile | 88 ? SourceResolverOptions.implicitHtmlFile |
84 : inputFile)); | 89 : inputFile)); |
85 var entryNode = graph.nodeFromUri(inputUri); | 90 var entryNode = graph.nodeFromUri(inputUri); |
86 | 91 |
87 return new Compiler._( | 92 return new Compiler._( |
88 options, context, reporter, rules, checker, entryNode); | 93 options, context, reporter, rules, checker, entryNode); |
89 } | 94 } |
90 | 95 |
91 Compiler._(this.options, this.context, this._reporter, this.rules, | 96 Compiler._(this.options, this.context, this._reporter, this.rules, |
92 this._checker, this._entryNode) { | 97 this._checker, this._entryNode) { |
93 if (options.outputDir != null) { | 98 if (outputDir != null) { |
94 _generators.add(new JSGenerator(this)); | 99 _generators.add(new JSGenerator(this)); |
95 } | 100 } |
96 // TODO(sigmund): refactor to support hashing of the dart output? | 101 // TODO(sigmund): refactor to support hashing of the dart output? |
97 _hashing = options.enableHashing && _generators.length == 1; | 102 _hashing = options.enableHashing && _generators.length == 1; |
98 } | 103 } |
99 | 104 |
100 Uri get entryPointUri => _entryNode.uri; | 105 Uri get entryPointUri => _entryNode.uri; |
| 106 String get outputDir => options.codegenOptions.outputDir; |
101 | 107 |
102 bool _buildSource(SourceNode node) { | 108 bool _buildSource(SourceNode node) { |
103 if (node is HtmlSourceNode) { | 109 if (node is HtmlSourceNode) { |
104 _buildHtmlFile(node); | 110 _buildHtmlFile(node); |
105 } else if (node is DartSourceNode) { | 111 } else if (node is DartSourceNode) { |
106 _buildDartLibrary(node); | 112 _buildDartLibrary(node); |
107 } else if (node is ResourceSourceNode) { | 113 } else if (node is ResourceSourceNode) { |
108 _buildResourceFile(node); | 114 _buildResourceFile(node); |
109 } else { | 115 } else { |
110 assert(false); // should not get a build request on PartSourceNode | 116 assert(false); // should not get a build request on PartSourceNode |
111 } | 117 } |
112 | 118 |
113 // TODO(sigmund): don't always return true. Use summarization to better | 119 // TODO(sigmund): don't always return true. Use summarization to better |
114 // determine when rebuilding is needed. | 120 // determine when rebuilding is needed. |
115 return true; | 121 return true; |
116 } | 122 } |
117 | 123 |
118 void _buildHtmlFile(HtmlSourceNode node) { | 124 void _buildHtmlFile(HtmlSourceNode node) { |
119 if (options.outputDir == null) return; | 125 if (outputDir == null) return; |
120 var uri = node.source.uri; | 126 var uri = node.source.uri; |
121 _reporter.enterHtml(uri); | 127 _reporter.enterHtml(uri); |
122 var output = generateEntryHtml(node, options); | 128 var output = generateEntryHtml(node, options); |
123 if (output == null) { | 129 if (output == null) { |
124 _failure = true; | 130 _failure = true; |
125 return; | 131 return; |
126 } | 132 } |
127 _reporter.leaveHtml(); | 133 _reporter.leaveHtml(); |
128 var filename = path.basename(node.uri.path); | 134 var filename = path.basename(node.uri.path); |
129 String outputFile = path.join(options.outputDir, filename); | 135 String outputFile = path.join(outputDir, filename); |
130 new File(outputFile).writeAsStringSync(output); | 136 new File(outputFile).writeAsStringSync(output); |
131 } | 137 } |
132 | 138 |
133 void _buildResourceFile(ResourceSourceNode node) { | 139 void _buildResourceFile(ResourceSourceNode node) { |
134 // ResourceSourceNodes files that just need to be copied over to the output | 140 // ResourceSourceNodes files that just need to be copied over to the output |
135 // location. These can be external dependencies or pieces of the | 141 // location. These can be external dependencies or pieces of the |
136 // dev_compiler runtime. | 142 // dev_compiler runtime. |
137 if (options.outputDir == null) return; | 143 if (outputDir == null) return; |
138 var filepath = resourceOutputPath(node.uri, _entryNode.uri); | 144 var filepath = resourceOutputPath(node.uri, _entryNode.uri); |
139 assert(filepath != null); | 145 assert(filepath != null); |
140 filepath = path.join(options.outputDir, filepath); | 146 filepath = path.join(outputDir, filepath); |
141 var dir = path.dirname(filepath); | 147 var dir = path.dirname(filepath); |
142 new Directory(dir).createSync(recursive: true); | 148 new Directory(dir).createSync(recursive: true); |
143 new File.fromUri(node.source.uri).copySync(filepath); | 149 new File.fromUri(node.source.uri).copySync(filepath); |
144 if (_hashing) node.cachingHash = computeHashFromFile(filepath); | 150 if (_hashing) node.cachingHash = computeHashFromFile(filepath); |
145 } | 151 } |
146 | 152 |
147 bool _isEntry(DartSourceNode node) { | 153 bool _isEntry(DartSourceNode node) { |
148 if (_entryNode is DartSourceNode) return _entryNode == node; | 154 if (_entryNode is DartSourceNode) return _entryNode == node; |
149 return (_entryNode as HtmlSourceNode).scripts.contains(node); | 155 return (_entryNode as HtmlSourceNode).scripts.contains(node); |
150 } | 156 } |
(...skipping 24 matching lines...) Expand all Loading... |
175 var unitSource = unit.element.source; | 181 var unitSource = unit.element.source; |
176 _reporter.enterCompilationUnit(unit); | 182 _reporter.enterCompilationUnit(unit); |
177 // TODO(sigmund): integrate analyzer errors with static-info (issue #6). | 183 // TODO(sigmund): integrate analyzer errors with static-info (issue #6). |
178 failureInLib = logErrors(unitSource) || failureInLib; | 184 failureInLib = logErrors(unitSource) || failureInLib; |
179 _checker.visitCompilationUnit(unit); | 185 _checker.visitCompilationUnit(unit); |
180 if (_checker.failure) failureInLib = true; | 186 if (_checker.failure) failureInLib = true; |
181 _reporter.leaveCompilationUnit(); | 187 _reporter.leaveCompilationUnit(); |
182 } | 188 } |
183 if (failureInLib) { | 189 if (failureInLib) { |
184 _failure = true; | 190 _failure = true; |
185 if (!options.forceCompile) return; | 191 if (!options.codegenOptions.forceCompile) return; |
186 } | 192 } |
187 | 193 |
188 for (var cg in _generators) { | 194 for (var cg in _generators) { |
189 var hash = cg.generateLibrary(libraryUnit, current); | 195 var hash = cg.generateLibrary(libraryUnit, current); |
190 if (_hashing) node.cachingHash = hash; | 196 if (_hashing) node.cachingHash = hash; |
191 } | 197 } |
192 _reporter.leaveLibrary(); | 198 _reporter.leaveLibrary(); |
193 } | 199 } |
194 | 200 |
195 /// Log any errors encountered when resolving [source] and return whether any | 201 /// Log any errors encountered when resolving [source] and return whether any |
(...skipping 18 matching lines...) Expand all Loading... |
214 // dependency_graph now detects broken imports or unsupported features | 220 // dependency_graph now detects broken imports or unsupported features |
215 // like more than one script tag (see .severe messages in | 221 // like more than one script tag (see .severe messages in |
216 // dependency_graph.dart). Such failures should be reported back | 222 // dependency_graph.dart). Such failures should be reported back |
217 // here so we can mark failure=true in the CheckerResutls. | 223 // here so we can mark failure=true in the CheckerResutls. |
218 rebuild(_entryNode, _buildSource); | 224 rebuild(_entryNode, _buildSource); |
219 _dumpInfoIfRequested(); | 225 _dumpInfoIfRequested(); |
220 clock.stop(); | 226 clock.stop(); |
221 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2); | 227 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2); |
222 _log.fine('Compiled ${_libraries.length} libraries in ${time} s\n'); | 228 _log.fine('Compiled ${_libraries.length} libraries in ${time} s\n'); |
223 return new CheckerResults( | 229 return new CheckerResults( |
224 _libraries, rules, _failure || options.forceCompile); | 230 _libraries, rules, _failure || options.codegenOptions.forceCompile); |
225 } | 231 } |
226 | 232 |
227 void _runAgain() { | 233 void _runAgain() { |
228 var clock = new Stopwatch()..start(); | 234 var clock = new Stopwatch()..start(); |
229 _libraries = <LibraryInfo>[]; | 235 _libraries = <LibraryInfo>[]; |
230 int changed = 0; | 236 int changed = 0; |
231 | 237 |
232 // TODO(sigmund): propagate failures here (see TODO in run). | 238 // TODO(sigmund): propagate failures here (see TODO in run). |
233 rebuild(_entryNode, (n) { | 239 rebuild(_entryNode, (n) { |
234 changed++; | 240 changed++; |
235 return _buildSource(n); | 241 return _buildSource(n); |
236 }); | 242 }); |
237 clock.stop(); | 243 clock.stop(); |
238 if (changed > 0) _dumpInfoIfRequested(); | 244 if (changed > 0) _dumpInfoIfRequested(); |
239 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2); | 245 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2); |
240 _log.fine("Compiled ${changed} libraries in ${time} s\n"); | 246 _log.fine("Compiled ${changed} libraries in ${time} s\n"); |
241 } | 247 } |
242 | 248 |
243 _dumpInfoIfRequested() { | 249 _dumpInfoIfRequested() { |
244 if (!options.dumpInfo || _reporter is! SummaryReporter) return; | 250 if (!options.dumpInfo || _reporter is! SummaryReporter) return; |
245 var result = (_reporter as SummaryReporter).result; | 251 var result = (_reporter as SummaryReporter).result; |
246 if (!options.serverMode) print(summaryToString(result)); | 252 if (!options.serverMode) print(summaryToString(result)); |
247 var filepath = options.serverMode | 253 var filepath = options.serverMode |
248 ? path.join(options.outputDir, 'messages.json') | 254 ? path.join(outputDir, 'messages.json') |
249 : options.dumpInfoFile; | 255 : options.dumpInfoFile; |
250 if (filepath == null) return; | 256 if (filepath == null) return; |
251 new File(filepath).writeAsStringSync(JSON.encode(result.toJsonMap())); | 257 new File(filepath).writeAsStringSync(JSON.encode(result.toJsonMap())); |
252 } | 258 } |
253 } | 259 } |
254 | 260 |
255 class CompilerServer { | 261 class CompilerServer { |
256 final Compiler compiler; | 262 final Compiler compiler; |
257 final String outDir; | 263 final String outDir; |
258 final String host; | 264 final String host; |
259 final int port; | 265 final int port; |
260 final String _entryPath; | 266 final String _entryPath; |
261 | 267 |
262 factory CompilerServer(CompilerOptions options) { | 268 factory CompilerServer(CompilerOptions options) { |
263 var entryPath = path.basename(options.entryPointFile); | 269 var entryPath = path.basename(options.sourceOptions.entryPointFile); |
264 var extension = path.extension(entryPath); | 270 var extension = path.extension(entryPath); |
265 if (extension != '.html' && !options.useImplicitHtml) { | 271 if (extension != '.html' && !options.sourceOptions.useImplicitHtml) { |
266 print('error: devc in server mode requires an HTML or Dart entry point.'); | 272 print('error: devc in server mode requires an HTML or Dart entry point.'); |
267 exit(1); | 273 exit(1); |
268 } | 274 } |
269 | 275 |
270 // TODO(sigmund): allow running without a dir, but keep output in memory? | 276 // TODO(sigmund): allow running without a dir, but keep output in memory? |
271 var outDir = options.outputDir; | 277 var outDir = options.codegenOptions.outputDir; |
272 if (outDir == null) { | 278 if (outDir == null) { |
273 print('error: devc in server mode also requires specifying and ' | 279 print('error: devc in server mode also requires specifying and ' |
274 'output location for generated code.'); | 280 'output location for generated code.'); |
275 exit(1); | 281 exit(1); |
276 } | 282 } |
277 var port = options.port; | 283 var port = options.port; |
278 var host = options.host; | 284 var host = options.host; |
279 var compiler = new Compiler(options); | 285 var compiler = new Compiler(options); |
280 return new CompilerServer._(compiler, outDir, host, port, entryPath); | 286 return new CompilerServer._(compiler, outDir, host, port, entryPath); |
281 } | 287 } |
282 | 288 |
283 CompilerServer._( | 289 CompilerServer._( |
284 Compiler compiler, this.outDir, this.host, this.port, String entryPath) | 290 Compiler compiler, this.outDir, this.host, this.port, String entryPath) |
285 : this.compiler = compiler, | 291 : this.compiler = compiler, |
286 this._entryPath = compiler.options.useImplicitHtml | 292 // TODO(jmesserly): this logic is duplicated in a few places |
287 ? ResolverOptions.implicitHtmlFile | 293 this._entryPath = compiler.options.sourceOptions.useImplicitHtml |
| 294 ? SourceResolverOptions.implicitHtmlFile |
288 : entryPath; | 295 : entryPath; |
289 | 296 |
290 Future start() async { | 297 Future start() async { |
291 // Create output directory if needed. shelf_static will fail otherwise. | 298 // Create output directory if needed. shelf_static will fail otherwise. |
292 var out = new Directory(outDir); | 299 var out = new Directory(outDir); |
293 if (!await out.exists()) await out.create(recursive: true); | 300 if (!await out.exists()) await out.create(recursive: true); |
294 | 301 |
295 var handler = const shelf.Pipeline() | 302 var handler = const shelf.Pipeline() |
296 .addMiddleware(rebuildAndCache) | 303 .addMiddleware(rebuildAndCache) |
297 .addHandler(shelf_static.createStaticHandler(outDir, | 304 .addHandler(shelf_static.createStaticHandler(outDir, |
(...skipping 21 matching lines...) Expand all Loading... |
319 // Note: the cache-control header should be enough, but this doesn't hurt | 326 // Note: the cache-control header should be enough, but this doesn't hurt |
320 // and can help renew the policy after it expires. | 327 // and can help renew the policy after it expires. |
321 headers['ETag'] = hash; | 328 headers['ETag'] = hash; |
322 } | 329 } |
323 return response.change(headers: headers); | 330 return response.change(headers: headers); |
324 }; | 331 }; |
325 } | 332 } |
326 | 333 |
327 final _log = new Logger('dev_compiler'); | 334 final _log = new Logger('dev_compiler'); |
328 final _earlyErrorResult = new CheckerResults(const [], null, true); | 335 final _earlyErrorResult = new CheckerResults(const [], null, true); |
OLD | NEW |