Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 library dart2js.cmdline; | 5 library dart2js.cmdline; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:io' | 8 import 'dart:io' |
| 9 show exit, File, FileMode, Platform, RandomAccessFile; | 9 show exit, File, FileMode, Platform, RandomAccessFile; |
| 10 import 'dart:math' as math; | 10 import 'dart:math' as math; |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 38 typedef void HandleOption(data); | 38 typedef void HandleOption(data); |
| 39 | 39 |
| 40 class OptionHandler { | 40 class OptionHandler { |
| 41 final String pattern; | 41 final String pattern; |
| 42 final HandleOption handle; | 42 final HandleOption handle; |
| 43 final bool multipleArguments; | 43 final bool multipleArguments; |
| 44 | 44 |
| 45 OptionHandler(this.pattern, this.handle, {this.multipleArguments: false}); | 45 OptionHandler(this.pattern, this.handle, {this.multipleArguments: false}); |
| 46 } | 46 } |
| 47 | 47 |
| 48 /** | 48 class _Dart2Js { |
|
ahe
2013/11/21 12:44:53
If you think it makes sense to create a class here
| |
| 49 * Extract the parameter of an option. | 49 final exitFunc; |
| 50 * | 50 final compileFunc; |
| 51 * For example, in ['--out=fisk.js'] and ['-ohest.js'], the parameters | 51 |
| 52 * are ['fisk.js'] and ['hest.js'], respectively. | 52 _Dart2Js(this.exitFunc, this.compileFunc); |
| 53 */ | 53 |
| 54 String extractParameter(String argument) { | 54 /** |
| 55 // m[0] is the entire match (which will be equal to argument). m[1] | 55 * Extract the parameter of an option. |
| 56 // is something like "-o" or "--out=", and m[2] is the parameter. | 56 * |
| 57 Match m = new RegExp('^(-[a-z]|--.+=)(.*)').firstMatch(argument); | 57 * For example, in ['--out=fisk.js'] and ['-ohest.js'], the parameters |
| 58 if (m == null) helpAndFail('Error: Unknown option "$argument".'); | 58 * are ['fisk.js'] and ['hest.js'], respectively. |
| 59 return m[2]; | 59 */ |
| 60 String extractParameter(String argument) { | |
| 61 // m[0] is the entire match (which will be equal to argument). m[1] | |
| 62 // is something like "-o" or "--out=", and m[2] is the parameter. | |
| 63 Match m = new RegExp('^(-[a-z]|--.+=)(.*)').firstMatch(argument); | |
| 64 if (m == null) helpAndFail('Error: Unknown option "$argument".'); | |
| 65 return m[2]; | |
| 66 } | |
| 67 | |
| 68 String extractPath(String argument) { | |
| 69 String path = nativeToUriPath(extractParameter(argument)); | |
| 70 return path.endsWith("/") ? path : "$path/"; | |
| 71 } | |
| 72 | |
| 73 void parseCommandLine(List<OptionHandler> handlers, List<String> argv) { | |
| 74 // TODO(ahe): Use ../../args/args.dart for parsing options instead. | |
| 75 var patterns = <String>[]; | |
| 76 for (OptionHandler handler in handlers) { | |
| 77 patterns.add(handler.pattern); | |
| 78 } | |
| 79 var pattern = new RegExp('^(${patterns.join(")\$|(")})\$'); | |
| 80 | |
| 81 Iterator<String> arguments = argv.iterator; | |
| 82 OUTER: while (arguments.moveNext()) { | |
| 83 String argument = arguments.current; | |
| 84 Match match = pattern.firstMatch(argument); | |
| 85 assert(match.groupCount == handlers.length); | |
| 86 for (int i = 0; i < handlers.length; i++) { | |
| 87 if (match[i + 1] != null) { | |
| 88 OptionHandler handler = handlers[i]; | |
| 89 if (handler.multipleArguments) { | |
| 90 handler.handle(arguments); | |
| 91 } else { | |
| 92 handler.handle(argument); | |
| 93 } | |
| 94 continue OUTER; | |
| 95 } | |
| 96 } | |
| 97 throw 'Internal error: "$argument" did not match'; | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 FormattingDiagnosticHandler diagnosticHandler; | |
| 102 | |
| 103 Future compile(List<String> argv) { | |
| 104 bool isWindows = (Platform.operatingSystem == 'windows'); | |
| 105 stackTraceFilePrefix = '$currentDirectory'; | |
| 106 Uri libraryRoot = currentDirectory; | |
| 107 Uri out = currentDirectory.resolve('out.js'); | |
| 108 Uri sourceMapOut = currentDirectory.resolve('out.js.map'); | |
| 109 Uri packageRoot = null; | |
| 110 List<String> options = new List<String>(); | |
| 111 bool explicitOut = false; | |
| 112 bool wantHelp = false; | |
| 113 bool wantVersion = false; | |
| 114 String outputLanguage = 'JavaScript'; | |
| 115 bool stripArgumentSet = false; | |
| 116 bool analyzeOnly = false; | |
| 117 bool hasDisallowUnsafeEval = false; | |
| 118 // TODO(johnniwinther): Measure time for reading files. | |
| 119 SourceFileProvider inputProvider = new CompilerSourceFileProvider(); | |
| 120 diagnosticHandler = new FormattingDiagnosticHandler(inputProvider); | |
| 121 Map<String, dynamic> environment = new Map<String, dynamic>(); | |
| 122 | |
| 123 passThrough(String argument) => options.add(argument); | |
| 124 | |
| 125 if (BUILD_ID != null) { | |
| 126 passThrough("--build-id=$BUILD_ID"); | |
| 127 } | |
| 128 | |
| 129 setLibraryRoot(String argument) { | |
| 130 libraryRoot = currentDirectory.resolve(extractPath(argument)); | |
| 131 } | |
| 132 | |
| 133 setPackageRoot(String argument) { | |
| 134 packageRoot = currentDirectory.resolve(extractPath(argument)); | |
| 135 } | |
| 136 | |
| 137 setOutput(Iterator<String> arguments) { | |
| 138 String path; | |
| 139 if (arguments.current == '-o') { | |
| 140 if (!arguments.moveNext()) { | |
| 141 helpAndFail('Error: Missing file after -o option.'); | |
| 142 } | |
| 143 path = arguments.current; | |
| 144 } else { | |
| 145 path = extractParameter(arguments.current); | |
| 146 } | |
| 147 explicitOut = true; | |
| 148 out = currentDirectory.resolve(nativeToUriPath(path)); | |
| 149 sourceMapOut = Uri.parse('$out.map'); | |
| 150 } | |
| 151 | |
| 152 setOutputType(String argument) { | |
| 153 if (argument == '--output-type=dart') { | |
| 154 outputLanguage = OUTPUT_LANGUAGE_DART; | |
| 155 if (!explicitOut) { | |
| 156 out = currentDirectory.resolve('out.dart'); | |
| 157 sourceMapOut = currentDirectory.resolve('out.dart.map'); | |
| 158 } | |
| 159 } | |
| 160 passThrough(argument); | |
| 161 } | |
| 162 | |
| 163 String getDepsOutput(Map<String, SourceFile> sourceFiles) { | |
| 164 var filenames = new List.from(sourceFiles.keys); | |
| 165 filenames.sort(); | |
| 166 return filenames.join("\n"); | |
| 167 } | |
| 168 | |
| 169 setStrip(String argument) { | |
| 170 stripArgumentSet = true; | |
| 171 passThrough(argument); | |
| 172 } | |
| 173 | |
| 174 setAnalyzeOnly(String argument) { | |
| 175 analyzeOnly = true; | |
| 176 passThrough(argument); | |
| 177 } | |
| 178 | |
| 179 setVerbose(_) { | |
| 180 diagnosticHandler.verbose = true; | |
| 181 passThrough('--verbose'); | |
| 182 } | |
| 183 | |
| 184 addInEnvironment(String argument) { | |
| 185 int eqIndex = argument.indexOf('='); | |
| 186 String name = argument.substring(2, eqIndex); | |
| 187 String value = argument.substring(eqIndex + 1); | |
| 188 environment[name] = value; | |
| 189 } | |
| 190 | |
| 191 setCategories(String argument) { | |
| 192 List<String> categories = extractParameter(argument).split(','); | |
| 193 Set<String> allowedCategories = | |
| 194 LIBRARIES.values.map((x) => x.category).toSet(); | |
| 195 allowedCategories.remove('Shared'); | |
| 196 allowedCategories.remove('Internal'); | |
| 197 List<String> allowedCategoriesList = | |
| 198 new List<String>.from(allowedCategories); | |
| 199 allowedCategoriesList.sort(); | |
| 200 if (categories.contains('all')) { | |
| 201 categories = allowedCategoriesList; | |
| 202 } else { | |
| 203 String allowedCategoriesString = allowedCategoriesList.join(', '); | |
| 204 for (String category in categories) { | |
| 205 if (!allowedCategories.contains(category)) { | |
| 206 fail('Error: unsupported library category "$category", ' | |
| 207 'supported categories are: $allowedCategoriesString'); | |
| 208 } | |
| 209 } | |
| 210 } | |
| 211 passThrough('--categories=${categories.join(",")}'); | |
| 212 } | |
| 213 | |
| 214 checkGlobalName(String argument) { | |
| 215 String globalName = extractParameter(argument); | |
| 216 if (!new RegExp(r'^\$[a-z]*$').hasMatch(globalName)) { | |
| 217 fail('Error: "$globalName" must match "\\\$[a-z]*"'); | |
| 218 } | |
| 219 passThrough(argument); | |
| 220 } | |
| 221 | |
| 222 handleShortOptions(String argument) { | |
| 223 var shortOptions = argument.substring(1).split(""); | |
| 224 for (var shortOption in shortOptions) { | |
| 225 switch (shortOption) { | |
| 226 case 'v': | |
| 227 setVerbose(null); | |
| 228 break; | |
| 229 case 'h': | |
| 230 case '?': | |
| 231 wantHelp = true; | |
| 232 break; | |
| 233 case 'c': | |
| 234 passThrough('--enable-checked-mode'); | |
| 235 break; | |
| 236 default: | |
| 237 throw 'Internal error: "$shortOption" did not match'; | |
| 238 } | |
| 239 } | |
| 240 } | |
| 241 | |
| 242 Uri computePrecompiledUri() { | |
| 243 String extension = 'precompiled.js'; | |
| 244 String outPath = out.path; | |
| 245 if (outPath.endsWith('.js')) { | |
| 246 outPath = outPath.substring(0, outPath.length - 3); | |
| 247 return out.resolve('$outPath.$extension'); | |
| 248 } else { | |
| 249 return out.resolve(extension); | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 List<String> arguments = <String>[]; | |
| 254 List<OptionHandler> handlers = <OptionHandler>[ | |
| 255 new OptionHandler('-[chv?]+', handleShortOptions), | |
| 256 new OptionHandler('--throw-on-error', | |
| 257 (_) => diagnosticHandler.throwOnError = true), | |
| 258 new OptionHandler('--suppress-warnings', | |
| 259 (_) => diagnosticHandler.showWarnings = false), | |
| 260 new OptionHandler('--suppress-hints', | |
| 261 (_) => diagnosticHandler.showHints = false), | |
| 262 new OptionHandler('--output-type=dart|--output-type=js', setOutputType), | |
| 263 new OptionHandler('--verbose', setVerbose), | |
| 264 new OptionHandler('--version', (_) => wantVersion = true), | |
| 265 new OptionHandler('--library-root=.+', setLibraryRoot), | |
| 266 new OptionHandler('--out=.+|-o.*', setOutput, multipleArguments: true), | |
| 267 new OptionHandler('--allow-mock-compilation', passThrough), | |
| 268 new OptionHandler('--minify', passThrough), | |
| 269 new OptionHandler('--force-strip=.*', setStrip), | |
| 270 new OptionHandler('--disable-diagnostic-colors', | |
| 271 (_) => diagnosticHandler.enableColors = false), | |
| 272 new OptionHandler('--enable-diagnostic-colors', | |
| 273 (_) => diagnosticHandler.enableColors = true), | |
| 274 new OptionHandler('--enable[_-]checked[_-]mode|--checked', | |
| 275 (_) => passThrough('--enable-checked-mode')), | |
| 276 new OptionHandler('--enable-concrete-type-inference', | |
| 277 (_) => passThrough('--enable-concrete-type-inference')), | |
| 278 new OptionHandler('--trust-type-annotations', | |
| 279 (_) => passThrough('--trust-type-annotations')), | |
| 280 new OptionHandler(r'--help|/\?|/h', (_) => wantHelp = true), | |
| 281 new OptionHandler('--package-root=.+|-p.+', setPackageRoot), | |
| 282 new OptionHandler('--analyze-all', passThrough), | |
| 283 new OptionHandler('--analyze-only', setAnalyzeOnly), | |
| 284 new OptionHandler('--analyze-signatures-only', passThrough), | |
| 285 new OptionHandler('--disable-native-live-type-analysis', passThrough), | |
| 286 new OptionHandler('--categories=.*', setCategories), | |
| 287 new OptionHandler('--global-js-name=.*', checkGlobalName), | |
| 288 new OptionHandler('--disable-type-inference', passThrough), | |
| 289 new OptionHandler('--terse', passThrough), | |
| 290 new OptionHandler('--disallow-unsafe-eval', | |
| 291 (_) => hasDisallowUnsafeEval = true), | |
| 292 new OptionHandler('-D.+=.*', addInEnvironment), | |
| 293 | |
| 294 // The following two options must come last. | |
| 295 new OptionHandler('-.*', (String argument) { | |
| 296 helpAndFail('Error: Unknown option "$argument".'); | |
| 297 }), | |
| 298 new OptionHandler('.*', (String argument) { | |
| 299 arguments.add(nativeToUriPath(argument)); | |
| 300 }) | |
| 301 ]; | |
| 302 | |
| 303 parseCommandLine(handlers, argv); | |
| 304 if (wantHelp || wantVersion) { | |
| 305 helpAndExit(wantHelp, wantVersion, diagnosticHandler.verbose); | |
| 306 } | |
| 307 | |
| 308 if (hasDisallowUnsafeEval) { | |
| 309 String precompiledName = | |
| 310 relativize(currentDirectory, computePrecompiledUri(), isWindows); | |
| 311 helpAndFail("Error: option '--disallow-unsafe-eval' has been removed." | |
| 312 " Instead, the compiler generates a file named" | |
| 313 " '$precompiledName'."); | |
| 314 } | |
| 315 | |
| 316 if (outputLanguage != OUTPUT_LANGUAGE_DART && stripArgumentSet) { | |
| 317 helpAndFail('Error: --force-strip may only be used with ' | |
| 318 '--output-type=dart'); | |
| 319 } | |
| 320 if (arguments.isEmpty) { | |
| 321 helpAndFail('Error: No Dart file specified.'); | |
| 322 } | |
| 323 if (arguments.length > 1) { | |
| 324 var extra = arguments.sublist(1); | |
| 325 helpAndFail('Error: Extra arguments: ${extra.join(" ")}'); | |
| 326 } | |
| 327 | |
| 328 Uri uri = currentDirectory.resolve(arguments[0]); | |
| 329 if (packageRoot == null) { | |
| 330 packageRoot = uri.resolve('./packages/'); | |
| 331 } | |
| 332 | |
| 333 diagnosticHandler.info('package root is $packageRoot'); | |
| 334 | |
| 335 int totalCharactersWritten = 0; | |
| 336 | |
| 337 options.add('--source-map=$sourceMapOut'); | |
| 338 | |
| 339 compilationDone(String code) { | |
| 340 if (analyzeOnly) return; | |
| 341 if (code == null) { | |
| 342 fail('Error: Compilation failed.'); | |
| 343 } | |
| 344 writeString(Uri.parse('$out.deps'), | |
| 345 getDepsOutput(inputProvider.sourceFiles)); | |
| 346 diagnosticHandler.info( | |
| 347 'compiled ${inputProvider.dartCharactersRead} characters Dart ' | |
| 348 '-> $totalCharactersWritten characters $outputLanguage ' | |
| 349 'in ${relativize(currentDirectory, out, isWindows)}'); | |
| 350 if (!explicitOut) { | |
| 351 String input = uriPathToNative(arguments[0]); | |
| 352 String output = relativize(currentDirectory, out, isWindows); | |
| 353 print('Dart file ($input) compiled to $outputLanguage: $output'); | |
| 354 } | |
| 355 } | |
| 356 | |
| 357 EventSink<String> outputProvider(String name, String extension) { | |
| 358 Uri uri; | |
| 359 String sourceMapFileName; | |
| 360 bool isPrimaryOutput = false; | |
| 361 if (name == '') { | |
| 362 if (extension == 'js' || extension == 'dart') { | |
| 363 isPrimaryOutput = true; | |
| 364 uri = out; | |
| 365 sourceMapFileName = | |
| 366 sourceMapOut.path.substring(sourceMapOut.path.lastIndexOf('/') + 1 ); | |
| 367 } else if (extension == 'precompiled.js') { | |
| 368 uri = computePrecompiledUri(); | |
| 369 diagnosticHandler.info( | |
| 370 "File ($uri) is compatible with header" | |
| 371 " \"Content-Security-Policy: script-src 'self'\""); | |
| 372 } else if (extension == 'js.map' || extension == 'dart.map') { | |
| 373 uri = sourceMapOut; | |
| 374 } else { | |
| 375 fail('Error: Unknown extension: $extension'); | |
| 376 } | |
| 377 } else { | |
| 378 uri = out.resolve('$name.$extension'); | |
| 379 } | |
| 380 | |
| 381 if (uri.scheme != 'file') { | |
| 382 fail('Error: Unhandled scheme ${uri.scheme} in $uri.'); | |
| 383 } | |
| 384 | |
| 385 RandomAccessFile output = | |
| 386 new File(uriPathToNative(uri.path)).openSync(mode: FileMode.WRITE); | |
| 387 int charactersWritten = 0; | |
| 388 | |
| 389 writeStringSync(String data) { | |
| 390 // Write the data in chunks of 8kb, otherwise we risk running OOM | |
| 391 int chunkSize = 8*1024; | |
| 392 | |
| 393 int offset = 0; | |
| 394 while (offset < data.length) { | |
| 395 output.writeStringSync( | |
| 396 data.substring(offset, math.min(offset + chunkSize, data.length))) ; | |
| 397 offset += chunkSize; | |
| 398 } | |
| 399 charactersWritten += data.length; | |
| 400 } | |
| 401 | |
| 402 onDone() { | |
| 403 if (sourceMapFileName != null) { | |
| 404 // Using # is the new proposed standard. @ caused problems in Internet | |
| 405 // Explorer due to "Conditional Compilation Statements" in JScript, | |
| 406 // see: | |
| 407 // http://msdn.microsoft.com/en-us/library/7kx09ct1(v=vs.80).aspx | |
| 408 // About source maps, see: | |
| 409 // https://docs.google.com/a/google.com/document/d/1U1RGAehQwRypUTovF1 KRlpiOFze0b-_2gc6fAH0KY0k/edit | |
| 410 // TODO(http://dartbug.com/11914): Remove @ line. | |
| 411 String sourceMapTag = ''' | |
| 412 | |
| 413 //# sourceMappingURL=$sourceMapFileName | |
| 414 //@ sourceMappingURL=$sourceMapFileName | |
| 415 '''; | |
| 416 writeStringSync(sourceMapTag); | |
| 417 } | |
| 418 output.closeSync(); | |
| 419 if (isPrimaryOutput) { | |
| 420 totalCharactersWritten += charactersWritten; | |
| 421 } | |
| 422 } | |
| 423 | |
| 424 return new EventSinkWrapper(writeStringSync, onDone); | |
| 425 } | |
| 426 | |
| 427 return compileFunc(uri, libraryRoot, packageRoot, | |
| 428 inputProvider, diagnosticHandler, | |
| 429 options, outputProvider, environment) | |
| 430 .then(compilationDone); | |
| 431 } | |
| 432 | |
| 433 void writeString(Uri uri, String text) { | |
| 434 if (uri.scheme != 'file') { | |
| 435 fail('Error: Unhandled scheme ${uri.scheme}.'); | |
| 436 } | |
| 437 var file = new File(uriPathToNative(uri.path)).openSync(mode: FileMode.WRITE ); | |
| 438 file.writeStringSync(text); | |
| 439 file.closeSync(); | |
| 440 } | |
| 441 | |
| 442 void fail(String message) { | |
| 443 if (diagnosticHandler != null) { | |
| 444 diagnosticHandler.diagnosticHandler( | |
| 445 null, -1, -1, message, api.Diagnostic.ERROR); | |
| 446 } else { | |
| 447 print(message); | |
| 448 } | |
| 449 exitFunc(1); | |
| 450 } | |
| 451 | |
| 452 Future compilerMain(List<String> arguments) { | |
| 453 var root = uriPathToNative("/$LIBRARY_ROOT"); | |
| 454 arguments = <String>['--library-root=${Platform.script.toFilePath()}$root'] | |
| 455 ..addAll(arguments); | |
| 456 return compile(arguments); | |
| 457 } | |
| 458 | |
| 459 void helpAndExit(bool wantHelp, bool wantVersion, bool verbose) { | |
| 460 if (wantVersion) { | |
| 461 var version = (BUILD_ID == null) | |
| 462 ? '<non-SDK build>' | |
| 463 : BUILD_ID; | |
| 464 print('Dart-to-JavaScript compiler (dart2js) version: $version'); | |
| 465 } | |
| 466 if (wantHelp) { | |
| 467 if (verbose) { | |
| 468 verboseHelp(); | |
| 469 } else { | |
| 470 help(); | |
| 471 } | |
| 472 } | |
| 473 exitFunc(0); | |
| 474 } | |
| 475 | |
| 476 void helpAndFail(String message) { | |
| 477 help(); | |
| 478 print(''); | |
| 479 fail(message); | |
| 480 } | |
| 60 } | 481 } |
| 61 | 482 |
| 62 String extractPath(String argument) { | |
| 63 String path = nativeToUriPath(extractParameter(argument)); | |
| 64 return path.endsWith("/") ? path : "$path/"; | |
| 65 } | |
| 66 | |
| 67 void parseCommandLine(List<OptionHandler> handlers, List<String> argv) { | |
| 68 // TODO(ahe): Use ../../args/args.dart for parsing options instead. | |
| 69 var patterns = <String>[]; | |
| 70 for (OptionHandler handler in handlers) { | |
| 71 patterns.add(handler.pattern); | |
| 72 } | |
| 73 var pattern = new RegExp('^(${patterns.join(")\$|(")})\$'); | |
| 74 | |
| 75 Iterator<String> arguments = argv.iterator; | |
| 76 OUTER: while (arguments.moveNext()) { | |
| 77 String argument = arguments.current; | |
| 78 Match match = pattern.firstMatch(argument); | |
| 79 assert(match.groupCount == handlers.length); | |
| 80 for (int i = 0; i < handlers.length; i++) { | |
| 81 if (match[i + 1] != null) { | |
| 82 OptionHandler handler = handlers[i]; | |
| 83 if (handler.multipleArguments) { | |
| 84 handler.handle(arguments); | |
| 85 } else { | |
| 86 handler.handle(argument); | |
| 87 } | |
| 88 continue OUTER; | |
| 89 } | |
| 90 } | |
| 91 throw 'Internal error: "$argument" did not match'; | |
| 92 } | |
| 93 } | |
| 94 | |
| 95 FormattingDiagnosticHandler diagnosticHandler; | |
| 96 | |
| 97 Future compile(List<String> argv) { | |
| 98 bool isWindows = (Platform.operatingSystem == 'windows'); | |
| 99 stackTraceFilePrefix = '$currentDirectory'; | |
| 100 Uri libraryRoot = currentDirectory; | |
| 101 Uri out = currentDirectory.resolve('out.js'); | |
| 102 Uri sourceMapOut = currentDirectory.resolve('out.js.map'); | |
| 103 Uri packageRoot = null; | |
| 104 List<String> options = new List<String>(); | |
| 105 bool explicitOut = false; | |
| 106 bool wantHelp = false; | |
| 107 bool wantVersion = false; | |
| 108 String outputLanguage = 'JavaScript'; | |
| 109 bool stripArgumentSet = false; | |
| 110 bool analyzeOnly = false; | |
| 111 bool hasDisallowUnsafeEval = false; | |
| 112 // TODO(johnniwinther): Measure time for reading files. | |
| 113 SourceFileProvider inputProvider = new CompilerSourceFileProvider(); | |
| 114 diagnosticHandler = new FormattingDiagnosticHandler(inputProvider); | |
| 115 Map<String, dynamic> environment = new Map<String, dynamic>(); | |
| 116 | |
| 117 passThrough(String argument) => options.add(argument); | |
| 118 | |
| 119 if (BUILD_ID != null) { | |
| 120 passThrough("--build-id=$BUILD_ID"); | |
| 121 } | |
| 122 | |
| 123 setLibraryRoot(String argument) { | |
| 124 libraryRoot = currentDirectory.resolve(extractPath(argument)); | |
| 125 } | |
| 126 | |
| 127 setPackageRoot(String argument) { | |
| 128 packageRoot = currentDirectory.resolve(extractPath(argument)); | |
| 129 } | |
| 130 | |
| 131 setOutput(Iterator<String> arguments) { | |
| 132 String path; | |
| 133 if (arguments.current == '-o') { | |
| 134 if (!arguments.moveNext()) { | |
| 135 helpAndFail('Error: Missing file after -o option.'); | |
| 136 } | |
| 137 path = arguments.current; | |
| 138 } else { | |
| 139 path = extractParameter(arguments.current); | |
| 140 } | |
| 141 explicitOut = true; | |
| 142 out = currentDirectory.resolve(nativeToUriPath(path)); | |
| 143 sourceMapOut = Uri.parse('$out.map'); | |
| 144 } | |
| 145 | |
| 146 setOutputType(String argument) { | |
| 147 if (argument == '--output-type=dart') { | |
| 148 outputLanguage = OUTPUT_LANGUAGE_DART; | |
| 149 if (!explicitOut) { | |
| 150 out = currentDirectory.resolve('out.dart'); | |
| 151 sourceMapOut = currentDirectory.resolve('out.dart.map'); | |
| 152 } | |
| 153 } | |
| 154 passThrough(argument); | |
| 155 } | |
| 156 | |
| 157 String getDepsOutput(Map<String, SourceFile> sourceFiles) { | |
| 158 var filenames = new List.from(sourceFiles.keys); | |
| 159 filenames.sort(); | |
| 160 return filenames.join("\n"); | |
| 161 } | |
| 162 | |
| 163 setStrip(String argument) { | |
| 164 stripArgumentSet = true; | |
| 165 passThrough(argument); | |
| 166 } | |
| 167 | |
| 168 setAnalyzeOnly(String argument) { | |
| 169 analyzeOnly = true; | |
| 170 passThrough(argument); | |
| 171 } | |
| 172 | |
| 173 setVerbose(_) { | |
| 174 diagnosticHandler.verbose = true; | |
| 175 passThrough('--verbose'); | |
| 176 } | |
| 177 | |
| 178 addInEnvironment(String argument) { | |
| 179 int eqIndex = argument.indexOf('='); | |
| 180 String name = argument.substring(2, eqIndex); | |
| 181 String value = argument.substring(eqIndex + 1); | |
| 182 environment[name] = value; | |
| 183 } | |
| 184 | |
| 185 setCategories(String argument) { | |
| 186 List<String> categories = extractParameter(argument).split(','); | |
| 187 Set<String> allowedCategories = | |
| 188 LIBRARIES.values.map((x) => x.category).toSet(); | |
| 189 allowedCategories.remove('Shared'); | |
| 190 allowedCategories.remove('Internal'); | |
| 191 List<String> allowedCategoriesList = | |
| 192 new List<String>.from(allowedCategories); | |
| 193 allowedCategoriesList.sort(); | |
| 194 if (categories.contains('all')) { | |
| 195 categories = allowedCategoriesList; | |
| 196 } else { | |
| 197 String allowedCategoriesString = allowedCategoriesList.join(', '); | |
| 198 for (String category in categories) { | |
| 199 if (!allowedCategories.contains(category)) { | |
| 200 fail('Error: unsupported library category "$category", ' | |
| 201 'supported categories are: $allowedCategoriesString'); | |
| 202 } | |
| 203 } | |
| 204 } | |
| 205 passThrough('--categories=${categories.join(",")}'); | |
| 206 } | |
| 207 | |
| 208 checkGlobalName(String argument) { | |
| 209 String globalName = extractParameter(argument); | |
| 210 if (!new RegExp(r'^\$[a-z]*$').hasMatch(globalName)) { | |
| 211 fail('Error: "$globalName" must match "\\\$[a-z]*"'); | |
| 212 } | |
| 213 passThrough(argument); | |
| 214 } | |
| 215 | |
| 216 handleShortOptions(String argument) { | |
| 217 var shortOptions = argument.substring(1).split(""); | |
| 218 for (var shortOption in shortOptions) { | |
| 219 switch (shortOption) { | |
| 220 case 'v': | |
| 221 setVerbose(null); | |
| 222 break; | |
| 223 case 'h': | |
| 224 case '?': | |
| 225 wantHelp = true; | |
| 226 break; | |
| 227 case 'c': | |
| 228 passThrough('--enable-checked-mode'); | |
| 229 break; | |
| 230 default: | |
| 231 throw 'Internal error: "$shortOption" did not match'; | |
| 232 } | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 Uri computePrecompiledUri() { | |
| 237 String extension = 'precompiled.js'; | |
| 238 String outPath = out.path; | |
| 239 if (outPath.endsWith('.js')) { | |
| 240 outPath = outPath.substring(0, outPath.length - 3); | |
| 241 return out.resolve('$outPath.$extension'); | |
| 242 } else { | |
| 243 return out.resolve(extension); | |
| 244 } | |
| 245 } | |
| 246 | |
| 247 List<String> arguments = <String>[]; | |
| 248 List<OptionHandler> handlers = <OptionHandler>[ | |
| 249 new OptionHandler('-[chv?]+', handleShortOptions), | |
| 250 new OptionHandler('--throw-on-error', | |
| 251 (_) => diagnosticHandler.throwOnError = true), | |
| 252 new OptionHandler('--suppress-warnings', | |
| 253 (_) => diagnosticHandler.showWarnings = false), | |
| 254 new OptionHandler('--suppress-hints', | |
| 255 (_) => diagnosticHandler.showHints = false), | |
| 256 new OptionHandler('--output-type=dart|--output-type=js', setOutputType), | |
| 257 new OptionHandler('--verbose', setVerbose), | |
| 258 new OptionHandler('--version', (_) => wantVersion = true), | |
| 259 new OptionHandler('--library-root=.+', setLibraryRoot), | |
| 260 new OptionHandler('--out=.+|-o.*', setOutput, multipleArguments: true), | |
| 261 new OptionHandler('--allow-mock-compilation', passThrough), | |
| 262 new OptionHandler('--minify', passThrough), | |
| 263 new OptionHandler('--force-strip=.*', setStrip), | |
| 264 new OptionHandler('--disable-diagnostic-colors', | |
| 265 (_) => diagnosticHandler.enableColors = false), | |
| 266 new OptionHandler('--enable-diagnostic-colors', | |
| 267 (_) => diagnosticHandler.enableColors = true), | |
| 268 new OptionHandler('--enable[_-]checked[_-]mode|--checked', | |
| 269 (_) => passThrough('--enable-checked-mode')), | |
| 270 new OptionHandler('--enable-concrete-type-inference', | |
| 271 (_) => passThrough('--enable-concrete-type-inference')), | |
| 272 new OptionHandler('--trust-type-annotations', | |
| 273 (_) => passThrough('--trust-type-annotations')), | |
| 274 new OptionHandler(r'--help|/\?|/h', (_) => wantHelp = true), | |
| 275 new OptionHandler('--package-root=.+|-p.+', setPackageRoot), | |
| 276 new OptionHandler('--analyze-all', passThrough), | |
| 277 new OptionHandler('--analyze-only', setAnalyzeOnly), | |
| 278 new OptionHandler('--analyze-signatures-only', passThrough), | |
| 279 new OptionHandler('--disable-native-live-type-analysis', passThrough), | |
| 280 new OptionHandler('--categories=.*', setCategories), | |
| 281 new OptionHandler('--global-js-name=.*', checkGlobalName), | |
| 282 new OptionHandler('--disable-type-inference', passThrough), | |
| 283 new OptionHandler('--terse', passThrough), | |
| 284 new OptionHandler('--disallow-unsafe-eval', | |
| 285 (_) => hasDisallowUnsafeEval = true), | |
| 286 new OptionHandler('-D.+=.*', addInEnvironment), | |
| 287 | |
| 288 // The following two options must come last. | |
| 289 new OptionHandler('-.*', (String argument) { | |
| 290 helpAndFail('Error: Unknown option "$argument".'); | |
| 291 }), | |
| 292 new OptionHandler('.*', (String argument) { | |
| 293 arguments.add(nativeToUriPath(argument)); | |
| 294 }) | |
| 295 ]; | |
| 296 | |
| 297 parseCommandLine(handlers, argv); | |
| 298 if (wantHelp || wantVersion) { | |
| 299 helpAndExit(wantHelp, wantVersion, diagnosticHandler.verbose); | |
| 300 } | |
| 301 | |
| 302 if (hasDisallowUnsafeEval) { | |
| 303 String precompiledName = | |
| 304 relativize(currentDirectory, computePrecompiledUri(), isWindows); | |
| 305 helpAndFail("Error: option '--disallow-unsafe-eval' has been removed." | |
| 306 " Instead, the compiler generates a file named" | |
| 307 " '$precompiledName'."); | |
| 308 } | |
| 309 | |
| 310 if (outputLanguage != OUTPUT_LANGUAGE_DART && stripArgumentSet) { | |
| 311 helpAndFail('Error: --force-strip may only be used with ' | |
| 312 '--output-type=dart'); | |
| 313 } | |
| 314 if (arguments.isEmpty) { | |
| 315 helpAndFail('Error: No Dart file specified.'); | |
| 316 } | |
| 317 if (arguments.length > 1) { | |
| 318 var extra = arguments.sublist(1); | |
| 319 helpAndFail('Error: Extra arguments: ${extra.join(" ")}'); | |
| 320 } | |
| 321 | |
| 322 Uri uri = currentDirectory.resolve(arguments[0]); | |
| 323 if (packageRoot == null) { | |
| 324 packageRoot = uri.resolve('./packages/'); | |
| 325 } | |
| 326 | |
| 327 diagnosticHandler.info('package root is $packageRoot'); | |
| 328 | |
| 329 int totalCharactersWritten = 0; | |
| 330 | |
| 331 options.add('--source-map=$sourceMapOut'); | |
| 332 | |
| 333 compilationDone(String code) { | |
| 334 if (analyzeOnly) return; | |
| 335 if (code == null) { | |
| 336 fail('Error: Compilation failed.'); | |
| 337 } | |
| 338 writeString(Uri.parse('$out.deps'), | |
| 339 getDepsOutput(inputProvider.sourceFiles)); | |
| 340 diagnosticHandler.info( | |
| 341 'compiled ${inputProvider.dartCharactersRead} characters Dart ' | |
| 342 '-> $totalCharactersWritten characters $outputLanguage ' | |
| 343 'in ${relativize(currentDirectory, out, isWindows)}'); | |
| 344 if (!explicitOut) { | |
| 345 String input = uriPathToNative(arguments[0]); | |
| 346 String output = relativize(currentDirectory, out, isWindows); | |
| 347 print('Dart file ($input) compiled to $outputLanguage: $output'); | |
| 348 } | |
| 349 } | |
| 350 | |
| 351 EventSink<String> outputProvider(String name, String extension) { | |
| 352 Uri uri; | |
| 353 String sourceMapFileName; | |
| 354 bool isPrimaryOutput = false; | |
| 355 if (name == '') { | |
| 356 if (extension == 'js' || extension == 'dart') { | |
| 357 isPrimaryOutput = true; | |
| 358 uri = out; | |
| 359 sourceMapFileName = | |
| 360 sourceMapOut.path.substring(sourceMapOut.path.lastIndexOf('/') + 1); | |
| 361 } else if (extension == 'precompiled.js') { | |
| 362 uri = computePrecompiledUri(); | |
| 363 diagnosticHandler.info( | |
| 364 "File ($uri) is compatible with header" | |
| 365 " \"Content-Security-Policy: script-src 'self'\""); | |
| 366 } else if (extension == 'js.map' || extension == 'dart.map') { | |
| 367 uri = sourceMapOut; | |
| 368 } else { | |
| 369 fail('Error: Unknown extension: $extension'); | |
| 370 } | |
| 371 } else { | |
| 372 uri = out.resolve('$name.$extension'); | |
| 373 } | |
| 374 | |
| 375 if (uri.scheme != 'file') { | |
| 376 fail('Error: Unhandled scheme ${uri.scheme} in $uri.'); | |
| 377 } | |
| 378 | |
| 379 RandomAccessFile output = | |
| 380 new File(uriPathToNative(uri.path)).openSync(mode: FileMode.WRITE); | |
| 381 int charactersWritten = 0; | |
| 382 | |
| 383 writeStringSync(String data) { | |
| 384 // Write the data in chunks of 8kb, otherwise we risk running OOM | |
| 385 int chunkSize = 8*1024; | |
| 386 | |
| 387 int offset = 0; | |
| 388 while (offset < data.length) { | |
| 389 output.writeStringSync( | |
| 390 data.substring(offset, math.min(offset + chunkSize, data.length))); | |
| 391 offset += chunkSize; | |
| 392 } | |
| 393 charactersWritten += data.length; | |
| 394 } | |
| 395 | |
| 396 onDone() { | |
| 397 if (sourceMapFileName != null) { | |
| 398 // Using # is the new proposed standard. @ caused problems in Internet | |
| 399 // Explorer due to "Conditional Compilation Statements" in JScript, | |
| 400 // see: | |
| 401 // http://msdn.microsoft.com/en-us/library/7kx09ct1(v=vs.80).aspx | |
| 402 // About source maps, see: | |
| 403 // https://docs.google.com/a/google.com/document/d/1U1RGAehQwRypUTovF1KR lpiOFze0b-_2gc6fAH0KY0k/edit | |
| 404 // TODO(http://dartbug.com/11914): Remove @ line. | |
| 405 String sourceMapTag = ''' | |
| 406 | |
| 407 //# sourceMappingURL=$sourceMapFileName | |
| 408 //@ sourceMappingURL=$sourceMapFileName | |
| 409 '''; | |
| 410 writeStringSync(sourceMapTag); | |
| 411 } | |
| 412 output.closeSync(); | |
| 413 if (isPrimaryOutput) { | |
| 414 totalCharactersWritten += charactersWritten; | |
| 415 } | |
| 416 } | |
| 417 | |
| 418 return new EventSinkWrapper(writeStringSync, onDone); | |
| 419 } | |
| 420 | |
| 421 return api.compile(uri, libraryRoot, packageRoot, | |
| 422 inputProvider, diagnosticHandler, | |
| 423 options, outputProvider, environment) | |
| 424 .then(compilationDone); | |
| 425 } | |
| 426 | |
| 427 class EventSinkWrapper extends EventSink<String> { | 483 class EventSinkWrapper extends EventSink<String> { |
| 428 var onAdd, onClose; | 484 var onAdd, onClose; |
| 429 | 485 |
| 430 EventSinkWrapper(this.onAdd, this.onClose); | 486 EventSinkWrapper(this.onAdd, this.onClose); |
| 431 | 487 |
| 432 void add(String data) => onAdd(data); | 488 void add(String data) => onAdd(data); |
| 433 | 489 |
| 434 void addError(error, [StackTrace stackTrace]) => throw error; | 490 void addError(error, [StackTrace stackTrace]) => throw error; |
| 435 | 491 |
| 436 void close() => onClose(); | 492 void close() => onClose(); |
| 437 } | 493 } |
| 438 | 494 |
| 439 class AbortLeg { | 495 class AbortLeg { |
| 440 final message; | 496 final message; |
| 441 AbortLeg(this.message); | 497 AbortLeg(this.message); |
| 442 toString() => 'Aborted due to --throw-on-error: $message'; | 498 toString() => 'Aborted due to --throw-on-error: $message'; |
| 443 } | 499 } |
| 444 | 500 |
| 445 void writeString(Uri uri, String text) { | |
| 446 if (uri.scheme != 'file') { | |
| 447 fail('Error: Unhandled scheme ${uri.scheme}.'); | |
| 448 } | |
| 449 var file = new File(uriPathToNative(uri.path)).openSync(mode: FileMode.WRITE); | |
| 450 file.writeStringSync(text); | |
| 451 file.closeSync(); | |
| 452 } | |
| 453 | |
| 454 void fail(String message) { | |
| 455 if (diagnosticHandler != null) { | |
| 456 diagnosticHandler.diagnosticHandler( | |
| 457 null, -1, -1, message, api.Diagnostic.ERROR); | |
| 458 } else { | |
| 459 print(message); | |
| 460 } | |
| 461 exit(1); | |
| 462 } | |
| 463 | |
| 464 Future compilerMain(List<String> arguments) { | |
| 465 var root = uriPathToNative("/$LIBRARY_ROOT"); | |
| 466 arguments = <String>['--library-root=${Platform.script.toFilePath()}$root'] | |
| 467 ..addAll(arguments); | |
| 468 return compile(arguments); | |
| 469 } | |
| 470 | |
| 471 void help() { | 501 void help() { |
| 472 // This message should be no longer than 20 lines. The default | 502 // This message should be no longer than 20 lines. The default |
| 473 // terminal size normally 80x24. Two lines are used for the prompts | 503 // terminal size normally 80x24. Two lines are used for the prompts |
| 474 // before and after running the compiler. Another two lines may be | 504 // before and after running the compiler. Another two lines may be |
| 475 // used to print an error message. | 505 // used to print an error message. |
| 476 print(''' | 506 print(''' |
| 477 Usage: dart2js [options] dartfile | 507 Usage: dart2js [options] dartfile |
| 478 | 508 |
| 479 Compiles Dart to JavaScript. | 509 Compiles Dart to JavaScript. |
| 480 | 510 |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 571 all categories, use --categories=all. | 601 all categories, use --categories=all. |
| 572 | 602 |
| 573 --global-js-name=<name> | 603 --global-js-name=<name> |
| 574 By default, dart2js generates JavaScript output that uses a global | 604 By default, dart2js generates JavaScript output that uses a global |
| 575 variable named "$". The name of this global can be overridden | 605 variable named "$". The name of this global can be overridden |
| 576 with this option. The name must match the regular expression "\$[a-z]*". | 606 with this option. The name must match the regular expression "\$[a-z]*". |
| 577 | 607 |
| 578 '''.trim()); | 608 '''.trim()); |
| 579 } | 609 } |
| 580 | 610 |
| 581 void helpAndExit(bool wantHelp, bool wantVersion, bool verbose) { | 611 Future internalMain(Function exitFunc, Function compileFunc, |
| 582 if (wantVersion) { | 612 List<String> arguments) { |
| 583 var version = (BUILD_ID == null) | 613 var dart2js = new _Dart2Js(exitFunc, compileFunc); |
| 584 ? '<non-SDK build>' | 614 |
| 585 : BUILD_ID; | 615 onError(exception, trace) { |
| 586 print('Dart-to-JavaScript compiler (dart2js) version: $version'); | 616 if (exception is CompilerCrashedException) { |
|
ahe
2013/11/21 12:44:53
This looks like a break down of the API to me.
Co
| |
| 587 } | 617 // Error and stack trace already reported. |
| 588 if (wantHelp) { | 618 exitFunc(253); // 253 is recognized as a crash by our test scripts. |
| 589 if (verbose) { | |
| 590 verboseHelp(); | |
| 591 } else { | 619 } else { |
| 592 help(); | 620 try { |
| 621 print('Internal error: $exception'); | |
| 622 } catch (ignored) { | |
| 623 print('Internal error: error while printing exception'); | |
| 624 } | |
| 625 | |
| 626 try { | |
| 627 if (trace != null) { | |
| 628 print(trace); | |
| 629 } | |
| 630 } finally { | |
| 631 exitFunc(253); // 253 is recognized as a crash by our test scripts. | |
| 632 } | |
| 593 } | 633 } |
| 594 } | 634 } |
| 595 exit(0); | |
| 596 } | |
| 597 | 635 |
| 598 void helpAndFail(String message) { | 636 try { |
| 599 help(); | 637 return dart2js.compilerMain(arguments).catchError(onError); |
| 600 print(''); | 638 } catch (exception, trace) { |
| 601 fail(message); | 639 onError(exception, trace); |
| 640 return new Future.value(); | |
| 641 } | |
| 602 } | 642 } |
| 603 | 643 |
| 604 void main(List<String> arguments) { | 644 void main(List<String> arguments) { |
| 605 runZoned(() => compilerMain(arguments), onError: (exception, trace) { | 645 internalMain(exit, api.compile, arguments); |
| 606 try { | |
| 607 print('Internal error: $exception'); | |
| 608 } catch (ignored) { | |
| 609 print('Internal error: error while printing exception'); | |
| 610 } | |
| 611 | |
| 612 try { | |
| 613 if (trace != null) { | |
| 614 print(trace); | |
| 615 } | |
| 616 } finally { | |
| 617 exit(253); // 253 is recognized as a crash by our test scripts. | |
| 618 } | |
| 619 }); | |
| 620 } | 646 } |
| OLD | NEW |