@@ -43,7 +43,6 @@ abstract class PackageBuilder {
4343/// A package builder that understands pub package format.
4444class PubPackageBuilder implements PackageBuilder {
4545 final DartdocOptionContext config;
46- final Set <String > _knownFiles = {};
4746 final PackageMetaProvider packageMetaProvider;
4847 final PackageConfigProvider packageConfigProvider;
4948
@@ -86,6 +85,7 @@ class PubPackageBuilder implements PackageBuilder {
8685 await getLibraries (newGraph);
8786 runtimeStats.endPerfTask ();
8887
88+ logDebug ('${DateTime .now ()}: Initializing package graph...' );
8989 runtimeStats.startPerfTask ('initializePackageGraph' );
9090 await newGraph.initializePackageGraph ();
9191 runtimeStats.endPerfTask ();
@@ -177,27 +177,6 @@ class PubPackageBuilder implements PackageBuilder {
177177 for (var filename in files) packageMetaProvider.fromFilename (filename)! ,
178178 };
179179
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-
201180 /// Whether to skip unreachable libraries when gathering all of the libraries
202181 /// for the package graph.
203182 ///
@@ -212,29 +191,47 @@ class PubPackageBuilder implements PackageBuilder {
212191 /// This set is used to prevent resolving set files more than once.
213192 final _knownParts = < String > {};
214193
215- /// Parses libraries with the analyzer and invokes [addLibrary] with each
216- /// result.
194+ /// Discovers and resolves libraries, invoking [addLibrary] with each result.
217195 ///
218- /// Uses [processedLibraries] to prevent calling the callback more than once
196+ /// Uses [processedLibraries] to prevent calling [addLibrary] more than once
219197 /// with the same [LibraryElement] . Adds each [LibraryElement] found to
220198 /// [processedLibraries] .
221- Future <void > _parseLibraries (
199+ ///
200+ /// [addingSpecials] indicates that only [SpecialClass] es are being resolved
201+ /// in this round.
202+ Future <void > _discoverLibraries (
222203 void Function (DartDocResolvedLibrary ) addLibrary,
223204 Set <LibraryElement > processedLibraries,
224205 Set <String > files, {
225- bool Function ( LibraryElement ) ? isLibraryIncluded ,
206+ bool addingSpecials = false ,
226207 }) async {
227208 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 > {};
231223 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 > {};
232228 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
236232 // [DartDocResolvedLibrary]s, as those eat our heap.
237233 var libraryFiles = files.difference (_knownParts);
234+
238235 for (var file in libraryFiles) {
239236 if (processedFiles.contains (file)) {
240237 continue ;
@@ -246,54 +243,72 @@ class PubPackageBuilder implements PackageBuilder {
246243 _knownParts.add (file);
247244 continue ;
248245 }
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)) {
252251 addLibrary (resolvedLibrary);
253252 processedLibraries.add (resolvedLibrary.element);
254253 }
255254 }
255+ files.addAll (newFiles);
256+ if (! addingSpecials) {
257+ files.addAll (_includedExternalsFrom (newFiles));
258+ }
256259
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 ());
269279 }
270- } else {
271- files.addAll (await _findFilesToDocumentInPackage (meta.dir.path,
272- includeDependencies: false , filterExcludes: false )
273- .toList ());
274280 }
281+ knownPackages.addAll (packages);
275282 }
276- } while (! lastPass .containsAll (current ));
283+ } while (! filesInLastPass .containsAll (filesInCurrentPass ));
277284 }
278285
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+
279290 /// Returns all top level library files in the 'lib/' directory of the given
280291 /// package root directory.
281292 ///
282293 /// If [includeDependencies] , then all top level library files in the 'lib/'
283294 /// directory of every package in [basePackageDir] 's package config are also
284295 /// 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 * {
287301 var packageDirs = {basePackageDir};
288302
289303 if (includeDependencies) {
290304 var packageConfig = (await packageConfigProvider
291305 .findPackageConfig (resourceProvider.getFolder (basePackageDir)))! ;
292306 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 ;
296309 }
310+ packageDirs.add (_pathContext.dirname (
311+ _pathContext.fromUri (packageConfig[package.name]! .packageUriRoot)));
297312 }
298313 }
299314
@@ -368,7 +383,7 @@ class PubPackageBuilder implements PackageBuilder {
368383 /// Assumes each file might be part of a [DartdocOptionContext] , and loads
369384 /// those objects to find any [DartdocOptionContext.includeExternal]
370385 /// configurations therein.
371- List <String > _includeExternalsFrom (Iterable <String > files) => [
386+ List <String > _includedExternalsFrom (Iterable <String > files) => [
372387 for (var file in files)
373388 ...DartdocOptionContext .fromContext (
374389 config,
@@ -380,10 +395,12 @@ class PubPackageBuilder implements PackageBuilder {
380395 Future <Set <String >> _getFiles () async {
381396 var files = config.topLevelPackageMeta.isSdk
382397 ? _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)];
387404 return {
388405 ...files.map ((s) => resourceProvider.pathContext
389406 .absolute (resourceProvider.getFile (s).path)),
@@ -417,15 +434,25 @@ class PubPackageBuilder implements PackageBuilder {
417434 var files = await _getFiles ();
418435 var specialFiles = specialLibraryFiles (findSpecialsSdk);
419436
437+ logDebug ('${DateTime .now ()}: Discovering Dart libraries...' );
420438 var foundLibraries = < LibraryElement > {};
421- await _parseLibraries (
439+ await _discoverLibraries (
422440 uninitializedPackageGraph.addLibraryToGraph,
423441 foundLibraries,
424442 files,
425- isLibraryIncluded: (LibraryElement libraryElement) =>
426- config.include.isEmpty ||
427- config.include.contains (libraryElement.name),
428443 );
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) {
429456 if (config.include.isNotEmpty) {
430457 var knownLibraryNames = foundLibraries.map ((l) => l.name);
431458 var notFound = config.include
@@ -436,9 +463,6 @@ class PubPackageBuilder implements PackageBuilder {
436463 'known libraries: [${knownLibraryNames .join (', ' )}]' );
437464 }
438465 }
439- // Include directive does not apply to special libraries.
440- await _parseLibraries (uninitializedPackageGraph.addSpecialLibraryToGraph,
441- foundLibraries, specialFiles.difference (files));
442466 }
443467
444468 p.Context get _pathContext => resourceProvider.pathContext;
@@ -472,3 +496,26 @@ class DartDocResolvedLibrary {
472496 : element = result.element,
473497 units = result.units.map ((unit) => unit.unit).toList ();
474498}
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