OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
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. | |
4 | |
5 /// Set of flags and options passed to the compiler | |
6 | |
7 import 'dart:io'; | |
8 | |
9 import 'package:args/args.dart'; | |
10 import 'package:cli_util/cli_util.dart' show getSdkDir; | |
11 import 'package:logging/logging.dart' show Level; | |
12 import 'package:path/path.dart' as path; | |
13 import 'package:yaml/yaml.dart'; | |
14 | |
15 import 'utils.dart' show parseEnum, getEnumName; | |
16 | |
17 const String _V8_BINARY_DEFAULT = 'node'; | |
18 const bool _CLOSURE_DEFAULT = false; | |
19 | |
20 /// Older V8 versions do not accept default values with destructuring in | |
21 /// arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them | |
22 /// with regular functions (e.g. `function({a} = {}) { return 1 }`). | |
23 /// | |
24 /// Supporting the syntax: | |
25 /// * Chrome Canary (51) | |
26 /// * Firefox | |
27 /// | |
28 /// Not yet supporting: | |
29 /// * Atom (1.5.4) | |
30 /// * Electron (0.36.3) | |
31 /// | |
32 // TODO(ochafik): Simplify this code when our target platforms catch up. | |
33 const bool _DESTRUCTURE_NAMED_PARAMS_DEFAULT = false; | |
34 | |
35 /// Options used to set up Source URI resolution in the analysis context. | |
36 class SourceResolverOptions { | |
37 /// Whether to resolve 'package:' uris using the multi-package resolver. | |
38 final bool useMultiPackage; | |
39 | |
40 /// Custom URI mappings, such as "dart:foo" -> "path/to/foo.dart" | |
41 final Map<String, String> customUrlMappings; | |
42 | |
43 /// Package root when resolving 'package:' urls the standard way. | |
44 final String packageRoot; | |
45 | |
46 /// List of paths used for the multi-package resolver. | |
47 final List<String> packagePaths; | |
48 | |
49 /// Whether to use a mock-sdk during compilation. | |
50 final bool useMockSdk; | |
51 | |
52 /// Path to the dart-sdk. Null if `useMockSdk` is true or if the path couldn't | |
53 /// be determined | |
54 final String dartSdkPath; | |
55 | |
56 const SourceResolverOptions( | |
57 {this.useMockSdk: false, | |
58 this.dartSdkPath, | |
59 this.useMultiPackage: false, | |
60 this.customUrlMappings: const {}, | |
61 this.packageRoot: 'packages/', | |
62 this.packagePaths: const <String>[]}); | |
63 } | |
64 | |
65 enum ModuleFormat { es6, legacy, node } | |
66 ModuleFormat parseModuleFormat(String s) => parseEnum(s, ModuleFormat.values); | |
67 | |
68 // TODO(jmesserly): refactor all codegen options here. | |
69 class CodegenOptions { | |
70 /// Whether to emit the source map files. | |
71 final bool emitSourceMaps; | |
72 | |
73 /// Whether to force compilation of code with static errors. | |
74 final bool forceCompile; | |
75 | |
76 /// Output directory for generated code. | |
77 final String outputDir; | |
78 | |
79 /// Emit Closure Compiler-friendly code. | |
80 final bool closure; | |
81 | |
82 /// Enable ES6 destructuring of named parameters. | |
83 final bool destructureNamedParams; | |
84 | |
85 /// Which module format to support. | |
86 /// Currently 'es6' and 'legacy' are supported. | |
87 final ModuleFormat moduleFormat; | |
88 | |
89 const CodegenOptions( | |
90 {this.emitSourceMaps: true, | |
91 this.forceCompile: false, | |
92 this.closure: _CLOSURE_DEFAULT, | |
93 this.destructureNamedParams: _DESTRUCTURE_NAMED_PARAMS_DEFAULT, | |
94 this.outputDir, | |
95 this.moduleFormat: ModuleFormat.legacy}); | |
96 } | |
97 | |
98 /// Options for devrun. | |
99 class RunnerOptions { | |
100 /// V8-based binary to be used to run the .js output (d8, iojs, node). | |
101 /// Can be just the executable name if it's in the path, or a path to the | |
102 /// executable. | |
103 final String v8Binary; | |
104 | |
105 const RunnerOptions({this.v8Binary: _V8_BINARY_DEFAULT}); | |
106 } | |
107 | |
108 /// General options used by the dev compiler and server. | |
109 class CompilerOptions { | |
110 final SourceResolverOptions sourceOptions; | |
111 final CodegenOptions codegenOptions; | |
112 final RunnerOptions runnerOptions; | |
113 | |
114 /// Whether to check the sdk libraries. | |
115 final bool checkSdk; | |
116 | |
117 /// Whether to use colors when interacting on the console. | |
118 final bool useColors; | |
119 | |
120 /// Whether the user asked for help. | |
121 final bool help; | |
122 | |
123 /// Whether the user asked for the app version. | |
124 final bool version; | |
125 | |
126 /// Minimum log-level reported on the command-line. | |
127 final Level logLevel; | |
128 | |
129 /// Location for runtime files, such as `dart_runtime.js`. By default this is | |
130 /// inferred to be under `lib/runtime/` in the location of the `dev_compiler` | |
131 /// package (if we can infer where that is located). | |
132 final String runtimeDir; | |
133 | |
134 /// The files to compile. | |
135 final List<String> inputs; | |
136 | |
137 /// The base directory for [inputs]. Module imports will be generated relative | |
138 /// to this directory. | |
139 final String inputBaseDir; | |
140 | |
141 const CompilerOptions( | |
142 {this.sourceOptions: const SourceResolverOptions(), | |
143 this.codegenOptions: const CodegenOptions(), | |
144 this.runnerOptions: const RunnerOptions(), | |
145 this.checkSdk: false, | |
146 this.useColors: true, | |
147 this.help: false, | |
148 this.version: false, | |
149 this.logLevel: Level.WARNING, | |
150 this.runtimeDir, | |
151 this.inputs, | |
152 this.inputBaseDir}); | |
153 } | |
154 | |
155 /// Parses options from the command-line | |
156 CompilerOptions parseOptions(List<String> argv, {bool forceOutDir: false}) { | |
157 ArgResults args = argParser.parse(argv); | |
158 bool showUsage = args['help']; | |
159 bool showVersion = args['version']; | |
160 | |
161 var logLevel = Level.WARNING; | |
162 var levelName = args['log']; | |
163 if (levelName != null) { | |
164 levelName = levelName.toUpperCase(); | |
165 logLevel = Level.LEVELS | |
166 .firstWhere((l) => l.name == levelName, orElse: () => logLevel); | |
167 } | |
168 var useColors = stdioType(stdout) == StdioType.TERMINAL; | |
169 var sdkPath = args['dart-sdk']; | |
170 if (sdkPath == null && !args['mock-sdk']) { | |
171 sdkPath = getSdkDir(argv).path; | |
172 } | |
173 var runtimeDir = args['runtime-dir']; | |
174 if (runtimeDir == null) { | |
175 runtimeDir = _computeRuntimeDir(); | |
176 } | |
177 var outputDir = args['out']; | |
178 if (outputDir == null && forceOutDir) { | |
179 outputDir = Directory.systemTemp.createTempSync("dev_compiler_out_").path; | |
180 } | |
181 | |
182 var v8Binary = args['v8-binary']; | |
183 if (v8Binary == null) v8Binary = _V8_BINARY_DEFAULT; | |
184 | |
185 var customUrlMappings = <String, String>{}; | |
186 for (var mapping in args['url-mapping']) { | |
187 var splitMapping = mapping.split(','); | |
188 if (splitMapping.length != 2) { | |
189 showUsage = true; | |
190 continue; | |
191 } | |
192 customUrlMappings[splitMapping[0]] = splitMapping[1]; | |
193 } | |
194 | |
195 return new CompilerOptions( | |
196 codegenOptions: new CodegenOptions( | |
197 emitSourceMaps: args['source-maps'], | |
198 forceCompile: args['force-compile'], | |
199 closure: args['closure'], | |
200 destructureNamedParams: args['destructure-named-params'], | |
201 outputDir: outputDir, | |
202 moduleFormat: parseModuleFormat(args['modules'])), | |
203 sourceOptions: new SourceResolverOptions( | |
204 useMockSdk: args['mock-sdk'], | |
205 dartSdkPath: sdkPath, | |
206 customUrlMappings: customUrlMappings, | |
207 useMultiPackage: args['use-multi-package'], | |
208 packageRoot: args['package-root'], | |
209 packagePaths: args['package-paths'].split(',')), | |
210 runnerOptions: new RunnerOptions(v8Binary: v8Binary), | |
211 checkSdk: args['sdk-check'], | |
212 useColors: useColors, | |
213 help: showUsage, | |
214 version: showVersion, | |
215 logLevel: logLevel, | |
216 runtimeDir: runtimeDir, | |
217 inputs: args.rest); | |
218 } | |
219 | |
220 final ArgParser argParser = new ArgParser() | |
221 ..addFlag('sdk-check', | |
222 abbr: 's', help: 'Typecheck sdk libs', defaultsTo: false) | |
223 ..addFlag('mock-sdk', | |
224 abbr: 'm', help: 'Use a mock Dart SDK', defaultsTo: false) | |
225 | |
226 // input/output options | |
227 ..addOption('out', abbr: 'o', help: 'Output directory', defaultsTo: null) | |
228 ..addOption('dart-sdk', help: 'Dart SDK Path', defaultsTo: null) | |
229 ..addOption('package-root', | |
230 abbr: 'p', | |
231 help: 'Package root to resolve "package:" imports', | |
232 defaultsTo: 'packages/') | |
233 ..addOption('url-mapping', | |
234 help: '--url-mapping=libraryUri,/path/to/library.dart uses library.dart\n' | |
235 'as the source for an import of of "libraryUri".', | |
236 allowMultiple: true, | |
237 splitCommas: false) | |
238 ..addFlag('use-multi-package', | |
239 help: 'Whether to use the multi-package resolver for "package:" imports', | |
240 defaultsTo: false) | |
241 ..addOption('package-paths', | |
242 help: 'if using the multi-package resolver, the list of directories to\n' | |
243 'look for packages in.', | |
244 defaultsTo: '') | |
245 ..addFlag('source-maps', | |
246 help: 'Whether to emit source map files', defaultsTo: true) | |
247 ..addOption('runtime-dir', | |
248 help: 'Where to find dev_compiler\'s runtime files', defaultsTo: null) | |
249 ..addOption('modules', | |
250 help: 'Which module pattern to emit', | |
251 allowed: ModuleFormat.values.map(getEnumName).toList(), | |
252 allowedHelp: { | |
253 getEnumName(ModuleFormat.es6): 'es6 modules', | |
254 getEnumName(ModuleFormat.legacy): | |
255 'a custom format used by dartdevc, similar to AMD', | |
256 getEnumName(ModuleFormat.node): | |
257 'node.js modules (https://nodejs.org/api/modules.html)' | |
258 }, | |
259 defaultsTo: getEnumName(ModuleFormat.legacy)) | |
260 | |
261 // general options | |
262 ..addFlag('help', abbr: 'h', help: 'Display this message') | |
263 ..addFlag('version', | |
264 negatable: false, help: 'Display the Dev Compiler verion') | |
265 ..addFlag('closure', | |
266 help: 'Emit Closure Compiler-friendly code (experimental)', | |
267 defaultsTo: _CLOSURE_DEFAULT) | |
268 ..addFlag('destructure-named-params', | |
269 help: 'Destructure named parameters (requires ES6-enabled runtime)', | |
270 defaultsTo: _DESTRUCTURE_NAMED_PARAMS_DEFAULT) | |
271 ..addFlag('force-compile', | |
272 abbr: 'f', help: 'Compile code with static errors', defaultsTo: false) | |
273 ..addOption('log', abbr: 'l', help: 'Logging level (defaults to warning)') | |
274 ..addOption('v8-binary', | |
275 help: 'V8-based binary to run JavaScript output with (iojs, node, d8)', | |
276 defaultsTo: _V8_BINARY_DEFAULT); | |
277 | |
278 // TODO: Switch over to the `pub_cache` package (or the Resource API)? | |
279 | |
280 const _ENTRY_POINTS = const [ | |
281 'dartdevc.dart', | |
282 'dev_compiler.dart', | |
283 'devrun.dart' | |
284 ]; | |
285 | |
286 final _ENTRY_POINT_SNAPSHOTS = _ENTRY_POINTS.map((f) => "$f.snapshot"); | |
287 | |
288 /// Tries to find the `lib/runtime/` directory of the dev_compiler package. This | |
289 /// works when running devc from it's sources or from a snapshot that is | |
290 /// activated via `pub global activate`. | |
291 String _computeRuntimeDir() { | |
292 var scriptPath = path.fromUri(Platform.script); | |
293 var file = path.basename(scriptPath); | |
294 var dir = path.dirname(scriptPath); | |
295 var lastdir = path.basename(dir); | |
296 dir = path.dirname(dir); | |
297 | |
298 // Both the source dartdevc.dart and the snapshot generated by pub global acti
vate | |
299 // are under a bin folder. | |
300 if (lastdir != 'bin') return null; | |
301 | |
302 // And both under a project directory containing a pubspec.lock file. | |
303 var lockfile = path.join(dir, 'pubspec.lock'); | |
304 if (!new File(lockfile).existsSync()) return null; | |
305 | |
306 // If running from sources we found it! | |
307 if (_ENTRY_POINTS.contains(file)) { | |
308 return path.join(dir, 'lib', 'runtime'); | |
309 } | |
310 | |
311 // If running from a pub global snapshot, we need to read the lock file to | |
312 // find where the actual sources are located in the pub cache. | |
313 if (_ENTRY_POINT_SNAPSHOTS.contains(file)) { | |
314 // Note: this depends on implementation details of pub. | |
315 var yaml = loadYaml(new File(lockfile).readAsStringSync()); | |
316 var info = yaml['packages']['dev_compiler']; | |
317 if (info == null) return null; | |
318 | |
319 var cacheDir; | |
320 if (info['source'] == 'hosted') { | |
321 cacheDir = path.join( | |
322 'hosted', 'pub.dartlang.org', 'dev_compiler-${info["version"]}'); | |
323 } else if (info['source'] == 'git') { | |
324 var ref = info['description']['resolved-ref']; | |
325 cacheDir = path.join('git', 'dev_compiler-${ref}'); | |
326 } | |
327 | |
328 // We should be under "/path/to/pub-cache/global_packages/dev_compiler". | |
329 // The pub-cache directory is two levels up, but we verify that the layout | |
330 // looks correct. | |
331 if (path.basename(dir) != 'dev_compiler') return null; | |
332 dir = path.dirname(dir); | |
333 if (path.basename(dir) != 'global_packages') return null; | |
334 dir = path.dirname(dir); | |
335 return path.join(dir, cacheDir, 'lib', 'runtime'); | |
336 } | |
337 return null; | |
338 } | |
OLD | NEW |