Index: pkg/polymer/lib/dwc.dart |
diff --git a/pkg/polymer/lib/dwc.dart b/pkg/polymer/lib/dwc.dart |
new file mode 100755 |
index 0000000000000000000000000000000000000000..b70f29bd5c75385a7f0d7347a4cab914578e3b67 |
--- /dev/null |
+++ b/pkg/polymer/lib/dwc.dart |
@@ -0,0 +1,198 @@ |
+// Copyright (c) 2012, 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. |
+ |
+/** The entry point to the compiler. Used to implement `bin/dwc.dart`. */ |
+library dwc; |
+ |
+import 'dart:async'; |
+import 'dart:io'; |
+import 'package:logging/logging.dart' show Level; |
+ |
+import 'src/compiler.dart'; |
+import 'src/file_system.dart'; |
+import 'src/file_system/console.dart'; |
+import 'src/files.dart'; |
+import 'src/messages.dart'; |
+import 'src/compiler_options.dart'; |
+import 'src/utils.dart'; |
+ |
+FileSystem _fileSystem; |
+ |
+void main() { |
+ run(new Options().arguments).then((result) { |
+ exit(result.success ? 0 : 1); |
+ }); |
+} |
+ |
+/** Contains the result of a compiler run. */ |
+class CompilerResult { |
+ final bool success; |
+ |
+ /** Map of output path to source, if there is one */ |
+ final Map<String, String> outputs; |
+ |
+ /** List of files read during compilation */ |
+ final List<String> inputs; |
+ |
+ final List<String> messages; |
+ String bootstrapFile; |
+ |
+ CompilerResult([this.success = true, |
+ this.outputs, |
+ this.inputs, |
+ this.messages = const [], |
+ this.bootstrapFile]); |
+ |
+ factory CompilerResult._(bool success, |
+ List<String> messages, List<OutputFile> outputs, List<SourceFile> files) { |
+ var file; |
+ var outs = new Map<String, String>(); |
+ for (var out in outputs) { |
+ if (path.basename(out.path).endsWith('_bootstrap.dart')) { |
+ file = out.path; |
+ } |
+ outs[out.path] = out.source; |
+ } |
+ var inputs = files.map((f) => f.path).toList(); |
+ return new CompilerResult(success, outs, inputs, messages, file); |
+ } |
+} |
+ |
+/** |
+ * Runs the web components compiler with the command-line options in [args]. |
+ * See [CompilerOptions] for the definition of valid arguments. |
+ */ |
+// TODO(jmesserly): fix this to return a proper exit code |
+// TODO(justinfagnani): return messages in the result |
+Future<CompilerResult> run(List<String> args, {bool printTime, |
+ bool shouldPrint: true}) { |
+ var options = CompilerOptions.parse(args); |
+ if (options == null) return new Future.value(new CompilerResult()); |
+ if (printTime == null) printTime = options.verbose; |
+ |
+ _fileSystem = new ConsoleFileSystem(); |
+ var messages = new Messages(options: options, shouldPrint: shouldPrint); |
+ |
+ return asyncTime('Total time spent on ${options.inputFile}', () { |
+ var compiler = new Compiler(_fileSystem, options, messages); |
+ var res; |
+ return compiler.run() |
+ .then((_) { |
+ var success = messages.messages.every((m) => m.level != Level.SEVERE); |
+ var msgs = options.jsonFormat |
+ ? messages.messages.map((m) => m.toJson()) |
+ : messages.messages.map((m) => m.toString()); |
+ res = new CompilerResult._(success, msgs.toList(), |
+ compiler.output, compiler.files); |
+ }) |
+ .then((_) => _symlinkPubPackages(res, options, messages)) |
+ .then((_) => _emitFiles(compiler.output, options.clean)) |
+ .then((_) => res); |
+ }, printTime: printTime, useColors: options.useColors); |
+} |
+ |
+Future _emitFiles(List<OutputFile> outputs, bool clean) { |
+ outputs.forEach((f) => _writeFile(f.path, f.contents, clean)); |
+ return _fileSystem.flush(); |
+} |
+ |
+void _writeFile(String filePath, String contents, bool clean) { |
+ if (clean) { |
+ File fileOut = new File(filePath); |
+ if (fileOut.existsSync()) { |
+ fileOut.deleteSync(); |
+ } |
+ } else { |
+ _createIfNeeded(path.dirname(filePath)); |
+ _fileSystem.writeString(filePath, contents); |
+ } |
+} |
+ |
+void _createIfNeeded(String outdir) { |
+ if (outdir.isEmpty) return; |
+ var outDirectory = new Directory(outdir); |
+ if (!outDirectory.existsSync()) { |
+ _createIfNeeded(path.dirname(outdir)); |
+ outDirectory.createSync(); |
+ } |
+} |
+ |
+/** |
+ * Creates a symlink to the pub packages directory in the output location. The |
+ * returned future completes when the symlink was created (or immediately if it |
+ * already exists). |
+ */ |
+Future _symlinkPubPackages(CompilerResult result, CompilerOptions options, |
+ Messages messages) { |
+ if (options.outputDir == null || result.bootstrapFile == null |
+ || options.packageRoot != null) { |
+ // We don't need to copy the packages directory if the output was generated |
+ // in-place where the input lives, if the compiler was called without an |
+ // entry-point file, or if the compiler was called with a package-root |
+ // option. |
+ return new Future.value(null); |
+ } |
+ |
+ var linkDir = path.dirname(result.bootstrapFile); |
+ _createIfNeeded(linkDir); |
+ var linkPath = path.join(linkDir, 'packages'); |
+ // A resolved symlink works like a directory |
+ // TODO(sigmund): replace this with something smarter once we have good |
+ // symlink support in dart:io |
+ if (new Directory(linkPath).existsSync()) { |
+ // Packages directory already exists. |
+ return new Future.value(null); |
+ } |
+ |
+ // A broken symlink works like a file |
+ var toFile = new File(linkPath); |
+ if (toFile.existsSync()) { |
+ toFile.deleteSync(); |
+ } |
+ |
+ var targetPath = path.join(path.dirname(options.inputFile), 'packages'); |
+ // [fullPathSync] will canonicalize the path, resolving any symlinks. |
+ // TODO(sigmund): once it's possible in dart:io, we just want to use a full |
+ // path, but not necessarily resolve symlinks. |
+ var target = new File(targetPath).fullPathSync().toString(); |
+ return createSymlink(target, linkPath, messages: messages); |
+} |
+ |
+ |
+// TODO(jmesserly): this code was taken from Pub's io library. |
+// Added error handling and don't return the file result, to match the code |
+// we had previously. Also "target" and "link" only accept strings. And inlined |
+// the relevant parts of runProcess. Note that it uses "cmd" to get the path |
+// on Windows. |
+/** |
+ * Creates a new symlink that creates an alias of [target] at [link], both of |
+ * which can be a [String], [File], or [Directory]. Returns a [Future] which |
+ * completes to the symlink file (i.e. [link]). |
+ */ |
+Future createSymlink(String target, String link, {Messages messages: null}) { |
+ messages = messages == null? new Messages.silent() : messages; |
+ var command = 'ln'; |
+ var args = ['-s', target, link]; |
+ |
+ if (Platform.operatingSystem == 'windows') { |
+ // Call mklink on Windows to create an NTFS junction point. Only works on |
+ // Vista or later. (Junction points are available earlier, but the "mklink" |
+ // command is not.) I'm using a junction point (/j) here instead of a soft |
+ // link (/d) because the latter requires some privilege shenanigans that |
+ // I'm not sure how to specify from the command line. |
+ command = 'cmd'; |
+ args = ['/c', 'mklink', '/j', link, target]; |
+ } |
+ |
+ return Process.run(command, args).then((result) { |
+ if (result.exitCode != 0) { |
+ var details = 'subprocess stdout:\n${result.stdout}\n' |
+ 'subprocess stderr:\n${result.stderr}'; |
+ messages.error( |
+ 'unable to create symlink\n target: $target\n link:$link\n$details', |
+ null); |
+ } |
+ return null; |
+ }); |
+} |