Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(625)

Side by Side Diff: packages/analyzer/lib/src/context/builder.dart

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2016, 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.src.context.context_builder;
6
7 import 'dart:collection';
8 import 'dart:core';
9
10 import 'package:analyzer/context/declared_variables.dart';
11 import 'package:analyzer/file_system/file_system.dart';
12 import 'package:analyzer/plugin/options.dart';
13 import 'package:analyzer/plugin/resolver_provider.dart';
14 import 'package:analyzer/source/analysis_options_provider.dart';
15 import 'package:analyzer/source/package_map_resolver.dart';
16 import 'package:analyzer/src/dart/sdk/sdk.dart';
17 import 'package:analyzer/src/generated/engine.dart';
18 import 'package:analyzer/src/generated/sdk.dart';
19 import 'package:analyzer/src/generated/source.dart';
20 import 'package:analyzer/src/summary/package_bundle_reader.dart';
21 import 'package:analyzer/src/summary/pub_summary.dart';
22 import 'package:analyzer/src/task/options.dart';
23 import 'package:package_config/packages.dart';
24 import 'package:package_config/packages_file.dart';
25 import 'package:package_config/src/packages_impl.dart';
26 import 'package:path/src/context.dart';
27 import 'package:yaml/yaml.dart';
28
29 /**
30 * A utility class used to build an analysis context for a given directory.
31 *
32 * The construction of analysis contexts is as follows:
33 *
34 * 1. Determine how package: URI's are to be resolved. This follows the lookup
35 * algorithm defined by the [package specification][1].
36 *
37 * 2. Using the results of step 1, look in each package for an embedder file
38 * (_embedder.yaml). If one exists then it defines the SDK. If multiple such
39 * files exist then use the first one found. Otherwise, use the default SDK.
40 *
41 * 3. Look in each package for an SDK extension file (_sdkext). For each such
42 * file, add the specified files to the SDK.
43 *
44 * 4. Look for an analysis options file (`analyis_options.yaml` or
45 * `.analysis_options`) and process the options in the file.
46 *
47 * 5. Create a new context. Initialize its source factory based on steps 1, 2
48 * and 3. Initialize its analysis options from step 4.
49 *
50 * [1]: https://github.com/dart-lang/dart_enhancement_proposals/blob/master/Acce pted/0005%20-%20Package%20Specification/DEP-pkgspec.md.
51 */
52 class ContextBuilder {
53 /**
54 * The [ResourceProvider] by which paths are converted into [Resource]s.
55 */
56 final ResourceProvider resourceProvider;
57
58 /**
59 * The manager used to manage the DartSdk's that have been created so that
60 * they can be shared across contexts.
61 */
62 final DartSdkManager sdkManager;
63
64 /**
65 * The cache containing the contents of overlaid files.
66 */
67 final ContentCache contentCache;
68
69 /**
70 * The resolver provider used to create a package: URI resolver, or `null` if
71 * the normal (Package Specification DEP) lookup mechanism is to be used.
72 */
73 ResolverProvider packageResolverProvider;
74
75 /**
76 * The resolver provider used to create a file: URI resolver, or `null` if
77 * the normal file URI resolver is to be used.
78 */
79 ResolverProvider fileResolverProvider;
80
81 /**
82 * The file path of the .packages file that should be used in place of any
83 * file found using the normal (Package Specification DEP) lookup mechanism,
84 * or `null` if the normal lookup mechanism should be used.
85 */
86 String defaultPackageFilePath;
87
88 /**
89 * The file path of the packages directory that should be used in place of any
90 * file found using the normal (Package Specification DEP) lookup mechanism,
91 * or `null` if the normal lookup mechanism should be used.
92 */
93 String defaultPackagesDirectoryPath;
94
95 /**
96 * The file path of the analysis options file that should be used in place of
97 * any file in the root directory or a parent of the root directory, or `null`
98 * if the normal lookup mechanism should be used.
99 */
100 String defaultAnalysisOptionsFilePath;
101
102 /**
103 * The default analysis options that should be used unless some or all of them
104 * are overridden in the analysis options file, or `null` if the default
105 * defaults should be used.
106 */
107 AnalysisOptions defaultOptions;
108
109 /**
110 * A table mapping variable names to values for the declared variables, or
111 * `null` if no additional variables should be declared.
112 */
113 Map<String, String> declaredVariables;
114
115 /**
116 * The manager of pub package summaries.
117 */
118 PubSummaryManager pubSummaryManager;
119
120 /**
121 * Initialize a newly created builder to be ready to build a context rooted in
122 * the directory with the given [rootDirectoryPath].
123 */
124 ContextBuilder(this.resourceProvider, this.sdkManager, this.contentCache);
125
126 /**
127 * Return an analysis context that is configured correctly to analyze code in
128 * the directory with the given [path].
129 *
130 * *Note:* This method is not yet fully implemented and should not be used.
131 */
132 AnalysisContext buildContext(String path) {
133 InternalAnalysisContext context =
134 AnalysisEngine.instance.createAnalysisContext();
135 AnalysisOptions options = getAnalysisOptions(context, path);
136 context.contentCache = contentCache;
137 context.sourceFactory = createSourceFactory(path, options);
138 context.analysisOptions = options;
139 context.name = path;
140 //_processAnalysisOptions(context, optionMap);
141 declareVariables(context);
142 configureSummaries(context);
143 return context;
144 }
145
146 /**
147 * Configure the context to make use of summaries.
148 */
149 void configureSummaries(InternalAnalysisContext context) {
150 if (pubSummaryManager != null) {
151 List<LinkedPubPackage> linkedBundles =
152 pubSummaryManager.getLinkedBundles(context);
153 if (linkedBundles.isNotEmpty) {
154 SummaryDataStore store = new SummaryDataStore([]);
155 for (LinkedPubPackage package in linkedBundles) {
156 store.addBundle(null, package.unlinked);
157 store.addBundle(null, package.linked);
158 }
159 context.resultProvider =
160 new InputPackagesResultProvider(context, store);
161 }
162 }
163 }
164
165 Map<String, List<Folder>> convertPackagesToMap(Packages packages) {
166 Map<String, List<Folder>> folderMap = new HashMap<String, List<Folder>>();
167 if (packages != null && packages != Packages.noPackages) {
168 packages.asMap().forEach((String packageName, Uri uri) {
169 String path = resourceProvider.pathContext.fromUri(uri);
170 folderMap[packageName] = [resourceProvider.getFolder(path)];
171 });
172 }
173 return folderMap;
174 }
175
176 // void _processAnalysisOptions(
177 // AnalysisContext context, Map<String, YamlNode> optionMap) {
178 // List<OptionsProcessor> optionsProcessors =
179 // AnalysisEngine.instance.optionsPlugin.optionsProcessors;
180 // try {
181 // optionsProcessors.forEach(
182 // (OptionsProcessor p) => p.optionsProcessed(context, optionMap));
183 //
184 // // Fill in lint rule defaults in case lints are enabled and rules are
185 // // not specified in an options file.
186 // if (context.analysisOptions.lint && !containsLintRuleEntry(optionMap)) {
187 // setLints(context, linterPlugin.contributedRules);
188 // }
189 //
190 // // Ask engine to further process options.
191 // if (optionMap != null) {
192 // configureContextOptions(context, optionMap);
193 // }
194 // } on Exception catch (e) {
195 // optionsProcessors.forEach((OptionsProcessor p) => p.onError(e));
196 // }
197 // }
198
199 /**
200 * Return an analysis options object containing the default option values.
201 */
202 AnalysisOptions createDefaultOptions() {
203 if (defaultOptions == null) {
204 return new AnalysisOptionsImpl();
205 }
206 return new AnalysisOptionsImpl.from(defaultOptions);
207 }
208
209 Packages createPackageMap(String rootDirectoryPath) {
210 if (defaultPackageFilePath != null) {
211 File configFile = resourceProvider.getFile(defaultPackageFilePath);
212 List<int> bytes = configFile.readAsBytesSync();
213 Map<String, Uri> map = parse(bytes, configFile.toUri());
214 resolveSymbolicLinks(map);
215 return new MapPackages(map);
216 } else if (defaultPackagesDirectoryPath != null) {
217 Folder folder = resourceProvider.getFolder(defaultPackagesDirectoryPath);
218 return getPackagesFromFolder(folder);
219 }
220 return findPackagesFromFile(rootDirectoryPath);
221 }
222
223 SourceFactory createSourceFactory(
224 String rootDirectoryPath, AnalysisOptions options) {
225 Folder _folder = null;
226 Folder folder() {
227 return _folder ??= resourceProvider.getFolder(rootDirectoryPath);
228 }
229
230 UriResolver fileResolver;
231 if (fileResolverProvider != null) {
232 fileResolver = fileResolverProvider(folder());
233 }
234 fileResolver ??= new ResourceUriResolver(resourceProvider);
235 if (packageResolverProvider != null) {
236 UriResolver packageResolver = packageResolverProvider(folder());
237 if (packageResolver != null) {
238 // TODO(brianwilkerson) This doesn't support either embedder files or
239 // sdk extensions because we don't have a way to get the package map
240 // from the resolver.
241 List<UriResolver> resolvers = <UriResolver>[
242 new DartUriResolver(findSdk(null, options)),
243 packageResolver,
244 fileResolver
245 ];
246 return new SourceFactory(resolvers, null, resourceProvider);
247 }
248 }
249 Packages packages = createPackageMap(rootDirectoryPath);
250 Map<String, List<Folder>> packageMap = convertPackagesToMap(packages);
251 List<UriResolver> resolvers = <UriResolver>[
252 new DartUriResolver(findSdk(packageMap, options)),
253 new PackageMapUriResolver(resourceProvider, packageMap),
254 fileResolver
255 ];
256 return new SourceFactory(resolvers, packages, resourceProvider);
257 }
258
259 /**
260 * Add any [declaredVariables] to the list of declared variables used by the
261 * given [context].
262 */
263 void declareVariables(InternalAnalysisContext context) {
264 if (declaredVariables != null && declaredVariables.isNotEmpty) {
265 DeclaredVariables contextVariables = context.declaredVariables;
266 declaredVariables.forEach((String variableName, String value) {
267 contextVariables.define(variableName, value);
268 });
269 }
270 }
271
272 /**
273 * Finds a package resolution strategy for the directory at the given absolute
274 * [path].
275 *
276 * This function first tries to locate a `.packages` file in the directory. If
277 * that is not found, it instead checks for the presence of a `packages/`
278 * directory in the same place. If that also fails, it starts checking parent
279 * directories for a `.packages` file, and stops if it finds it. Otherwise it
280 * gives up and returns [Packages.noPackages].
281 */
282 Packages findPackagesFromFile(String path) {
283 Resource location = _findPackagesLocation(path);
284 if (location is File) {
285 List<int> fileBytes = location.readAsBytesSync();
286 Map<String, Uri> map =
287 parse(fileBytes, resourceProvider.pathContext.toUri(location.path));
288 resolveSymbolicLinks(map);
289 return new MapPackages(map);
290 } else if (location is Folder) {
291 return getPackagesFromFolder(location);
292 }
293 return Packages.noPackages;
294 }
295
296 /**
297 * Return the SDK that should be used to analyze code. Use the given
298 * [packageMap] and [options] to locate the SDK.
299 */
300 DartSdk findSdk(
301 Map<String, List<Folder>> packageMap, AnalysisOptions options) {
302 if (packageMap != null) {
303 SdkExtensionFinder extFinder = new SdkExtensionFinder(packageMap);
304 List<String> extFilePaths = extFinder.extensionFilePaths;
305 EmbedderYamlLocator locator = new EmbedderYamlLocator(packageMap);
306 Map<Folder, YamlMap> embedderYamls = locator.embedderYamls;
307 EmbedderSdk embedderSdk =
308 new EmbedderSdk(resourceProvider, embedderYamls);
309 if (embedderSdk.sdkLibraries.length > 0) {
310 //
311 // There is an embedder file that defines the content of the SDK and
312 // there might be an extension file that extends it.
313 //
314 List<String> paths = <String>[];
315 for (Folder folder in embedderYamls.keys) {
316 paths.add(folder
317 .getChildAssumingFile(EmbedderYamlLocator.EMBEDDER_FILE_NAME)
318 .path);
319 }
320 paths.addAll(extFilePaths);
321 SdkDescription description = new SdkDescription(paths, options);
322 DartSdk dartSdk = sdkManager.getSdk(description, () {
323 if (extFilePaths.isNotEmpty) {
324 embedderSdk.addExtensions(extFinder.urlMappings);
325 }
326 embedderSdk.analysisOptions = options;
327 embedderSdk.useSummary = sdkManager.canUseSummaries;
328 return embedderSdk;
329 });
330 return dartSdk;
331 } else if (extFilePaths != null && extFilePaths.isNotEmpty) {
332 //
333 // We have an extension file, but no embedder file.
334 //
335 String sdkPath = sdkManager.defaultSdkDirectory;
336 List<String> paths = <String>[sdkPath];
337 paths.addAll(extFilePaths);
338 SdkDescription description = new SdkDescription(paths, options);
339 return sdkManager.getSdk(description, () {
340 FolderBasedDartSdk sdk = new FolderBasedDartSdk(
341 resourceProvider, resourceProvider.getFolder(sdkPath));
342 if (extFilePaths.isNotEmpty) {
343 sdk.addExtensions(extFinder.urlMappings);
344 }
345 sdk.analysisOptions = options;
346 sdk.useSummary = sdkManager.canUseSummaries;
347 return sdk;
348 });
349 }
350 }
351 String sdkPath = sdkManager.defaultSdkDirectory;
352 SdkDescription description = new SdkDescription(<String>[sdkPath], options);
353 return sdkManager.getSdk(description, () {
354 FolderBasedDartSdk sdk = new FolderBasedDartSdk(
355 resourceProvider, resourceProvider.getFolder(sdkPath));
356 sdk.analysisOptions = options;
357 sdk.useSummary = sdkManager.canUseSummaries;
358 return sdk;
359 });
360 }
361
362 /**
363 * Return the analysis options that should be used when the given [context] is
364 * used to analyze code in the directory with the given [path].
365 */
366 AnalysisOptions getAnalysisOptions(AnalysisContext context, String path) {
367 AnalysisOptionsImpl options = createDefaultOptions();
368 File optionsFile = getOptionsFile(path);
369 if (optionsFile != null) {
370 List<OptionsProcessor> optionsProcessors =
371 AnalysisEngine.instance.optionsPlugin.optionsProcessors;
372 try {
373 Map<String, YamlNode> optionMap =
374 new AnalysisOptionsProvider().getOptionsFromFile(optionsFile);
375 optionsProcessors.forEach(
376 (OptionsProcessor p) => p.optionsProcessed(context, optionMap));
377 applyToAnalysisOptions(options, optionMap);
378 } on Exception catch (exception) {
379 optionsProcessors.forEach((OptionsProcessor p) => p.onError(exception));
380 }
381 }
382 return options;
383 }
384
385 /**
386 * Return the analysis options file that should be used when analyzing code in
387 * the directory with the given [path].
388 */
389 File getOptionsFile(String path) {
390 if (defaultAnalysisOptionsFilePath != null) {
391 return resourceProvider.getFile(defaultAnalysisOptionsFilePath);
392 }
393 Folder root = resourceProvider.getFolder(path);
394 for (Folder folder = root; folder != null; folder = folder.parent) {
395 File file =
396 folder.getChildAssumingFile(AnalysisEngine.ANALYSIS_OPTIONS_FILE);
397 if (file.exists) {
398 return file;
399 }
400 file = folder
401 .getChildAssumingFile(AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
402 if (file.exists) {
403 return file;
404 }
405 }
406 return null;
407 }
408
409 /**
410 * Create a [Packages] object for a 'package' directory ([folder]).
411 *
412 * Package names are resolved as relative to sub-directories of the package
413 * directory.
414 */
415 Packages getPackagesFromFolder(Folder folder) {
416 Context pathContext = resourceProvider.pathContext;
417 Map<String, Uri> map = new HashMap<String, Uri>();
418 for (Resource child in folder.getChildren()) {
419 if (child is Folder) {
420 // Inline resolveSymbolicLinks for performance reasons.
421 String packageName = pathContext.basename(child.path);
422 String folderPath = resolveSymbolicLink(child);
423 String uriPath = pathContext.join(folderPath, '.');
424 map[packageName] = pathContext.toUri(uriPath);
425 }
426 }
427 return new MapPackages(map);
428 }
429
430 /**
431 * Resolve any symbolic links encoded in the path to the given [folder].
432 */
433 String resolveSymbolicLink(Folder folder) {
434 try {
435 return folder.resolveSymbolicLinksSync().path;
436 } on FileSystemException {
437 return folder.path;
438 }
439 }
440
441 /**
442 * Resolve any symbolic links encoded in the URI's in the given [map] by
443 * replacing the values in the map.
444 */
445 void resolveSymbolicLinks(Map<String, Uri> map) {
446 Context pathContext = resourceProvider.pathContext;
447 for (String packageName in map.keys) {
448 Folder folder =
449 resourceProvider.getFolder(pathContext.fromUri(map[packageName]));
450 String folderPath = resolveSymbolicLink(folder);
451 // Add a '.' so that the URI is suitable for resolving relative URI's
452 // against it.
453 String uriPath = pathContext.join(folderPath, '.');
454 map[packageName] = pathContext.toUri(uriPath);
455 }
456 }
457
458 /**
459 * Find the location of the package resolution file/directory for the
460 * directory at the given absolute [path].
461 *
462 * Checks for a `.packages` file in the [path]. If not found,
463 * checks for a `packages` directory in the same directory. If still not
464 * found, starts checking parent directories for `.packages` until reaching
465 * the root directory.
466 *
467 * Return a [File] object representing a `.packages` file if one is found, a
468 * [Folder] object for the `packages/` directory if that is found, or `null`
469 * if neither is found.
470 */
471 Resource _findPackagesLocation(String path) {
472 Folder folder = resourceProvider.getFolder(path);
473 if (!folder.exists) {
474 throw new ArgumentError.value(path, "path", "Directory does not exist.");
475 }
476 File checkForConfigFile(Folder folder) {
477 File file = folder.getChildAssumingFile('.packages');
478 if (file.exists) {
479 return file;
480 }
481 return null;
482 }
483
484 // Check for $cwd/.packages
485 File packagesCfgFile = checkForConfigFile(folder);
486 if (packagesCfgFile != null) {
487 return packagesCfgFile;
488 }
489 // Check for $cwd/packages/
490 Folder packagesDir = folder.getChildAssumingFolder("packages");
491 if (packagesDir.exists) {
492 return packagesDir;
493 }
494 // Check for cwd(/..)+/.packages
495 Folder parentDir = folder.parent;
496 while (parentDir != null) {
497 packagesCfgFile = checkForConfigFile(parentDir);
498 if (packagesCfgFile != null) {
499 return packagesCfgFile;
500 }
501 parentDir = parentDir.parent;
502 }
503 return null;
504 }
505 }
506
507 /**
508 * Given a package map, check in each package's lib directory for the existence
509 * of an `_embedder.yaml` file. If the file contains a top level YamlMap, it
510 * will be added to the [embedderYamls] map.
511 */
512 class EmbedderYamlLocator {
513 /**
514 * The name of the embedder files being searched for.
515 */
516 static const String EMBEDDER_FILE_NAME = '_embedder.yaml';
517
518 /**
519 * A mapping from a package's library directory to the parsed YamlMap.
520 */
521 final Map<Folder, YamlMap> embedderYamls = new HashMap<Folder, YamlMap>();
522
523 /**
524 * Initialize a newly created locator by processing the packages in the given
525 * [packageMap].
526 */
527 EmbedderYamlLocator(Map<String, List<Folder>> packageMap) {
528 if (packageMap != null) {
529 _processPackageMap(packageMap);
530 }
531 }
532
533 /**
534 * Programmatically add an `_embedder.yaml` mapping.
535 */
536 void addEmbedderYaml(Folder libDir, String embedderYaml) {
537 _processEmbedderYaml(libDir, embedderYaml);
538 }
539
540 /**
541 * Refresh the map of located files to those found by processing the given
542 * [packageMap].
543 */
544 void refresh(Map<String, List<Folder>> packageMap) {
545 // Clear existing.
546 embedderYamls.clear();
547 if (packageMap != null) {
548 _processPackageMap(packageMap);
549 }
550 }
551
552 /**
553 * Given the yaml for an embedder ([embedderYaml]) and a folder ([libDir]),
554 * setup the uri mapping.
555 */
556 void _processEmbedderYaml(Folder libDir, String embedderYaml) {
557 try {
558 YamlNode yaml = loadYaml(embedderYaml);
559 if (yaml is YamlMap) {
560 embedderYamls[libDir] = yaml;
561 }
562 } catch (_) {
563 // Ignored
564 }
565 }
566
567 /**
568 * Given a package [name] and a list of folders ([libDirs]), process any
569 * `_embedder.yaml` files that are found in any of the folders.
570 */
571 void _processPackage(String name, List<Folder> libDirs) {
572 for (Folder libDir in libDirs) {
573 String embedderYaml = _readEmbedderYaml(libDir);
574 if (embedderYaml != null) {
575 _processEmbedderYaml(libDir, embedderYaml);
576 }
577 }
578 }
579
580 /**
581 * Process each of the entries in the [packageMap].
582 */
583 void _processPackageMap(Map<String, List<Folder>> packageMap) {
584 packageMap.forEach(_processPackage);
585 }
586
587 /**
588 * Read and return the contents of [libDir]/[EMBEDDER_FILE_NAME], or `null` if
589 * the file doesn't exist.
590 */
591 String _readEmbedderYaml(Folder libDir) {
592 File file = libDir.getChild(EMBEDDER_FILE_NAME);
593 try {
594 return file.readAsStringSync();
595 } on FileSystemException {
596 // File can't be read.
597 return null;
598 }
599 }
600 }
OLDNEW
« no previous file with comments | « packages/analyzer/lib/src/codegen/tools.dart ('k') | packages/analyzer/lib/src/context/cache.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698