| OLD | NEW |
| (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.generated.package; |
| 6 |
| 7 import 'dart:collection'; |
| 8 |
| 9 import 'package:analyzer/exception/exception.dart'; |
| 10 import 'package:analyzer/file_system/file_system.dart'; |
| 11 import 'package:analyzer/source/package_map_resolver.dart'; |
| 12 import 'package:analyzer/src/context/builder.dart'; |
| 13 import 'package:analyzer/src/generated/engine.dart'; |
| 14 import 'package:analyzer/src/generated/sdk.dart'; |
| 15 import 'package:analyzer/src/generated/source.dart'; |
| 16 import 'package:analyzer/src/generated/utilities_general.dart'; |
| 17 import 'package:package_config/packages.dart'; |
| 18 import 'package:yaml/yaml.dart'; |
| 19 |
| 20 /** |
| 21 * Traverses the package structure to determine the transitive dependencies for |
| 22 * a given package. |
| 23 */ |
| 24 class DependencyFinder { |
| 25 /** |
| 26 * The name of the pubspec.yaml file. |
| 27 */ |
| 28 static const String pubspecName = 'pubspec.yaml'; |
| 29 |
| 30 /** |
| 31 * The resource provider used to access the file system. |
| 32 */ |
| 33 final ResourceProvider resourceProvider; |
| 34 |
| 35 /** |
| 36 * A table mapping the absolute paths of packages to a list of the names of |
| 37 * the packages on which those packages depend. |
| 38 */ |
| 39 final Map<String, List<String>> dependencyMap = |
| 40 new HashMap<String, List<String>>(); |
| 41 |
| 42 /** |
| 43 * Initialize a newly created dependency finder to use the given |
| 44 * [resourceProvider] to access the file system. |
| 45 */ |
| 46 DependencyFinder(this.resourceProvider); |
| 47 |
| 48 /** |
| 49 * Return a sorted list of the directories containing all of the packages on |
| 50 * which the package at the given [packagePath] depends. The [packageMap] |
| 51 * maps the names of packages to the directories in which they are contained. |
| 52 * |
| 53 * Throws an [AnalysisException] if any of the packages are missing their |
| 54 * 'pubspec.yaml' file. |
| 55 */ |
| 56 List<String> transitiveDependenciesFor( |
| 57 Map<String, List<Folder>> packageMap, String packagePath) { |
| 58 Set<String> processedPackages = new HashSet<String>(); |
| 59 Set<String> processedPaths = new HashSet<String>(); |
| 60 void process(String packageName) { |
| 61 if (processedPackages.add(packageName)) { |
| 62 List<Folder> folderList = packageMap[packageName]; |
| 63 if (folderList == null || folderList.isEmpty) { |
| 64 throw new StateError('No mapping for package "$packageName"'); |
| 65 } |
| 66 String packagePath = folderList[0].path; |
| 67 processedPaths.add(packagePath); |
| 68 List<String> dependencies = _dependenciesFor(packagePath); |
| 69 for (String dependency in dependencies) { |
| 70 process(dependency); |
| 71 } |
| 72 } |
| 73 } |
| 74 |
| 75 List<String> dependencies = _dependenciesFor(packagePath); |
| 76 dependencies.forEach(process); |
| 77 processedPaths.remove(packagePath); |
| 78 List<String> transitiveDependencies = processedPaths.toList(); |
| 79 transitiveDependencies.sort(); |
| 80 return transitiveDependencies; |
| 81 } |
| 82 |
| 83 /** |
| 84 * Add to the given set of [dependecies] all of the package names used as keys |
| 85 * in the given [yamlMap]. |
| 86 */ |
| 87 void _collectDependencies(HashSet<String> dependencies, YamlMap yamlMap) { |
| 88 if (yamlMap is Map) { |
| 89 for (var key in yamlMap.keys) { |
| 90 if (key is String) { |
| 91 dependencies.add(key); |
| 92 } |
| 93 } |
| 94 } |
| 95 } |
| 96 |
| 97 /** |
| 98 * Return a list of the names of the packages on which the package at the |
| 99 * [packagePath] depends. |
| 100 */ |
| 101 List<String> _dependenciesFor(String packagePath) { |
| 102 return dependencyMap.putIfAbsent(packagePath, () { |
| 103 Set<String> dependencies = new HashSet<String>(); |
| 104 YamlNode yamlNode = _readPubspec(packagePath); |
| 105 if (yamlNode is YamlMap) { |
| 106 _collectDependencies(dependencies, yamlNode['dependencies']); |
| 107 } |
| 108 return dependencies.toList(); |
| 109 }); |
| 110 } |
| 111 |
| 112 /** |
| 113 * Read the content of the pubspec file in the directory at the given |
| 114 * [directoryPath]. Return `null` if the file does not exist, cannot be read, |
| 115 * or has content that is not valid YAML. |
| 116 */ |
| 117 YamlNode _readPubspec(String directoryPath) { |
| 118 try { |
| 119 File yamlFile = resourceProvider |
| 120 .getFolder(directoryPath) |
| 121 .getChildAssumingFile(pubspecName); |
| 122 String yamlContent = yamlFile.readAsStringSync(); |
| 123 return loadYamlNode(yamlContent); |
| 124 } catch (exception, stackTrace) { |
| 125 throw new AnalysisException('Missing $pubspecName in $directoryPath', |
| 126 new CaughtException(exception, stackTrace)); |
| 127 } |
| 128 } |
| 129 } |
| 130 |
| 131 /** |
| 132 * A description of the context in which a package will be analyzed. |
| 133 */ |
| 134 class PackageDescription { |
| 135 /** |
| 136 * The id of the package being described. The id encodes the actual locations |
| 137 * of the package itself and all of the packages on which it depends. |
| 138 */ |
| 139 final String id; |
| 140 |
| 141 /** |
| 142 * The SDK against which the package will be analyzed. |
| 143 */ |
| 144 final DartSdk sdk; |
| 145 |
| 146 /** |
| 147 * The analysis options that will be used when analyzing the package. |
| 148 */ |
| 149 final AnalysisOptions options; |
| 150 |
| 151 /** |
| 152 * Initialize a newly create package description to describe the package with |
| 153 * the given [id] that is being analyzed against the given [sdk] using the |
| 154 * given [options]. |
| 155 */ |
| 156 PackageDescription(this.id, this.sdk, this.options); |
| 157 |
| 158 @override |
| 159 int get hashCode { |
| 160 int hashCode = options.encodeCrossContextOptions(); |
| 161 hashCode = JenkinsSmiHash.combine(hashCode, id.hashCode); |
| 162 hashCode = JenkinsSmiHash.combine(hashCode, sdk.hashCode); |
| 163 return JenkinsSmiHash.finish(hashCode); |
| 164 } |
| 165 |
| 166 @override |
| 167 bool operator ==(Object other) { |
| 168 return other is PackageDescription && |
| 169 other.sdk == sdk && |
| 170 other.options.encodeCrossContextOptions() == |
| 171 options.encodeCrossContextOptions() && |
| 172 other.id == id; |
| 173 } |
| 174 } |
| 175 |
| 176 /** |
| 177 * Manages the contexts in which each package is analyzed. |
| 178 */ |
| 179 class PackageManager { |
| 180 /** |
| 181 * The resource provider used to access the file system. |
| 182 */ |
| 183 final ResourceProvider resourceProvider; |
| 184 |
| 185 /** |
| 186 * A table mapping the id's of packages to the context in which the package is |
| 187 * analyzed. |
| 188 */ |
| 189 final Map<PackageDescription, AnalysisContext> contextMap = |
| 190 new HashMap<PackageDescription, AnalysisContext>(); |
| 191 |
| 192 /** |
| 193 * Initialize a newly created package manager. |
| 194 */ |
| 195 PackageManager(this.resourceProvider); |
| 196 |
| 197 /** |
| 198 * Return the context in which the package at the given [packagePath] should |
| 199 * be analyzed when the given [packages] object is used to resolve package |
| 200 * names, the given [resolver] will be used to resolve 'dart:' URI's, and the |
| 201 * given [options] will control the analysis. |
| 202 */ |
| 203 AnalysisContext getContext(String packagePath, Packages packages, |
| 204 DartUriResolver resolver, AnalysisOptions options) { |
| 205 DartSdk sdk = resolver.dartSdk; |
| 206 Map<String, List<Folder>> packageMap = |
| 207 new ContextBuilder(resourceProvider, null, null) |
| 208 .convertPackagesToMap(packages); |
| 209 PackageDescription description = |
| 210 new PackageDescription(_buildId(packagePath, packageMap), sdk, options); |
| 211 return contextMap.putIfAbsent(description, () { |
| 212 AnalysisContext context = AnalysisEngine.instance.createAnalysisContext(); |
| 213 context.sourceFactory = new SourceFactory(<UriResolver>[ |
| 214 resolver, |
| 215 new PackageMapUriResolver(resourceProvider, packageMap), |
| 216 new ResourceUriResolver(resourceProvider) |
| 217 ], packages, resourceProvider); |
| 218 context.analysisOptions = options; |
| 219 return context; |
| 220 }); |
| 221 } |
| 222 |
| 223 /** |
| 224 * Return the id associated with the package at the given [packagePath] when |
| 225 * the given [packageMap] is used to resolve package names. |
| 226 */ |
| 227 String _buildId(String packagePath, Map<String, List<Folder>> packageMap) { |
| 228 DependencyFinder finder = new DependencyFinder(resourceProvider); |
| 229 List<String> dependencies = |
| 230 finder.transitiveDependenciesFor(packageMap, packagePath); |
| 231 StringBuffer buffer = new StringBuffer(); |
| 232 buffer.write(packagePath); |
| 233 for (String dependency in dependencies) { |
| 234 buffer.write(';'); |
| 235 buffer.write(dependency); |
| 236 } |
| 237 return buffer.toString(); |
| 238 } |
| 239 } |
| OLD | NEW |