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