Chromium Code Reviews| 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.src.compiler; | 6 library dev_compiler.src.compiler; |
| 7 | 7 |
| 8 import 'dart:async'; | 8 import 'dart:async'; |
| 9 import 'dart:collection'; | 9 import 'dart:collection'; |
| 10 import 'dart:math' as math; | 10 import 'dart:math' as math; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 51 | 51 |
| 52 /// Already compiled sources, so we don't compile them again. | 52 /// Already compiled sources, so we don't compile them again. |
| 53 final _compiled = new HashSet<LibraryElement>(); | 53 final _compiled = new HashSet<LibraryElement>(); |
| 54 | 54 |
| 55 bool _failure = false; | 55 bool _failure = false; |
| 56 bool get failure => _failure; | 56 bool get failure => _failure; |
| 57 | 57 |
| 58 BatchCompiler(AnalysisContext context, CompilerOptions options, | 58 BatchCompiler(AnalysisContext context, CompilerOptions options, |
| 59 {AnalysisErrorListener reporter}) | 59 {AnalysisErrorListener reporter}) |
| 60 : super(context, options, reporter) { | 60 : super(context, options, reporter) { |
| 61 _inputBaseDir = options.inputBaseDir; | |
| 61 if (outputDir != null) { | 62 if (outputDir != null) { |
| 62 _jsGen = new JSGenerator(this); | 63 _jsGen = new JSGenerator(this); |
| 63 } | 64 } |
| 64 } | 65 } |
| 65 | 66 |
| 66 void reset() { | 67 void reset() { |
| 67 _compiled.clear(); | 68 _compiled.clear(); |
| 68 } | 69 } |
| 69 | 70 |
| 70 /// Compiles every file in [options.inputs]. | 71 /// Compiles every file in [options.inputs]. |
| 71 /// Returns true on successful compile. | 72 /// Returns true on successful compile. |
| 72 bool run() { | 73 bool run() { |
| 73 var clock = new Stopwatch()..start(); | 74 var clock = new Stopwatch()..start(); |
| 74 options.inputs.forEach(compileFromUriString); | 75 options.inputs.forEach(compileFromUriString); |
| 75 clock.stop(); | 76 clock.stop(); |
| 76 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2); | 77 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2); |
| 77 _log.fine('Compiled ${_compiled.length} libraries in ${time} s\n'); | 78 _log.fine('Compiled ${_compiled.length} libraries in ${time} s\n'); |
| 78 | 79 |
| 79 return !_failure; | 80 return !_failure; |
| 80 } | 81 } |
| 81 | 82 |
| 82 void compileFromUriString(String uriString) { | 83 void compileFromUriString(String uriString) { |
| 83 compileFromUri(stringToUri(uriString)); | 84 compileFromUri(stringToUri(uriString)); |
| 84 } | 85 } |
| 85 | 86 |
| 86 void compileFromUri(Uri uri) { | 87 void compileFromUri(Uri uri) { |
| 88 if (!uri.isAbsolute) { | |
| 89 throw new ArgumentError.value('$uri', 'uri', 'must be absolute'); | |
| 90 } | |
| 87 var source = context.sourceFactory.forUri(Uri.encodeFull('$uri')); | 91 var source = context.sourceFactory.forUri(Uri.encodeFull('$uri')); |
| 88 if (source == null) throw new ArgumentError.value( | 92 if (source == null) { |
| 89 uri.toString(), 'uri', 'could not find source for'); | 93 throw new ArgumentError.value('$uri', 'uri', 'could not find source for'); |
| 94 } | |
| 90 compileSource(source); | 95 compileSource(source); |
| 91 } | 96 } |
| 92 | 97 |
| 93 void compileSource(Source source) { | 98 void compileSource(Source source) { |
| 94 if (AnalysisEngine.isHtmlFileName(source.uri.path)) { | 99 if (AnalysisEngine.isHtmlFileName(source.uri.path)) { |
| 95 compileHtml(source); | 100 compileHtml(source); |
| 96 return; | 101 return; |
| 97 } | 102 } |
| 98 | 103 |
| 99 compileLibrary(context.computeLibraryElement(source)); | 104 compileLibrary(context.computeLibraryElement(source)); |
| 100 } | 105 } |
| 101 | 106 |
| 102 void compileLibrary(LibraryElement library) { | 107 void compileLibrary(LibraryElement library) { |
| 103 if (!_compiled.add(library)) return; | 108 if (!_compiled.add(library)) return; |
| 104 if (!options.checkSdk && library.source.uri.scheme == 'dart') return; | 109 if (!options.checkSdk && library.source.uri.scheme == 'dart') return; |
| 105 | 110 |
| 106 // TODO(jmesserly): in incremental mode, we can skip the transitive | 111 // TODO(jmesserly): in incremental mode, we can skip the transitive |
| 107 // compile of imports/exports. | 112 // compile of imports/exports. |
| 108 library.importedLibraries.forEach(compileLibrary); | 113 library.importedLibraries.forEach(compileLibrary); |
| 109 library.exportedLibraries.forEach(compileLibrary); | 114 library.exportedLibraries.forEach(compileLibrary); |
| 110 | 115 |
| 111 var unitElements = [library.definingCompilationUnit]..addAll(library.parts); | 116 var unitElements = [library.definingCompilationUnit]..addAll(library.parts); |
| 112 var units = <CompilationUnit>[]; | 117 var units = <CompilationUnit>[]; |
| 113 | 118 |
| 114 bool failureInLib = false; | 119 bool failureInLib = false; |
| 115 for (var element in unitElements) { | 120 for (var element in unitElements) { |
| 116 var unit = context.resolveCompilationUnit(element.source, library); | 121 var unit = context.resolveCompilationUnit(element.source, library); |
| 122 | |
| 123 // TODO(jmesserly): this hack is to avoid compiling the same compilation | |
| 124 // unit to JS twice. We mutate the AST, so it's not safe to run more than | |
| 125 // once on the same unit. | |
| 126 if (element.library == library) { | |
| 127 if (unit.getProperty(_propertyName) == true) return; | |
| 128 unit.setProperty(_propertyName, true); | |
| 129 } | |
| 130 | |
| 117 units.add(unit); | 131 units.add(unit); |
| 118 failureInLib = logErrors(element.source) || failureInLib; | 132 failureInLib = logErrors(element.source) || failureInLib; |
| 119 checker.visitCompilationUnit(unit); | 133 checker.visitCompilationUnit(unit); |
| 120 if (checker.failure) failureInLib = true; | 134 if (checker.failure) failureInLib = true; |
| 121 } | 135 } |
| 122 | 136 |
| 123 if (failureInLib) { | 137 if (failureInLib) { |
| 124 _failure = true; | 138 _failure = true; |
| 125 if (!options.codegenOptions.forceCompile) return; | 139 if (!options.codegenOptions.forceCompile) return; |
| 126 } | 140 } |
| 127 | 141 |
| 128 if (_jsGen != null) { | 142 if (_jsGen != null) { |
| 129 var unit = units.first; | 143 var unit = units.first; |
| 130 var parts = units.skip(1).toList(); | 144 var parts = units.skip(1).toList(); |
| 131 | 145 |
| 132 // TODO(jmesserly): this hack is to avoid compiling the same compilation | |
| 133 // unit to JS twice. We mutate the AST, so it's not safe to run more than | |
| 134 // once on the same unit. | |
| 135 if (unit.getProperty(_propertyName) == true) return; | |
| 136 unit.setProperty(_propertyName, true); | |
| 137 | |
| 138 _jsGen.generateLibrary(new LibraryUnit(unit, parts)); | 146 _jsGen.generateLibrary(new LibraryUnit(unit, parts)); |
| 139 } | 147 } |
| 140 } | 148 } |
| 141 | 149 |
| 142 static const String _propertyName = 'dev_compiler.BatchCompiler.isCompiled'; | 150 static const String _propertyName = 'dev_compiler.BatchCompiler.isCompiled'; |
| 143 | 151 |
| 144 void compileHtml(Source source) { | 152 void compileHtml(Source source) { |
| 145 // TODO(jmesserly): reuse DartScriptsTask instead of copy/paste. | 153 // TODO(jmesserly): reuse DartScriptsTask instead of copy/paste. |
| 146 var contents = context.getContents(source); | 154 var contents = context.getContents(source); |
| 147 var document = html.parse(contents.data, generateSpans: true); | 155 var document = html.parse(contents.data, generateSpans: true); |
| 148 var scripts = document.querySelectorAll('script[type="application/dart"]'); | 156 var scripts = document.querySelectorAll('script[type="application/dart"]'); |
| 149 | 157 |
| 150 var loadedLibs = new LinkedHashSet<Uri>(); | 158 var loadedLibs = new LinkedHashSet<Uri>(); |
| 151 | 159 |
| 160 var htmlOutDir = path.dirname(getOutputPath(source.uri)); | |
| 152 for (var script in scripts) { | 161 for (var script in scripts) { |
| 153 Source scriptSource = null; | 162 Source scriptSource = null; |
| 154 var srcAttr = script.attributes['src']; | 163 var srcAttr = script.attributes['src']; |
| 155 if (srcAttr == null) { | 164 if (srcAttr == null) { |
| 156 if (script.hasContent()) { | 165 if (script.hasContent()) { |
| 157 var fragments = <ScriptFragment>[]; | 166 var fragments = <ScriptFragment>[]; |
| 158 for (var node in script.nodes) { | 167 for (var node in script.nodes) { |
| 159 if (node is html.Text) { | 168 if (node is html.Text) { |
| 160 var start = node.sourceSpan.start; | 169 var start = node.sourceSpan.start; |
| 161 fragments.add(new ScriptFragment( | 170 fragments.add(new ScriptFragment( |
| 162 start.offset, start.line, start.column, node.data)); | 171 start.offset, start.line, start.column, node.data)); |
| 163 } | 172 } |
| 164 } | 173 } |
| 165 scriptSource = new DartScript(source, fragments); | 174 scriptSource = new DartScript(source, fragments); |
| 166 } | 175 } |
| 167 } else if (AnalysisEngine.isDartFileName(srcAttr)) { | 176 } else if (AnalysisEngine.isDartFileName(srcAttr)) { |
| 168 scriptSource = context.sourceFactory.resolveUri(source, srcAttr); | 177 scriptSource = context.sourceFactory.resolveUri(source, srcAttr); |
| 169 } | 178 } |
| 170 | 179 |
| 171 if (scriptSource != null) { | 180 if (scriptSource != null) { |
| 172 var lib = context.computeLibraryElement(scriptSource); | 181 var lib = context.computeLibraryElement(scriptSource); |
| 173 compileLibrary(lib); | 182 compileLibrary(lib); |
| 174 script.replaceWith(_linkLibraries(lib, loadedLibs)); | 183 script.replaceWith(_linkLibraries(lib, loadedLibs, from: htmlOutDir)); |
| 175 } | 184 } |
| 176 } | 185 } |
| 177 | 186 |
| 178 // TODO(jmesserly): we need to clean this up so we aren't treating these | 187 // TODO(jmesserly): we need to clean this up so we aren't treating these |
| 179 // as a special case. | 188 // as a special case. |
| 180 for (var file in defaultRuntimeFiles) { | 189 for (var file in defaultRuntimeFiles) { |
| 181 var input = path.join(options.runtimeDir, file); | 190 var input = path.join(options.runtimeDir, file); |
| 182 var output = path.join(outputDir, runtimeFileOutput(file)); | 191 var output = path.join(htmlOutDir, runtimeFileOutput(file)); |
| 183 new Directory(path.dirname(output)).createSync(recursive: true); | 192 new Directory(path.dirname(output)).createSync(recursive: true); |
| 184 new File(input).copySync(output); | 193 new File(input).copySync(output); |
| 185 } | 194 } |
| 186 | 195 |
| 187 new File(getOutputPath(source.uri)).openSync(mode: FileMode.WRITE) | 196 new File(getOutputPath(source.uri)).openSync(mode: FileMode.WRITE) |
| 188 ..writeStringSync(document.outerHtml) | 197 ..writeStringSync(document.outerHtml) |
| 189 ..writeStringSync('\n') | 198 ..writeStringSync('\n') |
| 190 ..closeSync(); | 199 ..closeSync(); |
| 191 } | 200 } |
| 192 | 201 |
| 193 html.DocumentFragment _linkLibraries( | 202 html.DocumentFragment _linkLibraries( |
| 194 LibraryElement mainLib, LinkedHashSet<Uri> loaded) { | 203 LibraryElement mainLib, LinkedHashSet<Uri> loaded, {String from}) { |
| 204 assert(from != null); | |
| 195 var alreadyLoaded = loaded.length; | 205 var alreadyLoaded = loaded.length; |
| 196 _collectLibraries(mainLib, loaded); | 206 _collectLibraries(mainLib, loaded); |
| 197 | 207 |
| 198 var newLibs = loaded.skip(alreadyLoaded); | 208 var newLibs = loaded.skip(alreadyLoaded); |
| 199 var df = new html.DocumentFragment(); | 209 var df = new html.DocumentFragment(); |
| 200 for (var path in defaultRuntimeFiles) { | 210 for (var file in defaultRuntimeFiles) { |
| 201 df.append(html_codegen.libraryInclude(runtimeFileOutput(path))); | 211 df.append(html_codegen.libraryInclude(runtimeFileOutput(file))); |
|
vsm
2015/07/20 16:11:16
I think you need to make a relative path wrt *from
Jennifer Messerly
2015/07/20 16:32:31
It's already a relative path. The (poorly named) r
| |
| 202 } | 212 } |
| 203 for (var uri in newLibs) { | 213 for (var uri in newLibs) { |
| 204 if (uri.scheme == 'dart') continue; | 214 if (uri.scheme == 'dart') continue; |
| 205 df.append(html_codegen.libraryInclude(getModulePath(uri))); | 215 var jsPath = getModulePath(uri); |
| 216 jsPath = path.relative(path.join(outputDir, jsPath), from: from); | |
| 217 df.append(html_codegen.libraryInclude(jsPath)); | |
| 206 } | 218 } |
| 207 df.append(html_codegen.invokeMain(getModuleName(mainLib.source.uri))); | 219 df.append(html_codegen.invokeMain(getModuleName(mainLib.source.uri))); |
| 208 return df; | 220 return df; |
| 209 } | 221 } |
| 210 | 222 |
| 211 void _collectLibraries(LibraryElement lib, LinkedHashSet<Uri> loaded) { | 223 void _collectLibraries(LibraryElement lib, LinkedHashSet<Uri> loaded) { |
| 212 var uri = lib.source.uri; | 224 var uri = lib.source.uri; |
| 213 if (!loaded.add(uri)) return; | 225 if (!loaded.add(uri)) return; |
| 214 for (var l in lib.importedLibraries) _collectLibraries(l, loaded); | 226 for (var l in lib.importedLibraries) _collectLibraries(l, loaded); |
| 215 for (var l in lib.exportedLibraries) _collectLibraries(l, loaded); | 227 for (var l in lib.exportedLibraries) _collectLibraries(l, loaded); |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 242 new RestrictedRules(typeProvider, options: options), reporter, options); | 254 new RestrictedRules(typeProvider, options: options), reporter, options); |
| 243 } | 255 } |
| 244 | 256 |
| 245 String get outputDir => options.codegenOptions.outputDir; | 257 String get outputDir => options.codegenOptions.outputDir; |
| 246 TypeRules get rules => checker.rules; | 258 TypeRules get rules => checker.rules; |
| 247 AnalysisErrorListener get reporter => checker.reporter; | 259 AnalysisErrorListener get reporter => checker.reporter; |
| 248 | 260 |
| 249 Uri stringToUri(String uriString) { | 261 Uri stringToUri(String uriString) { |
| 250 var uri = uriString.startsWith('dart:') || uriString.startsWith('package:') | 262 var uri = uriString.startsWith('dart:') || uriString.startsWith('package:') |
| 251 ? Uri.parse(uriString) | 263 ? Uri.parse(uriString) |
| 252 : new Uri.file(uriString); | 264 : new Uri.file(path.absolute(uriString)); |
| 253 return uri; | 265 return uri; |
| 254 } | 266 } |
| 255 | 267 |
| 256 /// Directory presumed to be the common prefix for all input file:// URIs. | 268 /// Directory presumed to be the common prefix for all input file:// URIs. |
| 257 /// Used when computing output paths. | 269 /// Used when computing output paths. |
| 258 /// | 270 /// |
| 259 /// For example: | 271 /// For example: |
| 260 /// dartdevc -o out foo/a.dart bar/b.dart | 272 /// dartdevc -o out foo/a.dart bar/b.dart |
| 261 /// | 273 /// |
| 262 /// Will produce: | 274 /// Will produce: |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 306 /// * dart:core -> dart/core | 318 /// * dart:core -> dart/core |
| 307 /// * file:foo/bar/baz.dart -> foo/bar/baz | 319 /// * file:foo/bar/baz.dart -> foo/bar/baz |
| 308 /// * package:qux/qux.dart -> qux/qux | 320 /// * package:qux/qux.dart -> qux/qux |
| 309 /// | 321 /// |
| 310 /// For file: URLs this will also make them relative to [inputBaseDir]. | 322 /// For file: URLs this will also make them relative to [inputBaseDir]. |
| 311 // TODO(jmesserly): we need to figure out a way to keep package and file URLs | 323 // TODO(jmesserly): we need to figure out a way to keep package and file URLs |
| 312 // from conflicting. | 324 // from conflicting. |
| 313 String getModuleName(Uri uri) { | 325 String getModuleName(Uri uri) { |
| 314 var filepath = path.withoutExtension(uri.path); | 326 var filepath = path.withoutExtension(uri.path); |
| 315 if (uri.scheme == 'dart') { | 327 if (uri.scheme == 'dart') { |
| 316 filepath = 'dart/$filepath'; | 328 return 'dart/$filepath'; |
| 317 } else if (uri.scheme == 'file') { | 329 } else if (uri.scheme == 'file') { |
| 318 filepath = path.relative(filepath, from: inputBaseDir); | 330 return path.relative(filepath, from: inputBaseDir); |
| 319 } else { | 331 } else { |
| 320 assert(uri.scheme == 'package'); | 332 assert(uri.scheme == 'package'); |
| 321 // filepath is good here, we want the output to start with a directory | 333 // filepath is good here, we want the output to start with a directory |
| 322 // matching the package name. | 334 // matching the package name. |
| 335 return filepath; | |
| 323 } | 336 } |
| 324 return filepath; | |
| 325 } | 337 } |
| 326 | 338 |
| 327 /// Log any errors encountered when resolving [source] and return whether any | 339 /// Log any errors encountered when resolving [source] and return whether any |
| 328 /// errors were found. | 340 /// errors were found. |
| 329 bool logErrors(Source source) { | 341 bool logErrors(Source source) { |
| 330 List<AnalysisError> errors = context.computeErrors(source); | 342 List<AnalysisError> errors = context.computeErrors(source); |
| 331 bool failure = false; | 343 bool failure = false; |
| 332 if (errors.isNotEmpty) { | 344 if (errors.isNotEmpty) { |
| 333 for (var error in errors) { | 345 for (var error in errors) { |
| 334 // Always skip TODOs. | 346 // Always skip TODOs. |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 394 '_rtti.js', | 406 '_rtti.js', |
| 395 '_classes.js', | 407 '_classes.js', |
| 396 '_operations.js', | 408 '_operations.js', |
| 397 'dart_runtime.js', | 409 'dart_runtime.js', |
| 398 ]; | 410 ]; |
| 399 files.addAll(corelibOrder.map((l) => l.replaceAll('.', '/') + '.js')); | 411 files.addAll(corelibOrder.map((l) => l.replaceAll('.', '/') + '.js')); |
| 400 return files; | 412 return files; |
| 401 }(); | 413 }(); |
| 402 | 414 |
| 403 final _log = new Logger('dev_compiler.src.compiler'); | 415 final _log = new Logger('dev_compiler.src.compiler'); |
| OLD | NEW |