OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, 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 library analyzer_cli.src.bootloader; |
| 6 |
| 7 import 'dart:async'; |
| 8 import 'dart:isolate'; |
| 9 |
| 10 import 'package:analyzer/file_system/physical_file_system.dart'; |
| 11 import 'package:analyzer/source/analysis_options_provider.dart'; |
| 12 import 'package:analyzer/src/context/context.dart'; |
| 13 import 'package:analyzer/src/generated/engine.dart' as engine; |
| 14 import 'package:analyzer/src/plugin/plugin_configuration.dart'; |
| 15 import 'package:analyzer_cli/src/driver.dart'; |
| 16 import 'package:analyzer_cli/src/options.dart'; |
| 17 import 'package:source_span/source_span.dart'; |
| 18 import 'package:yaml/src/yaml_node.dart'; |
| 19 |
| 20 const _analyzerPackageName = 'analyzer'; |
| 21 |
| 22 /// Return non-null if there is a validation issue with this plugin. |
| 23 String validate(PluginInfo plugin) { |
| 24 var missing = <String>[]; |
| 25 if (plugin.className == null) { |
| 26 missing.add('class name'); |
| 27 } |
| 28 if (plugin.libraryUri == null) { |
| 29 missing.add('library uri'); |
| 30 } |
| 31 if (missing.isEmpty) { |
| 32 // All good. |
| 33 return null; |
| 34 } |
| 35 return 'Plugin ${plugin.name} skipped, config missing: ${missing.join(", ")}'; |
| 36 } |
| 37 |
| 38 List<PluginInfo> _validate(Iterable<PluginInfo> plugins) { |
| 39 List<PluginInfo> validated = <PluginInfo>[]; |
| 40 plugins.forEach((PluginInfo plugin) { |
| 41 String validation = validate(plugin); |
| 42 if (validation != null) { |
| 43 errorSink.writeln(validation); |
| 44 } else { |
| 45 validated.add(plugin); |
| 46 } |
| 47 }); |
| 48 return validated; |
| 49 } |
| 50 |
| 51 /// Source code assembler. |
| 52 class Assembler { |
| 53 /// Plugins to configure. |
| 54 final Iterable<PluginInfo> plugins; |
| 55 |
| 56 /// Create an assembler for the given plugin [config]. |
| 57 Assembler(this.plugins); |
| 58 |
| 59 /// A string enumerating required package `import`s. |
| 60 String get enumerateImports => |
| 61 plugins.map((PluginInfo p) => "import '${p.libraryUri}';").join('\n'); |
| 62 |
| 63 /// A string listing initialized plugin instances. |
| 64 String get pluginList => |
| 65 plugins.map((PluginInfo p) => 'new ${p.className}()').join(', '); |
| 66 |
| 67 /// Create a file containing a `main()` suitable for loading in spawned |
| 68 /// isolate. |
| 69 String createMain() => _generateMain(); |
| 70 |
| 71 String _generateMain() => """ |
| 72 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 73 // for details. All rights reserved. Use of this source code is governed by a |
| 74 // BSD-style license that can be found in the LICENSE file. |
| 75 |
| 76 // This code was auto-generated, is not intended to be edited, and is subject to |
| 77 // significant change. Please see the README file for more information. |
| 78 |
| 79 import 'package:analyzer_cli/src/driver.dart'; |
| 80 |
| 81 $enumerateImports |
| 82 |
| 83 void main(List<String> args) { |
| 84 var starter = new Driver(); |
| 85 starter.userDefinedPlugins = [$pluginList]; |
| 86 starter.start(args); |
| 87 } |
| 88 """; |
| 89 } |
| 90 |
| 91 /// Given environment information extracted from command-line `args`, creates a |
| 92 /// a loadable analyzer "image". |
| 93 class BootLoader { |
| 94 /// Emits an error message to [errorSink] if plugin config can't be read. |
| 95 static final ErrorHandler _pluginConfigErrorHandler = (Exception e) { |
| 96 String details; |
| 97 if (e is PluginConfigFormatException) { |
| 98 details = e.message; |
| 99 var node = e.yamlNode; |
| 100 if (node is YamlNode) { |
| 101 SourceLocation location = node.span.start; |
| 102 details += ' (line ${location.line}, column ${location.column})'; |
| 103 } |
| 104 } else { |
| 105 details = e.toString(); |
| 106 } |
| 107 |
| 108 errorSink.writeln('Plugin configuration skipped: $details'); |
| 109 }; |
| 110 |
| 111 /// Reads plugin config info from `.analysis_options`. |
| 112 PluginConfigOptionsProcessor _pluginOptionsProcessor = |
| 113 new PluginConfigOptionsProcessor(_pluginConfigErrorHandler); |
| 114 |
| 115 /// Create a loadable analyzer image configured with plugins derived from |
| 116 /// the given analyzer command-line `args`. |
| 117 Image createImage(List<String> args) { |
| 118 // Parse commandline options. |
| 119 CommandLineOptions options = CommandLineOptions.parse(args); |
| 120 |
| 121 // Process analysis options file (and notify all interested parties). |
| 122 _processAnalysisOptions(options); |
| 123 |
| 124 // TODO(pquitslund): Pass in .packages info |
| 125 return new Image(_pluginOptionsProcessor.config, |
| 126 args: args, packageRootPath: options.packageRootPath); |
| 127 } |
| 128 |
| 129 void _processAnalysisOptions(CommandLineOptions options) { |
| 130 // Determine options file path. |
| 131 var filePath = options.analysisOptionsFile ?? |
| 132 engine.AnalysisEngine.ANALYSIS_OPTIONS_FILE; |
| 133 try { |
| 134 var file = PhysicalResourceProvider.INSTANCE.getFile(filePath); |
| 135 AnalysisOptionsProvider analysisOptionsProvider = |
| 136 new AnalysisOptionsProvider(); |
| 137 Map<String, YamlNode> options = |
| 138 analysisOptionsProvider.getOptionsFromFile(file); |
| 139 //TODO(pq): thread in proper context. |
| 140 var temporaryContext = new AnalysisContextImpl(); |
| 141 _pluginOptionsProcessor.optionsProcessed(temporaryContext, options); |
| 142 } on Exception catch (e) { |
| 143 _pluginOptionsProcessor.onError(e); |
| 144 } |
| 145 } |
| 146 } |
| 147 |
| 148 /// A loadable "image" of a a configured analyzer instance. |
| 149 class Image { |
| 150 /// (Optional) package root path. |
| 151 final String packageRootPath; |
| 152 |
| 153 /// (Optional) package map. |
| 154 final Map<String, Uri> packages; |
| 155 |
| 156 /// (Optional) args to be passed on to the loaded main. |
| 157 final List<String> args; |
| 158 |
| 159 /// Plugin configuration. |
| 160 final PluginConfig config; |
| 161 |
| 162 /// Create an image with the given [config] and optionally [packages], |
| 163 /// [packageRootPath], and command line [args]. |
| 164 Image(this.config, {this.packages, this.packageRootPath, this.args}); |
| 165 |
| 166 /// Load this image. |
| 167 /// |
| 168 /// Loading an image consists in assembling an analyzer `main()`, configured |
| 169 /// to include the appropriate analyzer plugins as specified in |
| 170 /// `.analyzer_options` which is then run in a spawned isolate. |
| 171 Future load() { |
| 172 List<PluginInfo> plugins = _validate(config.plugins); |
| 173 String mainSource = new Assembler(plugins).createMain(); |
| 174 |
| 175 Completer completer = new Completer(); |
| 176 ReceivePort exitListener = new ReceivePort(); |
| 177 exitListener.listen((data) { |
| 178 completer.complete(); |
| 179 exitListener.close(); |
| 180 }); |
| 181 |
| 182 Uri uri = |
| 183 Uri.parse('data:application/dart;charset=utf-8,${Uri.encodeComponent( |
| 184 mainSource)}'); |
| 185 |
| 186 // TODO(pquitslund): update once .packages are supported. |
| 187 String packageRoot = |
| 188 packageRootPath != null ? packageRootPath : './packages'; |
| 189 Uri packageUri = new Uri.file(packageRoot); |
| 190 |
| 191 Isolate.spawnUri(uri, args, null /* msg */, |
| 192 packageRoot: packageUri, onExit: exitListener.sendPort); |
| 193 |
| 194 return completer.future; |
| 195 } |
| 196 } |
OLD | NEW |