diff --git a/CHANGELOG.md b/CHANGELOG.md index 142c4c014..a6b5094d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ directory used for the analysis (helps switching different analyzer SDKs). - `ToolEnvironment.create()` takes `pubHostedUrl` as `PackageAnalyzer.create()` was removed. +- `InspectOptions.totalTimeBudget` to allow the dynamic reduction of `dartdocTimeout`. **BREAKING CHANGES** diff --git a/lib/src/package_analyzer.dart b/lib/src/package_analyzer.dart index e26eab6b1..27b04895f 100644 --- a/lib/src/package_analyzer.dart +++ b/lib/src/package_analyzer.dart @@ -26,15 +26,33 @@ import 'tool/run_constrained.dart'; import 'utils.dart'; class InspectOptions { + /// The PUB_HOSTED_URL to use for the package download and dependency analysis. final String? pubHostedUrl; + + /// The output directory to copy the generated docs. When not specified, + /// the generated docs will be discarded. final String? dartdocOutputDir; - final Duration? dartdocTimeout; + + /// The total time budget allocated for the full analysis. `pana` may not be + /// able to finish the analysis within this time, but some parts will be + /// running with reduced timeouts in the attempt to complete the analysis + /// with partial results. + final Duration? totalTimeBudget; + + /// The timeout to use when running `dartdoc` on the package. + /// + /// When [totalTimeBudget] is also specified, the lower of + /// [dartdocTimeout] and the remaining budget will be used. + final Duration dartdocTimeout; + + /// The line length parameter to be used for dart format checks. final int? lineLength; InspectOptions({ this.pubHostedUrl, this.dartdocOutputDir, - this.dartdocTimeout, + this.totalTimeBudget, + this.dartdocTimeout = const Duration(minutes: 5), this.lineLength, }); } diff --git a/lib/src/package_context.dart b/lib/src/package_context.dart index 1bc9f0308..724d37529 100644 --- a/lib/src/package_context.dart +++ b/lib/src/package_context.dart @@ -92,6 +92,7 @@ class PackageContext { final String packageDir; final errors = []; final urlProblems = {}; + final _stopwatch = Stopwatch(); Pubspec? _pubspec; List? _codeProblems; @@ -99,11 +100,26 @@ class PackageContext { PackageContext({ required this.sharedContext, required this.packageDir, - }); + }) { + _stopwatch.start(); + } ToolEnvironment get toolEnvironment => sharedContext.toolEnvironment; InspectOptions get options => sharedContext.options; + /// Returns the remaining time budget, or a very small but positive duration + /// if we are already above the total budget. + /// + /// Returns `null` if the total budget was not specified. + Duration? _remainingTimeBudget() { + if (options.totalTimeBudget == null) { + return null; + } + final threshold = const Duration(seconds: 1); + final remaining = options.totalTimeBudget! - _stopwatch.elapsed; + return remaining > threshold ? remaining : threshold; + } + late final Version currentSdkVersion = Version.parse(toolEnvironment.runtimeInfo.sdkVersion); @@ -235,7 +251,11 @@ class PackageContext { return DartdocResult.skipped(); } if (await resolveDependencies()) { - final timeout = options.dartdocTimeout ?? const Duration(minutes: 5); + var timeout = options.dartdocTimeout; + final rtb = _remainingTimeBudget(); + if (rtb != null && rtb < timeout) { + timeout = rtb; + } await normalizeDartdocOptionsYaml(packageDir); try { final pr = await toolEnvironment.dartdoc(