@@ -43,7 +43,6 @@ abstract class PackageBuilder {
43
43
/// A package builder that understands pub package format.
44
44
class PubPackageBuilder implements PackageBuilder {
45
45
final DartdocOptionContext config;
46
- final Set <String > _knownFiles = {};
47
46
final PackageMetaProvider packageMetaProvider;
48
47
final PackageConfigProvider packageConfigProvider;
49
48
@@ -86,6 +85,7 @@ class PubPackageBuilder implements PackageBuilder {
86
85
await getLibraries (newGraph);
87
86
runtimeStats.endPerfTask ();
88
87
88
+ logDebug ('${DateTime .now ()}: Initializing package graph...' );
89
89
runtimeStats.startPerfTask ('initializePackageGraph' );
90
90
await newGraph.initializePackageGraph ();
91
91
runtimeStats.endPerfTask ();
@@ -177,27 +177,6 @@ class PubPackageBuilder implements PackageBuilder {
177
177
for (var filename in files) packageMetaProvider.fromFilename (filename)! ,
178
178
};
179
179
180
- /// Adds [element] 's path and all of its part files' paths to [_knownFiles] ,
181
- /// and recursively adds the paths of all imported and exported libraries.
182
- void _addKnownFiles (LibraryElement ? element) {
183
- if (element != null ) {
184
- var path = element.source.fullName;
185
- if (_knownFiles.add (path)) {
186
- for (var import in element.libraryImports) {
187
- _addKnownFiles (import.importedLibrary);
188
- }
189
- for (var export in element.libraryExports) {
190
- _addKnownFiles (export.exportedLibrary);
191
- }
192
- for (var part in element.parts
193
- .map ((e) => e.uri)
194
- .whereType <DirectiveUriWithUnit >()) {
195
- _knownFiles.add (part.source.fullName);
196
- }
197
- }
198
- }
199
- }
200
-
201
180
/// Whether to skip unreachable libraries when gathering all of the libraries
202
181
/// for the package graph.
203
182
///
@@ -212,29 +191,47 @@ class PubPackageBuilder implements PackageBuilder {
212
191
/// This set is used to prevent resolving set files more than once.
213
192
final _knownParts = < String > {};
214
193
215
- /// Parses libraries with the analyzer and invokes [addLibrary] with each
216
- /// result.
194
+ /// Discovers and resolves libraries, invoking [addLibrary] with each result.
217
195
///
218
- /// Uses [processedLibraries] to prevent calling the callback more than once
196
+ /// Uses [processedLibraries] to prevent calling [addLibrary] more than once
219
197
/// with the same [LibraryElement] . Adds each [LibraryElement] found to
220
198
/// [processedLibraries] .
221
- Future <void > _parseLibraries (
199
+ ///
200
+ /// [addingSpecials] indicates that only [SpecialClass] es are being resolved
201
+ /// in this round.
202
+ Future <void > _discoverLibraries (
222
203
void Function (DartDocResolvedLibrary ) addLibrary,
223
204
Set <LibraryElement > processedLibraries,
224
205
Set <String > files, {
225
- bool Function ( LibraryElement ) ? isLibraryIncluded ,
206
+ bool addingSpecials = false ,
226
207
}) async {
227
208
files = {...files};
228
- isLibraryIncluded ?? = (_) => true ;
229
- var lastPass = < PackageMeta > {};
230
- var current = < PackageMeta > {};
209
+ // Discover Dart libraries in a loop. In each iteration of the loop, we take
210
+ // a set of files (starting with the ones passed into the function), resolve
211
+ // them, add them to the package graph via `addLibrary`, and then discover
212
+ // which additional files need to be processed in the next loop. This
213
+ // discovery depends on various options (TODO: which?), but the basic idea
214
+ // is to take a file we've just processed, and add all of the files which
215
+ // that file references via imports or exports, and add them to the set of
216
+ // files to be processed.
217
+ //
218
+ // This loop may execute a few times. We know to stop looping when we have
219
+ // added zero new files to process. This is tracked with `filesInLastPass`
220
+ // and `filesInCurrentPass`.
221
+ var filesInLastPass = < String > {};
222
+ var filesInCurrentPass = < String > {};
231
223
var processedFiles = < String > {};
224
+ // When the loop discovers new files in a new package, it does extra work to
225
+ // find all documentable files in that package, for the universal reference
226
+ // scope. This variable tracks which packages we've seen so far.
227
+ var knownPackages = < PackageMeta > {};
232
228
do {
233
- lastPass = current ;
234
-
235
- // Be careful here; not to accidentally stack up multiple
229
+ filesInLastPass = filesInCurrentPass ;
230
+ var newFiles = < String > {};
231
+ // Be careful here, not to accidentally stack up multiple
236
232
// [DartDocResolvedLibrary]s, as those eat our heap.
237
233
var libraryFiles = files.difference (_knownParts);
234
+
238
235
for (var file in libraryFiles) {
239
236
if (processedFiles.contains (file)) {
240
237
continue ;
@@ -246,54 +243,72 @@ class PubPackageBuilder implements PackageBuilder {
246
243
_knownParts.add (file);
247
244
continue ;
248
245
}
249
- _addKnownFiles (resolvedLibrary.element);
250
- if (! processedLibraries.contains (resolvedLibrary.element) &&
251
- isLibraryIncluded (resolvedLibrary.element)) {
246
+ newFiles.addFilesReferencedBy (resolvedLibrary.element);
247
+ if (processedLibraries.contains (resolvedLibrary.element)) {
248
+ continue ;
249
+ }
250
+ if (addingSpecials || _shouldIncludeLibrary (resolvedLibrary.element)) {
252
251
addLibrary (resolvedLibrary);
253
252
processedLibraries.add (resolvedLibrary.element);
254
253
}
255
254
}
255
+ files.addAll (newFiles);
256
+ if (! addingSpecials) {
257
+ files.addAll (_includedExternalsFrom (newFiles));
258
+ }
256
259
257
- files.addAll (_knownFiles);
258
- files.addAll (_includeExternalsFrom (_knownFiles));
259
-
260
- current = _packageMetasForFiles (files.difference (_knownParts));
261
- // To get canonicalization correct for non-locally documented packages
262
- // (so we can generate the right hyperlinks), it's vital that we add all
263
- // libraries in dependent packages. So if the analyzer discovers some
264
- // files in a package we haven't seen yet, add files for that package.
265
- for (var meta in current.difference (lastPass)) {
266
- if (meta.isSdk) {
267
- if (! _skipUnreachableSdkLibraries) {
268
- files.addAll (_sdkFilesToDocument);
260
+ var packages = _packageMetasForFiles (files.difference (_knownParts));
261
+ filesInCurrentPass = {...files.difference (_knownParts)};
262
+
263
+ if (! addingSpecials) {
264
+ // To get canonicalization correct for non-locally documented packages
265
+ // (so we can generate the right hyperlinks), it's vital that we add all
266
+ // libraries in dependent packages. So if the analyzer discovers some
267
+ // files in a package we haven't seen yet, add files for that package.
268
+ for (var meta in packages.difference (knownPackages)) {
269
+ if (meta.isSdk) {
270
+ if (! _skipUnreachableSdkLibraries) {
271
+ files.addAll (_sdkFilesToDocument);
272
+ }
273
+ } else {
274
+ files.addAll (await _findFilesToDocumentInPackage (
275
+ meta.dir.path,
276
+ includeDependencies: false ,
277
+ filterExcludes: false ,
278
+ ).toList ());
269
279
}
270
- } else {
271
- files.addAll (await _findFilesToDocumentInPackage (meta.dir.path,
272
- includeDependencies: false , filterExcludes: false )
273
- .toList ());
274
280
}
281
+ knownPackages.addAll (packages);
275
282
}
276
- } while (! lastPass .containsAll (current ));
283
+ } while (! filesInLastPass .containsAll (filesInCurrentPass ));
277
284
}
278
285
286
+ /// Whether [libraryElement] should be included in the libraries-to-document.
287
+ bool _shouldIncludeLibrary (LibraryElement libraryElement) =>
288
+ config.include.isEmpty || config.include.contains (libraryElement.name);
289
+
279
290
/// Returns all top level library files in the 'lib/' directory of the given
280
291
/// package root directory.
281
292
///
282
293
/// If [includeDependencies] , then all top level library files in the 'lib/'
283
294
/// directory of every package in [basePackageDir] 's package config are also
284
295
/// included.
285
- Stream <String > _findFilesToDocumentInPackage (String basePackageDir,
286
- {required bool includeDependencies, bool filterExcludes = true }) async * {
296
+ Stream <String > _findFilesToDocumentInPackage (
297
+ String basePackageDir, {
298
+ required bool includeDependencies,
299
+ required bool filterExcludes,
300
+ }) async * {
287
301
var packageDirs = {basePackageDir};
288
302
289
303
if (includeDependencies) {
290
304
var packageConfig = (await packageConfigProvider
291
305
.findPackageConfig (resourceProvider.getFolder (basePackageDir)))! ;
292
306
for (var package in packageConfig.packages) {
293
- if (! filterExcludes || ! config.exclude.contains (package.name)) {
294
- packageDirs.add (_pathContext.dirname (_pathContext
295
- .fromUri (packageConfig[package.name]! .packageUriRoot)));
307
+ if (filterExcludes && config.exclude.contains (package.name)) {
308
+ continue ;
296
309
}
310
+ packageDirs.add (_pathContext.dirname (
311
+ _pathContext.fromUri (packageConfig[package.name]! .packageUriRoot)));
297
312
}
298
313
}
299
314
@@ -368,7 +383,7 @@ class PubPackageBuilder implements PackageBuilder {
368
383
/// Assumes each file might be part of a [DartdocOptionContext] , and loads
369
384
/// those objects to find any [DartdocOptionContext.includeExternal]
370
385
/// configurations therein.
371
- List <String > _includeExternalsFrom (Iterable <String > files) => [
386
+ List <String > _includedExternalsFrom (Iterable <String > files) => [
372
387
for (var file in files)
373
388
...DartdocOptionContext .fromContext (
374
389
config,
@@ -380,10 +395,12 @@ class PubPackageBuilder implements PackageBuilder {
380
395
Future <Set <String >> _getFiles () async {
381
396
var files = config.topLevelPackageMeta.isSdk
382
397
? _sdkFilesToDocument
383
- : await _findFilesToDocumentInPackage (config.inputDir,
384
- includeDependencies: config.autoIncludeDependencies)
385
- .toList ();
386
- files = [...files, ..._includeExternalsFrom (files)];
398
+ : await _findFilesToDocumentInPackage (
399
+ config.inputDir,
400
+ includeDependencies: config.autoIncludeDependencies,
401
+ filterExcludes: true ,
402
+ ).toList ();
403
+ files = [...files, ..._includedExternalsFrom (files)];
387
404
return {
388
405
...files.map ((s) => resourceProvider.pathContext
389
406
.absolute (resourceProvider.getFile (s).path)),
@@ -417,15 +434,25 @@ class PubPackageBuilder implements PackageBuilder {
417
434
var files = await _getFiles ();
418
435
var specialFiles = specialLibraryFiles (findSpecialsSdk);
419
436
437
+ logDebug ('${DateTime .now ()}: Discovering Dart libraries...' );
420
438
var foundLibraries = < LibraryElement > {};
421
- await _parseLibraries (
439
+ await _discoverLibraries (
422
440
uninitializedPackageGraph.addLibraryToGraph,
423
441
foundLibraries,
424
442
files,
425
- isLibraryIncluded: (LibraryElement libraryElement) =>
426
- config.include.isEmpty ||
427
- config.include.contains (libraryElement.name),
428
443
);
444
+ _checkForMissingIncludedFiles (foundLibraries);
445
+ await _discoverLibraries (
446
+ uninitializedPackageGraph.addSpecialLibraryToGraph,
447
+ foundLibraries,
448
+ specialFiles.difference (files),
449
+ addingSpecials: true ,
450
+ );
451
+ }
452
+
453
+ /// Throws an exception if any configured-to-be-included files were not found
454
+ /// while gathering libraries.
455
+ void _checkForMissingIncludedFiles (Set <LibraryElement > foundLibraries) {
429
456
if (config.include.isNotEmpty) {
430
457
var knownLibraryNames = foundLibraries.map ((l) => l.name);
431
458
var notFound = config.include
@@ -436,9 +463,6 @@ class PubPackageBuilder implements PackageBuilder {
436
463
'known libraries: [${knownLibraryNames .join (', ' )}]' );
437
464
}
438
465
}
439
- // Include directive does not apply to special libraries.
440
- await _parseLibraries (uninitializedPackageGraph.addSpecialLibraryToGraph,
441
- foundLibraries, specialFiles.difference (files));
442
466
}
443
467
444
468
p.Context get _pathContext => resourceProvider.pathContext;
@@ -472,3 +496,26 @@ class DartDocResolvedLibrary {
472
496
: element = result.element,
473
497
units = result.units.map ((unit) => unit.unit).toList ();
474
498
}
499
+
500
+ extension on Set <String > {
501
+ /// Adds [element] 's path and all of its part files' paths to `this` , and
502
+ /// recursively adds the paths of all imported and exported libraries.
503
+ void addFilesReferencedBy (LibraryElement ? element) {
504
+ if (element != null ) {
505
+ var path = element.source.fullName;
506
+ if (add (path)) {
507
+ for (var import in element.libraryImports) {
508
+ addFilesReferencedBy (import.importedLibrary);
509
+ }
510
+ for (var export in element.libraryExports) {
511
+ addFilesReferencedBy (export.exportedLibrary);
512
+ }
513
+ for (var part in element.parts
514
+ .map ((e) => e.uri)
515
+ .whereType <DirectiveUriWithUnit >()) {
516
+ add (part.source.fullName);
517
+ }
518
+ }
519
+ }
520
+ }
521
+ }
0 commit comments