Chromium Code Reviews| 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/file_system/file_system.dart'; | |
| 10 import 'package:analyzer/source/package_map_resolver.dart'; | |
| 11 import 'package:analyzer/src/context/builder.dart'; | |
| 12 import 'package:analyzer/src/generated/engine.dart'; | |
| 13 import 'package:analyzer/src/generated/java_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 [packageaPath] depends. The [packageMap] | |
|
scheglov
2016/08/11 18:48:34
[packagePath]
Brian Wilkerson
2016/08/11 20:58:02
done
| |
| 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); | |
|
scheglov
2016/08/11 18:48:35
Does it handle correctly circular dependencies?
Brian Wilkerson
2016/08/11 20:58:02
Yes. It adds each packageName to processedPackages
| |
| 68 List<String> dependencies = _dependenciesFor(packagePath); | |
| 69 for (String dependency in dependencies) { | |
| 70 process(dependency); | |
| 71 } | |
| 72 } | |
| 73 } | |
| 74 List<String> dependencies = _dependenciesFor(packagePath); | |
| 75 for (String dependency in dependencies) { | |
|
scheglov
2016/08/11 18:48:34
dependencies.forEach(process);
Brian Wilkerson
2016/08/11 20:58:02
done
| |
| 76 process(dependency); | |
| 77 } | |
| 78 List<String> transitiveDependencies = new List<String>.from(processedPaths); | |
|
scheglov
2016/08/11 18:48:35
processedPaths.toList() would be shorted IMHO.
Brian Wilkerson
2016/08/11 20:58:02
done
| |
| 79 transitiveDependencies.sort(); | |
| 80 return transitiveDependencies; | |
| 81 } | |
| 82 | |
| 83 /** | |
| 84 * Return a list of the names of the packages on which the package at the | |
| 85 * [packagePath] depends. | |
| 86 */ | |
| 87 List<String> _dependenciesFor(String packagePath) { | |
| 88 return dependencyMap.putIfAbsent(packagePath, () { | |
| 89 Set<String> dependencies = new HashSet<String>(); | |
| 90 YamlNode yamlNode = _readPubspec(packagePath); | |
| 91 if (yamlNode is YamlMap) { | |
| 92 _gatherDependencies(dependencies, yamlNode['dependencies']); | |
| 93 } | |
| 94 return new List<String>.from(dependencies); | |
|
scheglov
2016/08/11 18:48:35
Same here.
dependencies.toList() lets us to avoid
Brian Wilkerson
2016/08/11 20:58:02
done
| |
| 95 }); | |
| 96 } | |
| 97 | |
| 98 /** | |
| 99 * Add to the given set of [dependecies] all of the package names used as keys | |
| 100 * in the given [yamlMap]. | |
| 101 */ | |
| 102 void _gatherDependencies(HashSet<String> dependencies, YamlMap yamlMap) { | |
|
scheglov
2016/08/11 18:48:35
This function is used only once, probably could be
Brian Wilkerson
2016/08/11 20:58:02
It's messy to inline this because of limitations o
| |
| 103 if (yamlMap is Map) { | |
| 104 for (var key in yamlMap.keys) { | |
| 105 if (key is String) { | |
| 106 dependencies.add(key); | |
| 107 } | |
| 108 } | |
| 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 packageId; | |
|
scheglov
2016/08/11 18:48:35
Why not just "id"?
We have already told that it's
Brian Wilkerson
2016/08/11 20:58:02
Because I like to type :-)
done
| |
| 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 [packageId] that is being analyzed against the given [sdk] using | |
| 154 * the given [options]. | |
| 155 */ | |
| 156 PackageDescription(this.packageId, this.sdk, this.options); | |
| 157 | |
| 158 @override | |
| 159 int get hashCode { | |
| 160 int hashCode = options.encodeCrossContextOptions(); | |
| 161 hashCode = JenkinsSmiHash.combine(hashCode, packageId.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.packageId == packageId; | |
| 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); | |
|
scheglov
2016/08/11 18:48:35
It seems that this method should be a function or
Brian Wilkerson
2016/08/11 20:58:02
It makes use of both instance fields; how could it
| |
| 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 |