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

Unified Diff: packages/analyzer/lib/src/context/builder.dart

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 years, 5 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: packages/analyzer/lib/src/context/builder.dart
diff --git a/packages/analyzer/lib/src/context/builder.dart b/packages/analyzer/lib/src/context/builder.dart
new file mode 100644
index 0000000000000000000000000000000000000000..02e1237a2eae93b76517e3a6970c256812e3c5be
--- /dev/null
+++ b/packages/analyzer/lib/src/context/builder.dart
@@ -0,0 +1,600 @@
+// 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.
+
+library analyzer.src.context.context_builder;
+
+import 'dart:collection';
+import 'dart:core';
+
+import 'package:analyzer/context/declared_variables.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/plugin/options.dart';
+import 'package:analyzer/plugin/resolver_provider.dart';
+import 'package:analyzer/source/analysis_options_provider.dart';
+import 'package:analyzer/source/package_map_resolver.dart';
+import 'package:analyzer/src/dart/sdk/sdk.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/sdk.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/summary/package_bundle_reader.dart';
+import 'package:analyzer/src/summary/pub_summary.dart';
+import 'package:analyzer/src/task/options.dart';
+import 'package:package_config/packages.dart';
+import 'package:package_config/packages_file.dart';
+import 'package:package_config/src/packages_impl.dart';
+import 'package:path/src/context.dart';
+import 'package:yaml/yaml.dart';
+
+/**
+ * A utility class used to build an analysis context for a given directory.
+ *
+ * The construction of analysis contexts is as follows:
+ *
+ * 1. Determine how package: URI's are to be resolved. This follows the lookup
+ * algorithm defined by the [package specification][1].
+ *
+ * 2. Using the results of step 1, look in each package for an embedder file
+ * (_embedder.yaml). If one exists then it defines the SDK. If multiple such
+ * files exist then use the first one found. Otherwise, use the default SDK.
+ *
+ * 3. Look in each package for an SDK extension file (_sdkext). For each such
+ * file, add the specified files to the SDK.
+ *
+ * 4. Look for an analysis options file (`analyis_options.yaml` or
+ * `.analysis_options`) and process the options in the file.
+ *
+ * 5. Create a new context. Initialize its source factory based on steps 1, 2
+ * and 3. Initialize its analysis options from step 4.
+ *
+ * [1]: https://github.com/dart-lang/dart_enhancement_proposals/blob/master/Accepted/0005%20-%20Package%20Specification/DEP-pkgspec.md.
+ */
+class ContextBuilder {
+ /**
+ * The [ResourceProvider] by which paths are converted into [Resource]s.
+ */
+ final ResourceProvider resourceProvider;
+
+ /**
+ * The manager used to manage the DartSdk's that have been created so that
+ * they can be shared across contexts.
+ */
+ final DartSdkManager sdkManager;
+
+ /**
+ * The cache containing the contents of overlaid files.
+ */
+ final ContentCache contentCache;
+
+ /**
+ * The resolver provider used to create a package: URI resolver, or `null` if
+ * the normal (Package Specification DEP) lookup mechanism is to be used.
+ */
+ ResolverProvider packageResolverProvider;
+
+ /**
+ * The resolver provider used to create a file: URI resolver, or `null` if
+ * the normal file URI resolver is to be used.
+ */
+ ResolverProvider fileResolverProvider;
+
+ /**
+ * The file path of the .packages file that should be used in place of any
+ * file found using the normal (Package Specification DEP) lookup mechanism,
+ * or `null` if the normal lookup mechanism should be used.
+ */
+ String defaultPackageFilePath;
+
+ /**
+ * The file path of the packages directory that should be used in place of any
+ * file found using the normal (Package Specification DEP) lookup mechanism,
+ * or `null` if the normal lookup mechanism should be used.
+ */
+ String defaultPackagesDirectoryPath;
+
+ /**
+ * The file path of the analysis options file that should be used in place of
+ * any file in the root directory or a parent of the root directory, or `null`
+ * if the normal lookup mechanism should be used.
+ */
+ String defaultAnalysisOptionsFilePath;
+
+ /**
+ * The default analysis options that should be used unless some or all of them
+ * are overridden in the analysis options file, or `null` if the default
+ * defaults should be used.
+ */
+ AnalysisOptions defaultOptions;
+
+ /**
+ * A table mapping variable names to values for the declared variables, or
+ * `null` if no additional variables should be declared.
+ */
+ Map<String, String> declaredVariables;
+
+ /**
+ * The manager of pub package summaries.
+ */
+ PubSummaryManager pubSummaryManager;
+
+ /**
+ * Initialize a newly created builder to be ready to build a context rooted in
+ * the directory with the given [rootDirectoryPath].
+ */
+ ContextBuilder(this.resourceProvider, this.sdkManager, this.contentCache);
+
+ /**
+ * Return an analysis context that is configured correctly to analyze code in
+ * the directory with the given [path].
+ *
+ * *Note:* This method is not yet fully implemented and should not be used.
+ */
+ AnalysisContext buildContext(String path) {
+ InternalAnalysisContext context =
+ AnalysisEngine.instance.createAnalysisContext();
+ AnalysisOptions options = getAnalysisOptions(context, path);
+ context.contentCache = contentCache;
+ context.sourceFactory = createSourceFactory(path, options);
+ context.analysisOptions = options;
+ context.name = path;
+ //_processAnalysisOptions(context, optionMap);
+ declareVariables(context);
+ configureSummaries(context);
+ return context;
+ }
+
+ /**
+ * Configure the context to make use of summaries.
+ */
+ void configureSummaries(InternalAnalysisContext context) {
+ if (pubSummaryManager != null) {
+ List<LinkedPubPackage> linkedBundles =
+ pubSummaryManager.getLinkedBundles(context);
+ if (linkedBundles.isNotEmpty) {
+ SummaryDataStore store = new SummaryDataStore([]);
+ for (LinkedPubPackage package in linkedBundles) {
+ store.addBundle(null, package.unlinked);
+ store.addBundle(null, package.linked);
+ }
+ context.resultProvider =
+ new InputPackagesResultProvider(context, store);
+ }
+ }
+ }
+
+ Map<String, List<Folder>> convertPackagesToMap(Packages packages) {
+ Map<String, List<Folder>> folderMap = new HashMap<String, List<Folder>>();
+ if (packages != null && packages != Packages.noPackages) {
+ packages.asMap().forEach((String packageName, Uri uri) {
+ String path = resourceProvider.pathContext.fromUri(uri);
+ folderMap[packageName] = [resourceProvider.getFolder(path)];
+ });
+ }
+ return folderMap;
+ }
+
+// void _processAnalysisOptions(
+// AnalysisContext context, Map<String, YamlNode> optionMap) {
+// List<OptionsProcessor> optionsProcessors =
+// AnalysisEngine.instance.optionsPlugin.optionsProcessors;
+// try {
+// optionsProcessors.forEach(
+// (OptionsProcessor p) => p.optionsProcessed(context, optionMap));
+//
+// // Fill in lint rule defaults in case lints are enabled and rules are
+// // not specified in an options file.
+// if (context.analysisOptions.lint && !containsLintRuleEntry(optionMap)) {
+// setLints(context, linterPlugin.contributedRules);
+// }
+//
+// // Ask engine to further process options.
+// if (optionMap != null) {
+// configureContextOptions(context, optionMap);
+// }
+// } on Exception catch (e) {
+// optionsProcessors.forEach((OptionsProcessor p) => p.onError(e));
+// }
+// }
+
+ /**
+ * Return an analysis options object containing the default option values.
+ */
+ AnalysisOptions createDefaultOptions() {
+ if (defaultOptions == null) {
+ return new AnalysisOptionsImpl();
+ }
+ return new AnalysisOptionsImpl.from(defaultOptions);
+ }
+
+ Packages createPackageMap(String rootDirectoryPath) {
+ if (defaultPackageFilePath != null) {
+ File configFile = resourceProvider.getFile(defaultPackageFilePath);
+ List<int> bytes = configFile.readAsBytesSync();
+ Map<String, Uri> map = parse(bytes, configFile.toUri());
+ resolveSymbolicLinks(map);
+ return new MapPackages(map);
+ } else if (defaultPackagesDirectoryPath != null) {
+ Folder folder = resourceProvider.getFolder(defaultPackagesDirectoryPath);
+ return getPackagesFromFolder(folder);
+ }
+ return findPackagesFromFile(rootDirectoryPath);
+ }
+
+ SourceFactory createSourceFactory(
+ String rootDirectoryPath, AnalysisOptions options) {
+ Folder _folder = null;
+ Folder folder() {
+ return _folder ??= resourceProvider.getFolder(rootDirectoryPath);
+ }
+
+ UriResolver fileResolver;
+ if (fileResolverProvider != null) {
+ fileResolver = fileResolverProvider(folder());
+ }
+ fileResolver ??= new ResourceUriResolver(resourceProvider);
+ if (packageResolverProvider != null) {
+ UriResolver packageResolver = packageResolverProvider(folder());
+ if (packageResolver != null) {
+ // TODO(brianwilkerson) This doesn't support either embedder files or
+ // sdk extensions because we don't have a way to get the package map
+ // from the resolver.
+ List<UriResolver> resolvers = <UriResolver>[
+ new DartUriResolver(findSdk(null, options)),
+ packageResolver,
+ fileResolver
+ ];
+ return new SourceFactory(resolvers, null, resourceProvider);
+ }
+ }
+ Packages packages = createPackageMap(rootDirectoryPath);
+ Map<String, List<Folder>> packageMap = convertPackagesToMap(packages);
+ List<UriResolver> resolvers = <UriResolver>[
+ new DartUriResolver(findSdk(packageMap, options)),
+ new PackageMapUriResolver(resourceProvider, packageMap),
+ fileResolver
+ ];
+ return new SourceFactory(resolvers, packages, resourceProvider);
+ }
+
+ /**
+ * Add any [declaredVariables] to the list of declared variables used by the
+ * given [context].
+ */
+ void declareVariables(InternalAnalysisContext context) {
+ if (declaredVariables != null && declaredVariables.isNotEmpty) {
+ DeclaredVariables contextVariables = context.declaredVariables;
+ declaredVariables.forEach((String variableName, String value) {
+ contextVariables.define(variableName, value);
+ });
+ }
+ }
+
+ /**
+ * Finds a package resolution strategy for the directory at the given absolute
+ * [path].
+ *
+ * This function first tries to locate a `.packages` file in the directory. If
+ * that is not found, it instead checks for the presence of a `packages/`
+ * directory in the same place. If that also fails, it starts checking parent
+ * directories for a `.packages` file, and stops if it finds it. Otherwise it
+ * gives up and returns [Packages.noPackages].
+ */
+ Packages findPackagesFromFile(String path) {
+ Resource location = _findPackagesLocation(path);
+ if (location is File) {
+ List<int> fileBytes = location.readAsBytesSync();
+ Map<String, Uri> map =
+ parse(fileBytes, resourceProvider.pathContext.toUri(location.path));
+ resolveSymbolicLinks(map);
+ return new MapPackages(map);
+ } else if (location is Folder) {
+ return getPackagesFromFolder(location);
+ }
+ return Packages.noPackages;
+ }
+
+ /**
+ * Return the SDK that should be used to analyze code. Use the given
+ * [packageMap] and [options] to locate the SDK.
+ */
+ DartSdk findSdk(
+ Map<String, List<Folder>> packageMap, AnalysisOptions options) {
+ if (packageMap != null) {
+ SdkExtensionFinder extFinder = new SdkExtensionFinder(packageMap);
+ List<String> extFilePaths = extFinder.extensionFilePaths;
+ EmbedderYamlLocator locator = new EmbedderYamlLocator(packageMap);
+ Map<Folder, YamlMap> embedderYamls = locator.embedderYamls;
+ EmbedderSdk embedderSdk =
+ new EmbedderSdk(resourceProvider, embedderYamls);
+ if (embedderSdk.sdkLibraries.length > 0) {
+ //
+ // There is an embedder file that defines the content of the SDK and
+ // there might be an extension file that extends it.
+ //
+ List<String> paths = <String>[];
+ for (Folder folder in embedderYamls.keys) {
+ paths.add(folder
+ .getChildAssumingFile(EmbedderYamlLocator.EMBEDDER_FILE_NAME)
+ .path);
+ }
+ paths.addAll(extFilePaths);
+ SdkDescription description = new SdkDescription(paths, options);
+ DartSdk dartSdk = sdkManager.getSdk(description, () {
+ if (extFilePaths.isNotEmpty) {
+ embedderSdk.addExtensions(extFinder.urlMappings);
+ }
+ embedderSdk.analysisOptions = options;
+ embedderSdk.useSummary = sdkManager.canUseSummaries;
+ return embedderSdk;
+ });
+ return dartSdk;
+ } else if (extFilePaths != null && extFilePaths.isNotEmpty) {
+ //
+ // We have an extension file, but no embedder file.
+ //
+ String sdkPath = sdkManager.defaultSdkDirectory;
+ List<String> paths = <String>[sdkPath];
+ paths.addAll(extFilePaths);
+ SdkDescription description = new SdkDescription(paths, options);
+ return sdkManager.getSdk(description, () {
+ FolderBasedDartSdk sdk = new FolderBasedDartSdk(
+ resourceProvider, resourceProvider.getFolder(sdkPath));
+ if (extFilePaths.isNotEmpty) {
+ sdk.addExtensions(extFinder.urlMappings);
+ }
+ sdk.analysisOptions = options;
+ sdk.useSummary = sdkManager.canUseSummaries;
+ return sdk;
+ });
+ }
+ }
+ String sdkPath = sdkManager.defaultSdkDirectory;
+ SdkDescription description = new SdkDescription(<String>[sdkPath], options);
+ return sdkManager.getSdk(description, () {
+ FolderBasedDartSdk sdk = new FolderBasedDartSdk(
+ resourceProvider, resourceProvider.getFolder(sdkPath));
+ sdk.analysisOptions = options;
+ sdk.useSummary = sdkManager.canUseSummaries;
+ return sdk;
+ });
+ }
+
+ /**
+ * Return the analysis options that should be used when the given [context] is
+ * used to analyze code in the directory with the given [path].
+ */
+ AnalysisOptions getAnalysisOptions(AnalysisContext context, String path) {
+ AnalysisOptionsImpl options = createDefaultOptions();
+ File optionsFile = getOptionsFile(path);
+ if (optionsFile != null) {
+ List<OptionsProcessor> optionsProcessors =
+ AnalysisEngine.instance.optionsPlugin.optionsProcessors;
+ try {
+ Map<String, YamlNode> optionMap =
+ new AnalysisOptionsProvider().getOptionsFromFile(optionsFile);
+ optionsProcessors.forEach(
+ (OptionsProcessor p) => p.optionsProcessed(context, optionMap));
+ applyToAnalysisOptions(options, optionMap);
+ } on Exception catch (exception) {
+ optionsProcessors.forEach((OptionsProcessor p) => p.onError(exception));
+ }
+ }
+ return options;
+ }
+
+ /**
+ * Return the analysis options file that should be used when analyzing code in
+ * the directory with the given [path].
+ */
+ File getOptionsFile(String path) {
+ if (defaultAnalysisOptionsFilePath != null) {
+ return resourceProvider.getFile(defaultAnalysisOptionsFilePath);
+ }
+ Folder root = resourceProvider.getFolder(path);
+ for (Folder folder = root; folder != null; folder = folder.parent) {
+ File file =
+ folder.getChildAssumingFile(AnalysisEngine.ANALYSIS_OPTIONS_FILE);
+ if (file.exists) {
+ return file;
+ }
+ file = folder
+ .getChildAssumingFile(AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
+ if (file.exists) {
+ return file;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Create a [Packages] object for a 'package' directory ([folder]).
+ *
+ * Package names are resolved as relative to sub-directories of the package
+ * directory.
+ */
+ Packages getPackagesFromFolder(Folder folder) {
+ Context pathContext = resourceProvider.pathContext;
+ Map<String, Uri> map = new HashMap<String, Uri>();
+ for (Resource child in folder.getChildren()) {
+ if (child is Folder) {
+ // Inline resolveSymbolicLinks for performance reasons.
+ String packageName = pathContext.basename(child.path);
+ String folderPath = resolveSymbolicLink(child);
+ String uriPath = pathContext.join(folderPath, '.');
+ map[packageName] = pathContext.toUri(uriPath);
+ }
+ }
+ return new MapPackages(map);
+ }
+
+ /**
+ * Resolve any symbolic links encoded in the path to the given [folder].
+ */
+ String resolveSymbolicLink(Folder folder) {
+ try {
+ return folder.resolveSymbolicLinksSync().path;
+ } on FileSystemException {
+ return folder.path;
+ }
+ }
+
+ /**
+ * Resolve any symbolic links encoded in the URI's in the given [map] by
+ * replacing the values in the map.
+ */
+ void resolveSymbolicLinks(Map<String, Uri> map) {
+ Context pathContext = resourceProvider.pathContext;
+ for (String packageName in map.keys) {
+ Folder folder =
+ resourceProvider.getFolder(pathContext.fromUri(map[packageName]));
+ String folderPath = resolveSymbolicLink(folder);
+ // Add a '.' so that the URI is suitable for resolving relative URI's
+ // against it.
+ String uriPath = pathContext.join(folderPath, '.');
+ map[packageName] = pathContext.toUri(uriPath);
+ }
+ }
+
+ /**
+ * Find the location of the package resolution file/directory for the
+ * directory at the given absolute [path].
+ *
+ * Checks for a `.packages` file in the [path]. If not found,
+ * checks for a `packages` directory in the same directory. If still not
+ * found, starts checking parent directories for `.packages` until reaching
+ * the root directory.
+ *
+ * Return a [File] object representing a `.packages` file if one is found, a
+ * [Folder] object for the `packages/` directory if that is found, or `null`
+ * if neither is found.
+ */
+ Resource _findPackagesLocation(String path) {
+ Folder folder = resourceProvider.getFolder(path);
+ if (!folder.exists) {
+ throw new ArgumentError.value(path, "path", "Directory does not exist.");
+ }
+ File checkForConfigFile(Folder folder) {
+ File file = folder.getChildAssumingFile('.packages');
+ if (file.exists) {
+ return file;
+ }
+ return null;
+ }
+
+ // Check for $cwd/.packages
+ File packagesCfgFile = checkForConfigFile(folder);
+ if (packagesCfgFile != null) {
+ return packagesCfgFile;
+ }
+ // Check for $cwd/packages/
+ Folder packagesDir = folder.getChildAssumingFolder("packages");
+ if (packagesDir.exists) {
+ return packagesDir;
+ }
+ // Check for cwd(/..)+/.packages
+ Folder parentDir = folder.parent;
+ while (parentDir != null) {
+ packagesCfgFile = checkForConfigFile(parentDir);
+ if (packagesCfgFile != null) {
+ return packagesCfgFile;
+ }
+ parentDir = parentDir.parent;
+ }
+ return null;
+ }
+}
+
+/**
+ * Given a package map, check in each package's lib directory for the existence
+ * of an `_embedder.yaml` file. If the file contains a top level YamlMap, it
+ * will be added to the [embedderYamls] map.
+ */
+class EmbedderYamlLocator {
+ /**
+ * The name of the embedder files being searched for.
+ */
+ static const String EMBEDDER_FILE_NAME = '_embedder.yaml';
+
+ /**
+ * A mapping from a package's library directory to the parsed YamlMap.
+ */
+ final Map<Folder, YamlMap> embedderYamls = new HashMap<Folder, YamlMap>();
+
+ /**
+ * Initialize a newly created locator by processing the packages in the given
+ * [packageMap].
+ */
+ EmbedderYamlLocator(Map<String, List<Folder>> packageMap) {
+ if (packageMap != null) {
+ _processPackageMap(packageMap);
+ }
+ }
+
+ /**
+ * Programmatically add an `_embedder.yaml` mapping.
+ */
+ void addEmbedderYaml(Folder libDir, String embedderYaml) {
+ _processEmbedderYaml(libDir, embedderYaml);
+ }
+
+ /**
+ * Refresh the map of located files to those found by processing the given
+ * [packageMap].
+ */
+ void refresh(Map<String, List<Folder>> packageMap) {
+ // Clear existing.
+ embedderYamls.clear();
+ if (packageMap != null) {
+ _processPackageMap(packageMap);
+ }
+ }
+
+ /**
+ * Given the yaml for an embedder ([embedderYaml]) and a folder ([libDir]),
+ * setup the uri mapping.
+ */
+ void _processEmbedderYaml(Folder libDir, String embedderYaml) {
+ try {
+ YamlNode yaml = loadYaml(embedderYaml);
+ if (yaml is YamlMap) {
+ embedderYamls[libDir] = yaml;
+ }
+ } catch (_) {
+ // Ignored
+ }
+ }
+
+ /**
+ * Given a package [name] and a list of folders ([libDirs]), process any
+ * `_embedder.yaml` files that are found in any of the folders.
+ */
+ void _processPackage(String name, List<Folder> libDirs) {
+ for (Folder libDir in libDirs) {
+ String embedderYaml = _readEmbedderYaml(libDir);
+ if (embedderYaml != null) {
+ _processEmbedderYaml(libDir, embedderYaml);
+ }
+ }
+ }
+
+ /**
+ * Process each of the entries in the [packageMap].
+ */
+ void _processPackageMap(Map<String, List<Folder>> packageMap) {
+ packageMap.forEach(_processPackage);
+ }
+
+ /**
+ * Read and return the contents of [libDir]/[EMBEDDER_FILE_NAME], or `null` if
+ * the file doesn't exist.
+ */
+ String _readEmbedderYaml(Folder libDir) {
+ File file = libDir.getChild(EMBEDDER_FILE_NAME);
+ try {
+ return file.readAsStringSync();
+ } on FileSystemException {
+ // File can't be read.
+ return null;
+ }
+ }
+}
« 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