| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 /** The entry point to the compiler. Used to implement `bin/dwc.dart`. */ | 5 /** The entry point to the compiler. Used to implement `bin/dwc.dart`. */ |
| 6 library dwc; | 6 library dwc; |
| 7 | 7 |
| 8 import 'dart:async'; | 8 import 'dart:async'; |
| 9 import 'dart:io'; | 9 import 'dart:io'; |
| 10 import 'package:logging/logging.dart' show Level; | 10 import 'package:logging/logging.dart' show Level; |
| 11 | 11 |
| 12 import 'src/compiler.dart'; | 12 import 'src/compiler.dart'; |
| 13 import 'src/file_system.dart'; | 13 import 'src/file_system.dart'; |
| 14 import 'src/file_system/console.dart'; | 14 import 'src/file_system/console.dart'; |
| 15 import 'src/files.dart'; | 15 import 'src/files.dart'; |
| 16 import 'src/messages.dart'; | 16 import 'src/messages.dart'; |
| 17 import 'src/compiler_options.dart'; | 17 import 'src/compiler_options.dart'; |
| 18 import 'src/utils.dart'; | 18 import 'src/utils.dart'; |
| 19 | 19 |
| 20 FileSystem _fileSystem; | |
| 21 | |
| 22 void main() { | 20 void main() { |
| 23 run(new Options().arguments).then((result) { | 21 run(new Options().arguments).then((result) { |
| 24 exit(result.success ? 0 : 1); | 22 exit(result.success ? 0 : 1); |
| 25 }); | 23 }); |
| 26 } | 24 } |
| 27 | 25 |
| 28 /** Contains the result of a compiler run. */ | 26 /** Contains the result of a compiler run. */ |
| 29 class CompilerResult { | 27 class AnalysisResults { |
| 28 |
| 29 /** False when errors were found by our polymer analyzer. */ |
| 30 final bool success; | 30 final bool success; |
| 31 | 31 |
| 32 /** Map of output path to source, if there is one */ | 32 /** Error and warning messages collected by the analyzer. */ |
| 33 final Map<String, String> outputs; | 33 final List<String> messages; |
| 34 | 34 |
| 35 /** List of files read during compilation */ | 35 AnalysisResults(this.success, this.messages); |
| 36 final List<String> inputs; | |
| 37 | |
| 38 final List<String> messages; | |
| 39 String bootstrapFile; | |
| 40 | |
| 41 CompilerResult([this.success = true, | |
| 42 this.outputs, | |
| 43 this.inputs, | |
| 44 this.messages = const [], | |
| 45 this.bootstrapFile]); | |
| 46 | |
| 47 factory CompilerResult._(bool success, | |
| 48 List<String> messages, List<OutputFile> outputs, List<SourceFile> files) { | |
| 49 var file; | |
| 50 var outs = new Map<String, String>(); | |
| 51 for (var out in outputs) { | |
| 52 if (path.basename(out.path).endsWith('_bootstrap.dart')) { | |
| 53 file = out.path; | |
| 54 } | |
| 55 outs[out.path] = out.source; | |
| 56 } | |
| 57 var inputs = files.map((f) => f.path).toList(); | |
| 58 return new CompilerResult(success, outs, inputs, messages, file); | |
| 59 } | |
| 60 } | 36 } |
| 61 | 37 |
| 62 /** | 38 /** |
| 63 * Runs the web components compiler with the command-line options in [args]. | 39 * Runs the polymer analyzer with the command-line options in [args]. |
| 64 * See [CompilerOptions] for the definition of valid arguments. | 40 * See [CompilerOptions] for the definition of valid arguments. |
| 65 */ | 41 */ |
| 66 // TODO(jmesserly): fix this to return a proper exit code | 42 // TODO(sigmund): rename to analyze? and rename file as analyzer.dart |
| 67 // TODO(justinfagnani): return messages in the result | 43 Future<AnalysisResults> run(List<String> args, {bool printTime, |
| 68 Future<CompilerResult> run(List<String> args, {bool printTime, | |
| 69 bool shouldPrint: true}) { | 44 bool shouldPrint: true}) { |
| 70 var options = CompilerOptions.parse(args); | 45 var options = CompilerOptions.parse(args); |
| 71 if (options == null) return new Future.value(new CompilerResult()); | 46 if (options == null) return new Future.value(new AnalysisResults(true, [])); |
| 72 if (printTime == null) printTime = options.verbose; | 47 if (printTime == null) printTime = options.verbose; |
| 73 | 48 |
| 74 _fileSystem = new ConsoleFileSystem(); | |
| 75 var messages = new Messages(options: options, shouldPrint: shouldPrint); | |
| 76 | |
| 77 return asyncTime('Total time spent on ${options.inputFile}', () { | 49 return asyncTime('Total time spent on ${options.inputFile}', () { |
| 78 var compiler = new Compiler(_fileSystem, options, messages); | 50 var messages = new Messages(options: options, shouldPrint: shouldPrint); |
| 79 var res; | 51 var compiler = new Compiler(new ConsoleFileSystem(), options, messages); |
| 80 return compiler.run() | 52 return compiler.run().then((_) { |
| 81 .then((_) { | 53 var success = messages.messages.every((m) => m.level != Level.SEVERE); |
| 82 var success = messages.messages.every((m) => m.level != Level.SEVERE); | 54 var msgs = options.jsonFormat |
| 83 var msgs = options.jsonFormat | 55 ? messages.messages.map((m) => m.toJson()) |
| 84 ? messages.messages.map((m) => m.toJson()) | 56 : messages.messages.map((m) => m.toString()); |
| 85 : messages.messages.map((m) => m.toString()); | 57 return new AnalysisResults(success, msgs.toList()); |
| 86 res = new CompilerResult._(success, msgs.toList(), | 58 }); |
| 87 compiler.output, compiler.files); | |
| 88 }) | |
| 89 .then((_) => _symlinkPubPackages(res, options, messages)) | |
| 90 .then((_) => _emitFiles(compiler.output, options.clean)) | |
| 91 .then((_) => res); | |
| 92 }, printTime: printTime, useColors: options.useColors); | 59 }, printTime: printTime, useColors: options.useColors); |
| 93 } | 60 } |
| 94 | |
| 95 Future _emitFiles(List<OutputFile> outputs, bool clean) { | |
| 96 outputs.forEach((f) => _writeFile(f.path, f.contents, clean)); | |
| 97 return _fileSystem.flush(); | |
| 98 } | |
| 99 | |
| 100 void _writeFile(String filePath, String contents, bool clean) { | |
| 101 if (clean) { | |
| 102 File fileOut = new File(filePath); | |
| 103 if (fileOut.existsSync()) { | |
| 104 fileOut.deleteSync(); | |
| 105 } | |
| 106 } else { | |
| 107 _createIfNeeded(path.dirname(filePath)); | |
| 108 _fileSystem.writeString(filePath, contents); | |
| 109 } | |
| 110 } | |
| 111 | |
| 112 void _createIfNeeded(String outdir) { | |
| 113 if (outdir.isEmpty) return; | |
| 114 var outDirectory = new Directory(outdir); | |
| 115 if (!outDirectory.existsSync()) { | |
| 116 _createIfNeeded(path.dirname(outdir)); | |
| 117 outDirectory.createSync(); | |
| 118 } | |
| 119 } | |
| 120 | |
| 121 /** | |
| 122 * Creates a symlink to the pub packages directory in the output location. The | |
| 123 * returned future completes when the symlink was created (or immediately if it | |
| 124 * already exists). | |
| 125 */ | |
| 126 Future _symlinkPubPackages(CompilerResult result, CompilerOptions options, | |
| 127 Messages messages) { | |
| 128 if (options.outputDir == null || result.bootstrapFile == null | |
| 129 || options.packageRoot != null) { | |
| 130 // We don't need to copy the packages directory if the output was generated | |
| 131 // in-place where the input lives, if the compiler was called without an | |
| 132 // entry-point file, or if the compiler was called with a package-root | |
| 133 // option. | |
| 134 return new Future.value(null); | |
| 135 } | |
| 136 | |
| 137 var linkDir = path.dirname(result.bootstrapFile); | |
| 138 _createIfNeeded(linkDir); | |
| 139 var linkPath = path.join(linkDir, 'packages'); | |
| 140 // A resolved symlink works like a directory | |
| 141 // TODO(sigmund): replace this with something smarter once we have good | |
| 142 // symlink support in dart:io | |
| 143 if (new Directory(linkPath).existsSync()) { | |
| 144 // Packages directory already exists. | |
| 145 return new Future.value(null); | |
| 146 } | |
| 147 | |
| 148 // A broken symlink works like a file | |
| 149 var toFile = new File(linkPath); | |
| 150 if (toFile.existsSync()) { | |
| 151 toFile.deleteSync(); | |
| 152 } | |
| 153 | |
| 154 var targetPath = path.join(path.dirname(options.inputFile), 'packages'); | |
| 155 // [fullPathSync] will canonicalize the path, resolving any symlinks. | |
| 156 // TODO(sigmund): once it's possible in dart:io, we just want to use a full | |
| 157 // path, but not necessarily resolve symlinks. | |
| 158 var target = new File(targetPath).fullPathSync().toString(); | |
| 159 return createSymlink(target, linkPath, messages: messages); | |
| 160 } | |
| 161 | |
| 162 | |
| 163 // TODO(jmesserly): this code was taken from Pub's io library. | |
| 164 // Added error handling and don't return the file result, to match the code | |
| 165 // we had previously. Also "target" and "link" only accept strings. And inlined | |
| 166 // the relevant parts of runProcess. Note that it uses "cmd" to get the path | |
| 167 // on Windows. | |
| 168 /** | |
| 169 * Creates a new symlink that creates an alias of [target] at [link], both of | |
| 170 * which can be a [String], [File], or [Directory]. Returns a [Future] which | |
| 171 * completes to the symlink file (i.e. [link]). | |
| 172 */ | |
| 173 Future createSymlink(String target, String link, {Messages messages: null}) { | |
| 174 messages = messages == null? new Messages.silent() : messages; | |
| 175 var command = 'ln'; | |
| 176 var args = ['-s', target, link]; | |
| 177 | |
| 178 if (Platform.operatingSystem == 'windows') { | |
| 179 // Call mklink on Windows to create an NTFS junction point. Only works on | |
| 180 // Vista or later. (Junction points are available earlier, but the "mklink" | |
| 181 // command is not.) I'm using a junction point (/j) here instead of a soft | |
| 182 // link (/d) because the latter requires some privilege shenanigans that | |
| 183 // I'm not sure how to specify from the command line. | |
| 184 command = 'cmd'; | |
| 185 args = ['/c', 'mklink', '/j', link, target]; | |
| 186 } | |
| 187 | |
| 188 return Process.run(command, args).then((result) { | |
| 189 if (result.exitCode != 0) { | |
| 190 var details = 'subprocess stdout:\n${result.stdout}\n' | |
| 191 'subprocess stderr:\n${result.stderr}'; | |
| 192 messages.error( | |
| 193 'unable to create symlink\n target: $target\n link:$link\n$details', | |
| 194 null); | |
| 195 } | |
| 196 return null; | |
| 197 }); | |
| 198 } | |
| OLD | NEW |