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

Side by Side Diff: pkg/analyzer/lib/src/summary/bazel_summary.dart

Issue 2382473005: Implement package linking for Bazel. (Closed)
Patch Set: Created 4 years, 2 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
« no previous file with comments | « no previous file | pkg/analyzer/test/src/summary/bazel_summary_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 import 'dart:convert'; 5 import 'dart:convert';
6 6
7 import 'package:analyzer/file_system/file_system.dart'; 7 import 'package:analyzer/file_system/file_system.dart';
8 import 'package:analyzer/src/generated/engine.dart'; 8 import 'package:analyzer/src/generated/engine.dart';
9 import 'package:analyzer/src/generated/source.dart'; 9 import 'package:analyzer/src/generated/source.dart';
10 import 'package:analyzer/src/generated/utilities_collection.dart'; 10 import 'package:analyzer/src/generated/utilities_collection.dart';
11 import 'package:analyzer/src/summary/format.dart';
11 import 'package:analyzer/src/summary/idl.dart'; 12 import 'package:analyzer/src/summary/idl.dart';
13 import 'package:analyzer/src/summary/link.dart';
14 import 'package:analyzer/src/summary/package_bundle_reader.dart';
15 import 'package:analyzer/src/summary/summarize_elements.dart';
16 import 'package:analyzer/src/util/fast_uri.dart';
12 import 'package:convert/convert.dart'; 17 import 'package:convert/convert.dart';
13 import 'package:crypto/crypto.dart'; 18 import 'package:crypto/crypto.dart';
14 import 'package:meta/meta.dart'; 19 import 'package:meta/meta.dart';
15 20
16 /** 21 /**
17 * Return the [Folder] where bundles for the given [absoluteUri] should be 22 * Return the [Folder] where bundles for the given [absoluteUri] should be
18 * looked for. This folder should contain corresponding `*.full.ds` files, 23 * looked for. This folder should contain corresponding `*.full.ds` files,
19 * possibly more than one one. Return `null` if the given [absoluteUri] 24 * possibly more than one one. Return `null` if the given [absoluteUri]
20 * does not have the expected structure, so the output path cannot be computed. 25 * does not have the expected structure, so the output path cannot be computed.
21 */ 26 */
22 typedef Folder GetOutputFolder(Uri absoluteUri); 27 typedef Folder GetOutputFolder(Uri absoluteUri);
23 28
24 /** 29 /**
25 * Information about a Dart package in Bazel. 30 * Information about a Dart package in Bazel.
26 */ 31 */
27 class Package { 32 class Package {
28 final File unlinkedFile; 33 final File unlinkedFile;
29 final PackageBundle unlinked; 34 final PackageBundle unlinked;
30 final Set<String> _unitUris = new Set<String>(); 35 final Set<String> _unitUris = new Set<String>();
31 36
37 PackageBundle _linked;
38
32 Package(this.unlinkedFile, this.unlinked) { 39 Package(this.unlinkedFile, this.unlinked) {
33 _unitUris.addAll(unlinked.unlinkedUnitUris); 40 _unitUris.addAll(unlinked.unlinkedUnitUris);
34 } 41 }
42
43 PackageBundle get linked => _linked;
44
45 @override
46 String toString() => '$unlinkedFile';
35 } 47 }
36 48
37 /** 49 /**
38 * Class that reads summaries of Bazel packages. 50 * Class that reads summaries of Bazel packages.
39 * 51 *
40 * When the client needs to produce a resolution result for a new [Source], it 52 * When the client needs to produce a resolution result for a new [Source], it
41 * should call [getLinkedPackages] to check whether there is the set of 53 * should call [getLinkedPackages] to check whether there is the set of
42 * packages to resynthesize resolution results. 54 * packages to resynthesize resolution results.
43 */ 55 */
44 class SummaryProvider { 56 class SummaryProvider {
45 final ResourceProvider provider; 57 final ResourceProvider provider;
46 final GetOutputFolder getOutputFolder; 58 final GetOutputFolder getOutputFolder;
47 final AnalysisContext context; 59 final AnalysisContext context;
60 final PackageBundle sdkBundle;
48 61
49 /** 62 /**
50 * Mapping from bundle paths to corresponding [Package]s. The packages in 63 * Mapping from bundle paths to corresponding [Package]s. The packages in
51 * the map were consistent with their constituent sources at the moment when 64 * the map were consistent with their constituent sources at the moment when
52 * they were put into the map. 65 * they were put into the map.
53 */ 66 */
54 final Map<Folder, List<Package>> folderToPackagesMap = {}; 67 final Map<Folder, List<Package>> folderToPackagesMap = {};
55 68
56 SummaryProvider(this.provider, this.getOutputFolder, this.context); 69 /**
70 * Mapping from [Uri]s to corresponding [_LinkNode]s.
71 */
72 final Map<Uri, _LinkNode> uriToNodeMap = {};
73
74 SummaryProvider(this.provider, this.getOutputFolder, AnalysisContext context)
75 : context = context,
76 sdkBundle = context.sourceFactory.dartSdk?.getLinkedBundle();
57 77
58 /** 78 /**
59 * Return the complete list of [Package]s that are required to provide all 79 * Return the complete list of [Package]s that are required to provide all
60 * resolution results for the given [source]. 80 * resolution results for the given [source].
61 * 81 *
62 * The same list of packages is returned for the same [Source], i.e. always 82 * The same list of packages is returned for the same [Source], i.e. always
63 * the full list, not a difference with a previous request. It is up to the 83 * the full list, not a difference with a previous request. It is up to the
64 * client to decide whether some of the returned packages should be excluded 84 * client to decide whether some of the returned packages should be excluded
65 * as already mixed into a resynthesizer. 85 * as already mixed into a resynthesizer.
66 * 86 *
67 * If the full set of packages cannot be produced, for example because some 87 * If the full set of packages cannot be produced, for example because some
68 * bundles are not built, or out of date, etc, then `null` is returned. 88 * bundles are not built, or out of date, etc, then `null` is returned.
69 */ 89 */
70 List<Package> getLinkedPackages(Source source) { 90 List<Package> getLinkedPackages(Source source) {
71 // TODO(scheglov) implement 91 // Find the node that contains the source.
72 return null; 92 _LinkNode node = _getLinkNodeForUri(source.uri);
93 if (node == null) {
94 return null;
95 }
96
97 // Compute all transitive dependencies.
98 node.computeTransitiveDependencies();
99 List<_LinkNode> nodes = node.transitiveDependencies.toList();
100 nodes.forEach((dependency) => dependency.computeTransitiveDependencies());
101
102 // Fail if any dependency cannot be resolved.
103 if (node.failed) {
104 return null;
105 }
106
107 _link(nodes);
108
109 // Create successfully linked packages.
110 return nodes
111 .map((node) => node.package)
112 .where((package) => package.linked != null)
113 .toList();
73 } 114 }
74 115
75 /** 116 /**
76 * Return the [Package] that contains information about the source with 117 * Return the [Package] that contains information about the source with
77 * the given [uri], or `null` if such package does not exist. 118 * the given [uri], or `null` if such package does not exist.
78 */ 119 */
79 @visibleForTesting 120 @visibleForTesting
80 Package getUnlinkedForUri(Uri uri) { 121 Package getUnlinkedForUri(Uri uri) {
81 Folder outputFolder = getOutputFolder(uri); 122 Folder outputFolder = getOutputFolder(uri);
82 if (outputFolder != null) { 123 if (outputFolder != null) {
(...skipping 13 matching lines...) Expand all
96 * given [source] in [context]. 137 * given [source] in [context].
97 */ 138 */
98 String _computeSourceHashHex(Source source) { 139 String _computeSourceHashHex(Source source) {
99 String text = context.getContents(source).data; 140 String text = context.getContents(source).data;
100 List<int> bytes = UTF8.encode(text); 141 List<int> bytes = UTF8.encode(text);
101 List<int> hashBytes = md5.convert(bytes).bytes; 142 List<int> hashBytes = md5.convert(bytes).bytes;
102 return hex.encode(hashBytes); 143 return hex.encode(hashBytes);
103 } 144 }
104 145
105 /** 146 /**
147 * Return the node for the given [uri], or `null` if there is no unlinked
148 * bundle that contains [uri].
149 */
150 _LinkNode _getLinkNodeForUri(Uri uri) {
151 return uriToNodeMap.putIfAbsent(uri, () {
152 Package package = getUnlinkedForUri(uri);
153 if (package == null) {
154 return null;
155 }
156 return new _LinkNode(this, package);
157 });
158 }
159
160 /**
106 * Return all consistent unlinked [Package]s in the given [folder]. Some of 161 * Return all consistent unlinked [Package]s in the given [folder]. Some of
107 * the returned packages might be already linked. 162 * the returned packages might be already linked.
108 */ 163 */
109 List<Package> _getUnlinkedPackages(Folder folder) { 164 List<Package> _getUnlinkedPackages(Folder folder) {
110 List<Package> packages = folderToPackagesMap[folder]; 165 List<Package> packages = folderToPackagesMap[folder];
111 if (packages == null) { 166 if (packages == null) {
112 packages = <Package>[]; 167 packages = <Package>[];
113 try { 168 try {
114 List<Resource> children = folder.getChildren(); 169 List<Resource> children = folder.getChildren();
115 for (Resource child in children) { 170 for (Resource child in children) {
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
147 } 202 }
148 // Compare sorted actual and bundle unit hashes. 203 // Compare sorted actual and bundle unit hashes.
149 List<String> bundleHashes = bundle.unlinkedUnitHashes.toList()..sort(); 204 List<String> bundleHashes = bundle.unlinkedUnitHashes.toList()..sort();
150 actualHashes.sort(); 205 actualHashes.sort();
151 return listsEqual(actualHashes, bundleHashes); 206 return listsEqual(actualHashes, bundleHashes);
152 } on FileSystemException {} 207 } on FileSystemException {}
153 return false; 208 return false;
154 } 209 }
155 210
156 /** 211 /**
212 * Link the given [nodes].
213 */
214 void _link(List<_LinkNode> nodes) {
215 // Fill the store with bundles.
216 // Append the linked SDK bundle.
217 // Append unlinked and (if read from a cache) linked package bundles.
218 SummaryDataStore store = new SummaryDataStore(const <String>[]);
219 store.addBundle(null, sdkBundle);
220 for (_LinkNode node in nodes) {
221 store.addBundle(null, node.package.unlinked);
222 if (node.package.linked != null) {
223 store.addBundle(null, node.package.linked);
224 }
225 }
226
227 // Prepare URIs to link.
228 Map<String, _LinkNode> uriToNode = <String, _LinkNode>{};
229 for (_LinkNode node in nodes) {
230 if (!node.isReady) {
231 for (String uri in node.package.unlinked.unlinkedUnitUris) {
232 uriToNode[uri] = node;
233 }
234 }
235 }
236 Set<String> libraryUris = uriToNode.keys.toSet();
237
238 // Perform linking.
239 Map<String, LinkedLibraryBuilder> linkedLibraries =
240 link(libraryUris, (String uri) {
241 return store.linkedMap[uri];
242 }, (String uri) {
243 return store.unlinkedMap[uri];
244 }, context.declaredVariables.get, context.analysisOptions.strongMode);
245
246 // Assemble newly linked bundles.
247 for (_LinkNode node in nodes) {
248 if (!node.isReady) {
249 PackageBundleAssembler assembler = new PackageBundleAssembler();
250 linkedLibraries.forEach((uri, linkedLibrary) {
251 if (identical(uriToNode[uri], node)) {
252 assembler.addLinkedLibrary(uri, linkedLibrary);
253 }
254 });
255 List<int> bytes = assembler.assemble().toBuffer();
256 node.package._linked = new PackageBundle.fromBuffer(bytes);
257 }
258 }
259 }
260
261 /**
157 * Read the unlinked [Package] from the given [file], or return `null` if the 262 * Read the unlinked [Package] from the given [file], or return `null` if the
158 * file does not exist, or it cannot be read, or is not consistent with the 263 * file does not exist, or it cannot be read, or is not consistent with the
159 * constituent sources on the file system. 264 * constituent sources on the file system.
160 */ 265 */
161 Package _readUnlinkedPackage(File file) { 266 Package _readUnlinkedPackage(File file) {
162 try { 267 try {
163 List<int> bytes = file.readAsBytesSync(); 268 List<int> bytes = file.readAsBytesSync();
164 PackageBundle bundle = new PackageBundle.fromBuffer(bytes); 269 PackageBundle bundle = new PackageBundle.fromBuffer(bytes);
165 // Check for consistency, and fail if it's not. 270 // Check for consistency, and fail if it's not.
166 if (!_isUnlinkedBundleConsistent(bundle)) { 271 if (!_isUnlinkedBundleConsistent(bundle)) {
167 return null; 272 return null;
168 } 273 }
169 // OK, use the bundle. 274 // OK, use the bundle.
170 return new Package(file, bundle); 275 return new Package(file, bundle);
171 } on FileSystemException {} 276 } on FileSystemException {}
172 return null; 277 return null;
173 } 278 }
174 } 279 }
280
281 /**
282 * Information about a single [Package].
283 */
284 class _LinkNode {
285 final SummaryProvider linker;
286 final Package package;
287
288 bool failed = false;
289 Set<_LinkNode> transitiveDependencies;
290
291 List<_LinkNode> _dependencies;
292
293 _LinkNode(this.linker, this.package);
294
295 /**
296 * Retrieve the dependencies of this node.
297 */
298 List<_LinkNode> get dependencies {
299 if (_dependencies == null) {
300 Set<_LinkNode> dependencies = new Set<_LinkNode>();
301
302 void appendDependency(String uriStr) {
303 Uri uri = FastUri.parse(uriStr);
304 if (!uri.hasScheme) {
305 // A relative path in this package, skip it.
306 } else if (uri.scheme == 'dart') {
307 // Dependency on the SDK is implicit and always added.
308 // The SDK linked bundle is precomputed before linking packages.
309 } else if (uri.scheme == 'package') {
310 _LinkNode packageNode = linker._getLinkNodeForUri(uri);
311 if (packageNode == null) {
312 failed = true;
313 }
314 if (packageNode != null) {
315 dependencies.add(packageNode);
316 }
317 } else {
318 failed = true;
319 }
320 }
321
322 for (UnlinkedUnit unit in package.unlinked.unlinkedUnits) {
323 for (UnlinkedImport import in unit.imports) {
324 if (!import.isImplicit) {
325 appendDependency(import.uri);
326 }
327 }
328 for (UnlinkedExportPublic export in unit.publicNamespace.exports) {
329 appendDependency(export.uri);
330 }
331 }
332
333 _dependencies = dependencies.toList();
334 }
335 return _dependencies;
336 }
337
338 /**
339 * Return `true` is the node is ready - has the linked bundle or failed (does
340 * not have all required dependencies).
341 */
342 bool get isReady => package.linked != null || failed;
343
344 /**
345 * Compute the set of existing transitive dependencies for this node.
346 * If any dependency cannot be resolved, then set [failed] to `true`.
347 * Only unlinked bundle is used, so this method can be called before linking.
348 */
349 void computeTransitiveDependencies() {
350 if (transitiveDependencies == null) {
351 transitiveDependencies = new Set<_LinkNode>();
352
353 void appendDependencies(_LinkNode node) {
354 if (transitiveDependencies.add(node)) {
355 node.dependencies.forEach(appendDependencies);
356 }
357 }
358
359 appendDependencies(this);
360 if (transitiveDependencies.any((node) => node.failed)) {
361 failed = true;
362 }
363 }
364 }
365
366 @override
367 String toString() => package.toString();
368 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analyzer/test/src/summary/bazel_summary_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698