| Index: packages/analyzer/lib/src/generated/package.dart
|
| diff --git a/packages/analyzer/lib/src/generated/package.dart b/packages/analyzer/lib/src/generated/package.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b715e3bd13efcd32cbcaa37476f1f1ce224bd3a6
|
| --- /dev/null
|
| +++ b/packages/analyzer/lib/src/generated/package.dart
|
| @@ -0,0 +1,239 @@
|
| +// 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.generated.package;
|
| +
|
| +import 'dart:collection';
|
| +
|
| +import 'package:analyzer/exception/exception.dart';
|
| +import 'package:analyzer/file_system/file_system.dart';
|
| +import 'package:analyzer/source/package_map_resolver.dart';
|
| +import 'package:analyzer/src/context/builder.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/generated/utilities_general.dart';
|
| +import 'package:package_config/packages.dart';
|
| +import 'package:yaml/yaml.dart';
|
| +
|
| +/**
|
| + * Traverses the package structure to determine the transitive dependencies for
|
| + * a given package.
|
| + */
|
| +class DependencyFinder {
|
| + /**
|
| + * The name of the pubspec.yaml file.
|
| + */
|
| + static const String pubspecName = 'pubspec.yaml';
|
| +
|
| + /**
|
| + * The resource provider used to access the file system.
|
| + */
|
| + final ResourceProvider resourceProvider;
|
| +
|
| + /**
|
| + * A table mapping the absolute paths of packages to a list of the names of
|
| + * the packages on which those packages depend.
|
| + */
|
| + final Map<String, List<String>> dependencyMap =
|
| + new HashMap<String, List<String>>();
|
| +
|
| + /**
|
| + * Initialize a newly created dependency finder to use the given
|
| + * [resourceProvider] to access the file system.
|
| + */
|
| + DependencyFinder(this.resourceProvider);
|
| +
|
| + /**
|
| + * Return a sorted list of the directories containing all of the packages on
|
| + * which the package at the given [packagePath] depends. The [packageMap]
|
| + * maps the names of packages to the directories in which they are contained.
|
| + *
|
| + * Throws an [AnalysisException] if any of the packages are missing their
|
| + * 'pubspec.yaml' file.
|
| + */
|
| + List<String> transitiveDependenciesFor(
|
| + Map<String, List<Folder>> packageMap, String packagePath) {
|
| + Set<String> processedPackages = new HashSet<String>();
|
| + Set<String> processedPaths = new HashSet<String>();
|
| + void process(String packageName) {
|
| + if (processedPackages.add(packageName)) {
|
| + List<Folder> folderList = packageMap[packageName];
|
| + if (folderList == null || folderList.isEmpty) {
|
| + throw new StateError('No mapping for package "$packageName"');
|
| + }
|
| + String packagePath = folderList[0].path;
|
| + processedPaths.add(packagePath);
|
| + List<String> dependencies = _dependenciesFor(packagePath);
|
| + for (String dependency in dependencies) {
|
| + process(dependency);
|
| + }
|
| + }
|
| + }
|
| +
|
| + List<String> dependencies = _dependenciesFor(packagePath);
|
| + dependencies.forEach(process);
|
| + processedPaths.remove(packagePath);
|
| + List<String> transitiveDependencies = processedPaths.toList();
|
| + transitiveDependencies.sort();
|
| + return transitiveDependencies;
|
| + }
|
| +
|
| + /**
|
| + * Add to the given set of [dependecies] all of the package names used as keys
|
| + * in the given [yamlMap].
|
| + */
|
| + void _collectDependencies(HashSet<String> dependencies, YamlMap yamlMap) {
|
| + if (yamlMap is Map) {
|
| + for (var key in yamlMap.keys) {
|
| + if (key is String) {
|
| + dependencies.add(key);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Return a list of the names of the packages on which the package at the
|
| + * [packagePath] depends.
|
| + */
|
| + List<String> _dependenciesFor(String packagePath) {
|
| + return dependencyMap.putIfAbsent(packagePath, () {
|
| + Set<String> dependencies = new HashSet<String>();
|
| + YamlNode yamlNode = _readPubspec(packagePath);
|
| + if (yamlNode is YamlMap) {
|
| + _collectDependencies(dependencies, yamlNode['dependencies']);
|
| + }
|
| + return dependencies.toList();
|
| + });
|
| + }
|
| +
|
| + /**
|
| + * Read the content of the pubspec file in the directory at the given
|
| + * [directoryPath]. Return `null` if the file does not exist, cannot be read,
|
| + * or has content that is not valid YAML.
|
| + */
|
| + YamlNode _readPubspec(String directoryPath) {
|
| + try {
|
| + File yamlFile = resourceProvider
|
| + .getFolder(directoryPath)
|
| + .getChildAssumingFile(pubspecName);
|
| + String yamlContent = yamlFile.readAsStringSync();
|
| + return loadYamlNode(yamlContent);
|
| + } catch (exception, stackTrace) {
|
| + throw new AnalysisException('Missing $pubspecName in $directoryPath',
|
| + new CaughtException(exception, stackTrace));
|
| + }
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * A description of the context in which a package will be analyzed.
|
| + */
|
| +class PackageDescription {
|
| + /**
|
| + * The id of the package being described. The id encodes the actual locations
|
| + * of the package itself and all of the packages on which it depends.
|
| + */
|
| + final String id;
|
| +
|
| + /**
|
| + * The SDK against which the package will be analyzed.
|
| + */
|
| + final DartSdk sdk;
|
| +
|
| + /**
|
| + * The analysis options that will be used when analyzing the package.
|
| + */
|
| + final AnalysisOptions options;
|
| +
|
| + /**
|
| + * Initialize a newly create package description to describe the package with
|
| + * the given [id] that is being analyzed against the given [sdk] using the
|
| + * given [options].
|
| + */
|
| + PackageDescription(this.id, this.sdk, this.options);
|
| +
|
| + @override
|
| + int get hashCode {
|
| + int hashCode = options.encodeCrossContextOptions();
|
| + hashCode = JenkinsSmiHash.combine(hashCode, id.hashCode);
|
| + hashCode = JenkinsSmiHash.combine(hashCode, sdk.hashCode);
|
| + return JenkinsSmiHash.finish(hashCode);
|
| + }
|
| +
|
| + @override
|
| + bool operator ==(Object other) {
|
| + return other is PackageDescription &&
|
| + other.sdk == sdk &&
|
| + other.options.encodeCrossContextOptions() ==
|
| + options.encodeCrossContextOptions() &&
|
| + other.id == id;
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * Manages the contexts in which each package is analyzed.
|
| + */
|
| +class PackageManager {
|
| + /**
|
| + * The resource provider used to access the file system.
|
| + */
|
| + final ResourceProvider resourceProvider;
|
| +
|
| + /**
|
| + * A table mapping the id's of packages to the context in which the package is
|
| + * analyzed.
|
| + */
|
| + final Map<PackageDescription, AnalysisContext> contextMap =
|
| + new HashMap<PackageDescription, AnalysisContext>();
|
| +
|
| + /**
|
| + * Initialize a newly created package manager.
|
| + */
|
| + PackageManager(this.resourceProvider);
|
| +
|
| + /**
|
| + * Return the context in which the package at the given [packagePath] should
|
| + * be analyzed when the given [packages] object is used to resolve package
|
| + * names, the given [resolver] will be used to resolve 'dart:' URI's, and the
|
| + * given [options] will control the analysis.
|
| + */
|
| + AnalysisContext getContext(String packagePath, Packages packages,
|
| + DartUriResolver resolver, AnalysisOptions options) {
|
| + DartSdk sdk = resolver.dartSdk;
|
| + Map<String, List<Folder>> packageMap =
|
| + new ContextBuilder(resourceProvider, null, null)
|
| + .convertPackagesToMap(packages);
|
| + PackageDescription description =
|
| + new PackageDescription(_buildId(packagePath, packageMap), sdk, options);
|
| + return contextMap.putIfAbsent(description, () {
|
| + AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
|
| + context.sourceFactory = new SourceFactory(<UriResolver>[
|
| + resolver,
|
| + new PackageMapUriResolver(resourceProvider, packageMap),
|
| + new ResourceUriResolver(resourceProvider)
|
| + ], packages, resourceProvider);
|
| + context.analysisOptions = options;
|
| + return context;
|
| + });
|
| + }
|
| +
|
| + /**
|
| + * Return the id associated with the package at the given [packagePath] when
|
| + * the given [packageMap] is used to resolve package names.
|
| + */
|
| + String _buildId(String packagePath, Map<String, List<Folder>> packageMap) {
|
| + DependencyFinder finder = new DependencyFinder(resourceProvider);
|
| + List<String> dependencies =
|
| + finder.transitiveDependenciesFor(packageMap, packagePath);
|
| + StringBuffer buffer = new StringBuffer();
|
| + buffer.write(packagePath);
|
| + for (String dependency in dependencies) {
|
| + buffer.write(';');
|
| + buffer.write(dependency);
|
| + }
|
| + return buffer.toString();
|
| + }
|
| +}
|
|
|