| 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 |