Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, 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 import 'dart:convert' show JSON; | 5 import 'dart:convert' show JSON; |
| 6 import 'dart:io'; | 6 import 'dart:io'; |
| 7 import 'package:args/command_runner.dart'; | |
| 8 import 'package:analyzer/src/generated/source.dart' show Source; | 7 import 'package:analyzer/src/generated/source.dart' show Source; |
| 9 import 'package:analyzer/src/summary/package_bundle_reader.dart' | 8 import 'package:analyzer/src/summary/package_bundle_reader.dart' |
| 10 show InSummarySource; | 9 show InSummarySource; |
| 10 import 'package:args/args.dart' show ArgParser, ArgResults; | |
| 11 import 'package:args/command_runner.dart' show UsageException; | |
| 12 import 'package:path/path.dart' as path; | |
| 13 | |
| 11 import 'compiler.dart' | 14 import 'compiler.dart' |
| 12 show BuildUnit, CompilerOptions, JSModuleFile, ModuleCompiler; | 15 show BuildUnit, CompilerOptions, JSModuleFile, ModuleCompiler; |
| 13 import '../analyzer/context.dart' show AnalyzerOptions; | 16 import '../analyzer/context.dart' show AnalyzerOptions; |
| 14 import 'package:path/path.dart' as path; | |
| 15 | 17 |
| 16 typedef void MessageHandler(Object message); | 18 final ArgParser _argParser = () { |
|
Jennifer Messerly
2016/08/12 21:09:23
In case you're curious, our type inference would t
| |
| 19 var argParser = new ArgParser() | |
| 20 ..addOption('out', abbr: 'o', help: 'Output file (required)') | |
| 21 ..addOption('module-root', | |
| 22 help: 'Root module directory.\n' | |
| 23 'Generated module paths are relative to this root.') | |
| 24 ..addOption('library-root', | |
| 25 help: 'Root of source files.\n' | |
| 26 'Generated library names are relative to this root.') | |
| 27 ..addOption('build-root', | |
| 28 help: 'Deprecated in favor of --library-root', hide: true); | |
| 29 AnalyzerOptions.addArguments(argParser); | |
| 30 CompilerOptions.addArguments(argParser); | |
| 31 return argParser; | |
| 32 }(); | |
| 17 | 33 |
| 18 /// The command for invoking the modular compiler. | 34 /// The runs a single compile for dartdevc. |
|
nweiz
2016/08/12 21:16:38
Remove "The".
Jennifer Messerly
2016/08/12 21:58:42
Eeep, good catch!
| |
| 19 class CompileCommand extends Command { | 35 /// |
| 20 final MessageHandler messageHandler; | 36 /// This handles argument parsing, usage, error handling. |
| 21 CompilerOptions _compilerOptions; | 37 /// See bin/dartdevc.dart for the actual entry point, which includes Bazel |
| 22 | 38 /// worker support. |
| 23 CompileCommand({MessageHandler messageHandler}) | 39 int compile(List<String> args, {void printFn(Object obj)}) { |
| 24 : this.messageHandler = messageHandler ?? print { | 40 printFn ??= print; |
| 25 argParser.addOption('out', abbr: 'o', help: 'Output file (required)'); | 41 try { |
| 26 argParser.addOption('module-root', | 42 _compile(_argParser.parse(args), printFn); |
| 27 help: 'Root module directory. ' | 43 return 0; |
| 28 'Generated module paths are relative to this root.'); | 44 } on UsageException catch (error) { |
| 29 argParser.addOption('library-root', | 45 // Incorrect usage, input file not found, etc. |
| 30 help: 'Root of source files. ' | 46 printFn(error); |
| 31 'Generated library names are relative to this root.'); | 47 return 64; |
| 32 argParser.addOption('build-root', | 48 } on CompileErrorException catch (error) { |
| 33 help: 'Deprecated in favor of --library-root'); | 49 // Code has error(s) and failed to compile. |
| 34 CompilerOptions.addArguments(argParser); | 50 printFn(error); |
| 35 AnalyzerOptions.addArguments(argParser); | 51 return 1; |
| 36 } | 52 } catch (error, stackTrace) { |
| 37 | 53 // Anything else is likely a compiler bug. |
| 38 get name => 'compile'; | 54 // |
| 39 get description => 'Compile a set of Dart files into a JavaScript module.'; | 55 // --unsafe-force-compile is a bit of a grey area, but it's nice not to |
| 40 | 56 // crash while compiling |
| 41 @override | 57 // (of course, output code may crash, if it had errors). |
| 42 void run() { | 58 // |
| 43 var compiler = | 59 printFn(''' |
| 44 new ModuleCompiler(new AnalyzerOptions.fromArguments(argResults)); | 60 We're sorry, you've found a bug in our compiler. |
| 45 _compilerOptions = new CompilerOptions.fromArguments(argResults); | 61 You can report this bug at: |
| 46 var outPath = argResults['out']; | 62 https://github.com/dart-lang/dev_compiler/issues |
| 47 | 63 Please include the information below in your report, along with |
| 48 if (outPath == null) { | 64 any other information that may help us track it down. Thanks! |
| 49 usageException('Please include the output file location. For example:\n' | 65 dartdevc arguments: ${args.join(' ')} |
| 50 ' -o PATH/TO/OUTPUT_FILE.js'); | 66 dart --version: ${Platform.version} |
| 51 } | 67 ``` |
| 52 | 68 $error |
| 53 var libraryRoot = argResults['library-root'] as String; | 69 $stackTrace |
| 54 libraryRoot ??= argResults['build-root'] as String; | 70 ```'''); |
| 55 if (libraryRoot != null) { | 71 return 70; |
| 56 libraryRoot = path.absolute(libraryRoot); | |
| 57 } else { | |
| 58 libraryRoot = Directory.current.path; | |
| 59 } | |
| 60 var moduleRoot = argResults['module-root'] as String; | |
| 61 String modulePath; | |
| 62 if (moduleRoot != null) { | |
| 63 moduleRoot = path.absolute(moduleRoot); | |
| 64 if (!path.isWithin(moduleRoot, outPath)) { | |
| 65 usageException('Output file $outPath must be within the module root ' | |
| 66 'directory $moduleRoot'); | |
| 67 } | |
| 68 modulePath = | |
| 69 path.withoutExtension(path.relative(outPath, from: moduleRoot)); | |
| 70 } else { | |
| 71 moduleRoot = path.dirname(outPath); | |
| 72 modulePath = path.basenameWithoutExtension(outPath); | |
| 73 } | |
| 74 | |
| 75 var unit = new BuildUnit(modulePath, libraryRoot, argResults.rest, | |
| 76 (source) => _moduleForLibrary(moduleRoot, source)); | |
| 77 | |
| 78 JSModuleFile module = compiler.compile(unit, _compilerOptions); | |
| 79 module.errors.forEach(messageHandler); | |
| 80 | |
| 81 if (!module.isValid) throw new CompileErrorException(); | |
| 82 | |
| 83 // Write JS file, as well as source map and summary (if requested). | |
| 84 new File(outPath).writeAsStringSync(module.code); | |
| 85 if (module.sourceMap != null) { | |
| 86 var mapPath = outPath + '.map'; | |
| 87 new File(mapPath) | |
| 88 .writeAsStringSync(JSON.encode(module.placeSourceMap(mapPath))); | |
| 89 } | |
| 90 if (module.summaryBytes != null) { | |
| 91 var summaryPath = path.withoutExtension(outPath) + | |
| 92 '.${_compilerOptions.summaryExtension}'; | |
| 93 new File(summaryPath).writeAsBytesSync(module.summaryBytes); | |
| 94 } | |
| 95 } | |
| 96 | |
| 97 String _moduleForLibrary(String moduleRoot, Source source) { | |
| 98 if (source is InSummarySource) { | |
| 99 var summaryPath = source.summaryPath; | |
| 100 var ext = '.${_compilerOptions.summaryExtension}'; | |
| 101 if (path.isWithin(moduleRoot, summaryPath) && summaryPath.endsWith(ext)) { | |
| 102 var buildUnitPath = | |
| 103 summaryPath.substring(0, summaryPath.length - ext.length); | |
| 104 return path.relative(buildUnitPath, from: moduleRoot); | |
| 105 } | |
| 106 | |
| 107 throw usageException( | |
| 108 'Imported file ${source.uri} is not within the module root ' | |
| 109 'directory $moduleRoot'); | |
| 110 } | |
| 111 | |
| 112 throw usageException( | |
| 113 'Imported file "${source.uri}" was not found as a summary or source ' | |
| 114 'file. Please pass in either the summary or the source file ' | |
| 115 'for this import.'); | |
| 116 } | 72 } |
| 117 } | 73 } |
| 118 | 74 |
| 75 void _compile(ArgResults argResults, void printFn(Object obj)) { | |
| 76 var compiler = | |
| 77 new ModuleCompiler(new AnalyzerOptions.fromArguments(argResults)); | |
| 78 var compilerOpts = new CompilerOptions.fromArguments(argResults); | |
| 79 var outPath = argResults['out']; | |
| 80 | |
| 81 if (outPath == null) { | |
| 82 _usageException('Please include the output file location. For example:\n' | |
| 83 ' -o PATH/TO/OUTPUT_FILE.js'); | |
| 84 } | |
| 85 | |
| 86 var libraryRoot = argResults['library-root'] as String; | |
| 87 libraryRoot ??= argResults['build-root'] as String; | |
| 88 if (libraryRoot != null) { | |
| 89 libraryRoot = path.absolute(libraryRoot); | |
| 90 } else { | |
| 91 libraryRoot = Directory.current.path; | |
| 92 } | |
| 93 var moduleRoot = argResults['module-root'] as String; | |
| 94 String modulePath; | |
| 95 if (moduleRoot != null) { | |
| 96 moduleRoot = path.absolute(moduleRoot); | |
| 97 if (!path.isWithin(moduleRoot, outPath)) { | |
| 98 _usageException('Output file $outPath must be within the module root ' | |
| 99 'directory $moduleRoot'); | |
| 100 } | |
| 101 modulePath = | |
| 102 path.withoutExtension(path.relative(outPath, from: moduleRoot)); | |
| 103 } else { | |
| 104 moduleRoot = path.dirname(outPath); | |
| 105 modulePath = path.basenameWithoutExtension(outPath); | |
| 106 } | |
| 107 | |
| 108 var unit = new BuildUnit(modulePath, libraryRoot, argResults.rest, | |
| 109 (source) => _moduleForLibrary(moduleRoot, source, compilerOpts)); | |
| 110 | |
| 111 JSModuleFile module = compiler.compile(unit, compilerOpts); | |
| 112 module.errors.forEach(printFn); | |
| 113 | |
| 114 if (!module.isValid) throw new CompileErrorException(); | |
| 115 | |
| 116 // Write JS file, as well as source map and summary (if requested). | |
| 117 new File(outPath).writeAsStringSync(module.code); | |
| 118 if (module.sourceMap != null) { | |
| 119 var mapPath = outPath + '.map'; | |
| 120 new File(mapPath) | |
| 121 .writeAsStringSync(JSON.encode(module.placeSourceMap(mapPath))); | |
| 122 } | |
| 123 if (module.summaryBytes != null) { | |
| 124 var summaryPath = | |
| 125 path.withoutExtension(outPath) + '.${compilerOpts.summaryExtension}'; | |
| 126 new File(summaryPath).writeAsBytesSync(module.summaryBytes); | |
| 127 } | |
| 128 } | |
| 129 | |
| 130 String _moduleForLibrary( | |
| 131 String moduleRoot, Source source, CompilerOptions compilerOpts) { | |
| 132 if (source is InSummarySource) { | |
| 133 var summaryPath = source.summaryPath; | |
| 134 var ext = '.${compilerOpts.summaryExtension}'; | |
| 135 if (path.isWithin(moduleRoot, summaryPath) && summaryPath.endsWith(ext)) { | |
| 136 var buildUnitPath = | |
| 137 summaryPath.substring(0, summaryPath.length - ext.length); | |
| 138 return path.relative(buildUnitPath, from: moduleRoot); | |
| 139 } | |
| 140 | |
| 141 _usageException('Imported file ${source.uri} is not within the module root ' | |
| 142 'directory $moduleRoot'); | |
| 143 } | |
| 144 | |
| 145 _usageException( | |
| 146 'Imported file "${source.uri}" was not found as a summary or source ' | |
| 147 'file. Please pass in either the summary or the source file ' | |
| 148 'for this import.'); | |
| 149 return null; // unreachable | |
| 150 } | |
| 151 | |
| 152 void _usageException(String message) { | |
| 153 throw new UsageException( | |
| 154 message, | |
| 155 'Dart Development Compiler compiles Dart into a JavaScript module.' | |
| 156 '\n\n${_argParser.usage}'); | |
| 157 } | |
| 158 | |
| 119 /// Thrown when the input source code has errors. | 159 /// Thrown when the input source code has errors. |
| 120 class CompileErrorException implements Exception { | 160 class CompileErrorException implements Exception { |
| 121 toString() => '\nPlease fix all errors before compiling (warnings are okay).'; | 161 toString() => '\nPlease fix all errors before compiling (warnings are okay).'; |
| 122 } | 162 } |
| OLD | NEW |