Index: bin/commands/global_compile.dart |
diff --git a/bin/commands/global_compile.dart b/bin/commands/global_compile.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0f452a01bad6640042b91eb205906d3929609472 |
--- /dev/null |
+++ b/bin/commands/global_compile.dart |
@@ -0,0 +1,198 @@ |
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+import 'dart:io'; |
+import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; |
+import 'package:analyzer/src/generated/source.dart' show Source; |
+import 'package:analyzer/src/summary/package_bundle_reader.dart' |
+ show InSummarySource; |
+import 'package:args/command_runner.dart'; |
+import 'package:dev_compiler/src/analyzer/context.dart' show |
+ createAnalysisContextWithSources; |
+import 'package:dev_compiler/src/compiler/command.dart'; |
+import 'package:dev_compiler/src/compiler/compiler.dart' |
+ show BuildUnit, CompilerOptions, JSModuleFile, ModuleCompiler, ModuleFormat; |
+import 'package:dev_compiler/src/analyzer/context.dart' show AnalyzerOptions; |
+import 'package:path/path.dart' as path; |
+import 'package:yaml/yaml.dart' show loadYaml; |
+ |
+ |
+/// The command for invoking the modular compiler on the whole program. |
+class GlobalCommand extends Command { |
Jennifer Messerly
2016/04/18 21:45:45
WholeProgramCompileCommand? or GlobalCompileComman
|
+ get name => 'global-compile'; |
Jennifer Messerly
2016/04/18 21:45:45
in the original design this was called "build". Th
|
+ get description => 'Compile all files reachable from an entry point.'; |
+ |
+ GlobalCommand() { |
+ argParser.addOption('out', abbr: 'o', help: 'Output file (required)'); |
+ CompilerOptions.addArguments(argParser); |
+ AnalyzerOptions.addArguments(argParser); |
+ } |
+ |
+ String canonicalize(String uri, String root) { |
Jennifer Messerly
2016/04/18 21:45:45
is this is the exact code we have in ModuleCompile
|
+ var sourceUri = Uri.parse(uri); |
+ if (sourceUri.scheme == '') { |
+ sourceUri = path.toUri(path.isAbsolute(uri) ? path.absolute(uri) : path.join(root, uri)); |
Jennifer Messerly
2016/04/18 21:45:45
looks like this needs dartfmt
|
+ } |
+ return sourceUri.toString(); |
+ } |
+ |
+ void transitiveFiles(Set<String> results, AnalysisContext context, String entryPoint, String root) { |
+ entryPoint = canonicalize(entryPoint, root); |
+ if (entryPoint.startsWith('dart:')) return; |
+ var entryDir = path.dirname(entryPoint); |
+ if (results.add(entryPoint)) { |
+ // Process this |
+ var source = context.sourceFactory.forUri(entryPoint); |
+ if (source == null) { |
+ throw new Exception('could not create a source for $entryPoint.' |
+ ' The file name is in the wrong format or was not found.'); |
+ } |
+ var library = context.computeLibraryElement(source); |
Jennifer Messerly
2016/04/18 21:45:45
I'm curious, why use resolution?
You can figure ou
|
+ for (var entry in library.imports) { |
+ if (entry.uri == null) continue; |
+ transitiveFiles(results, context, entry.uri, entryDir); |
+ } |
+ for (var entry in library.exports) { |
+ transitiveFiles(results, context, entry.uri, entryDir); |
+ } |
+ for (var part in library.parts) { |
+ results.add(canonicalize(part.uri, entryDir)); |
+ } |
+ } |
+ } |
+ |
+ @override |
+ void run() { |
+ var analyzerOptions = new AnalyzerOptions.fromArguments(argResults); |
+ |
+ var context = createAnalysisContextWithSources(analyzerOptions); |
+ var inputSet = new Set<String>(); |
+ for (var entry in argResults.rest) { |
+ transitiveFiles(inputSet, context, entry, Directory.current.path); |
+ } |
+ compile( |
+ new ModuleCompiler.withContext(context), |
Jennifer Messerly
2016/04/18 21:45:45
IMO, it would be better to have it use the command
Jennifer Messerly
2016/04/18 21:49:30
addendum -- a lot depends on where we are going wi
|
+ new CompilerOptions.fromArguments(argResults), |
+ argResults['out'], |
+ inputSet.toList()); |
+ } |
+ |
+ void compile(ModuleCompiler compiler, CompilerOptions compilerOptions, |
+ String outPath, List<String> extraArgs, |
+ {void forEachError(String error): print}) { |
+ if (outPath == null) { |
+ usageException('Please include the output file location. For example:\n' |
+ ' -o PATH/TO/OUTPUT_FILE.js'); |
+ } |
+ if (compilerOptions.sourceMap) { |
+ usageException('Source maps are not supported in global mode'); |
+ } |
+ if (compilerOptions.summarizeApi) { |
+ usageException('Summaries are not supported in global mode'); |
+ } |
+ if (compilerOptions.moduleFormat != ModuleFormat.legacy) { |
+ usageException('Only legacy modules are supported in global mode'); |
+ } |
+ var unit = new BuildUnit( |
+ path.basenameWithoutExtension(outPath), extraArgs, _moduleForLibrary); |
+ |
+ JSModuleFile module = compiler.compile(unit, compilerOptions); |
+ module.errors.forEach(forEachError); |
+ |
+ if (!module.isValid) throw new CompileErrorException(); |
+ |
+ // Write JS file, as well as source map and summary (if requested). |
+ var file = new File(outPath); |
+ if (file.existsSync()) file.delete(); |
+ _copyLegacyDartRuntime(file); |
+ file.writeAsStringSync(module.code, mode: FileMode.APPEND); |
Jennifer Messerly
2016/04/18 21:45:45
I don't think you want .APPEND, won't this break a
|
+ } |
+ |
+ String _moduleForLibrary(Source source) { |
+ if (source is InSummarySource) { |
+ return path.basenameWithoutExtension(source.summaryPath); |
+ } |
+ |
+ throw usageException( |
+ 'Imported file "${source.uri}" was not found as a summary or source ' |
+ 'file. Please pass in either the summary or the source file ' |
+ 'for this import.'); |
+ } |
+ |
+ void _copyLegacyDartRuntime(File outfile) { |
+ var runtimeDir = _computeRuntimeDir(); |
+ if (runtimeDir == null) { |
+ usageException('Cannot compute runtime directory for global mode'); |
+ } |
+ for (var runtimeFile in _RUNTIME_FILES) { |
+ var inPath = path.join(runtimeDir, runtimeFile); |
+ var infile = new File(inPath); |
+ var contents = infile.readAsStringSync(); |
+ outfile.writeAsStringSync(contents, mode: FileMode.APPEND); |
+ } |
+ } |
+} |
+ |
+const _ENTRY_POINTS = const [ |
+ 'dartdevc.dart', |
+]; |
+ |
+const _RUNTIME_FILES = const [ |
+ 'dart_library.js', |
+ 'dart_sdk.js' |
+]; |
+ |
+final _ENTRY_POINT_SNAPSHOTS = _ENTRY_POINTS.map((f) => "$f.snapshot"); |
+ |
+/// Tries to find the `lib/runtime/` directory of the dev_compiler package. This |
+/// works when running devc from it's sources or from a snapshot that is |
+/// activated via `pub global activate`. |
+String _computeRuntimeDir() { |
Jennifer Messerly
2016/04/18 21:45:45
Vader "NOOOOOOOooooooooooo" meme ... ;)
We just g
|
+ var scriptPath = path.fromUri(Platform.script); |
+ var file = path.basename(scriptPath); |
+ var dir = path.dirname(scriptPath); |
+ var lastdir = path.basename(dir); |
+ dir = path.dirname(dir); |
+ |
+ // Both the source dartdevc.dart and the snapshot generated by pub global activate |
+ // are under a bin folder. |
+ if (lastdir != 'bin') return null; |
+ |
+ // And both under a project directory containing a pubspec.lock file. |
+ var lockfile = path.join(dir, 'pubspec.lock'); |
+ if (!new File(lockfile).existsSync()) return null; |
+ |
+ // If running from sources we found it! |
+ if (_ENTRY_POINTS.contains(file)) { |
+ return path.join(dir, 'lib', 'runtime'); |
+ } |
+ |
+ // If running from a pub global snapshot, we need to read the lock file to |
+ // find where the actual sources are located in the pub cache. |
+ if (_ENTRY_POINT_SNAPSHOTS.contains(file)) { |
+ // Note: this depends on implementation details of pub. |
+ var yaml = loadYaml(new File(lockfile).readAsStringSync()); |
+ var info = yaml['packages']['dev_compiler']; |
+ if (info == null) return null; |
+ |
+ var cacheDir; |
+ if (info['source'] == 'hosted') { |
+ cacheDir = path.join( |
+ 'hosted', 'pub.dartlang.org', 'dev_compiler-${info["version"]}'); |
+ } else if (info['source'] == 'git') { |
+ var ref = info['description']['resolved-ref']; |
+ cacheDir = path.join('git', 'dev_compiler-${ref}'); |
+ } |
+ |
+ // We should be under "/path/to/pub-cache/global_packages/dev_compiler". |
+ // The pub-cache directory is two levels up, but we verify that the layout |
+ // looks correct. |
+ if (path.basename(dir) != 'dev_compiler') return null; |
+ dir = path.dirname(dir); |
+ if (path.basename(dir) != 'global_packages') return null; |
+ dir = path.dirname(dir); |
+ return path.join(dir, cacheDir, 'lib', 'runtime'); |
+ } |
+ return null; |
+} |