From 36112bd34d2e9dad9dae5b59f1ce8a570e656858 Mon Sep 17 00:00:00 2001 From: Alexander Yevsyukov Date: Tue, 4 Aug 2026 14:01:36 +0100 Subject: [PATCH 1/2] Do not cache `generateLicenseReport` tasks Gradle Doctor reports every per-project `generateLicenseReport` task as "slower from cache": the task only re-renders already resolved dependency metadata into a small Markdown file, so recomputing it costs less than hashing inputs, looking the entry up, and unpacking it. Opt the task out of the build cache via `outputs.doNotCacheIf()`. Up-to-date checks are unaffected, so an unchanged project still skips the task. Co-Authored-By: Claude Opus 5 --- .../gradle/report/license/LicenseReporter.kt | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt index aa7e65f4..75ffa87b 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt @@ -33,6 +33,8 @@ import io.spine.dependency.local.Spine import io.spine.gradle.SpineTaskGroup import io.spine.gradle.applyPlugin import io.spine.gradle.getTask +import io.spine.gradle.report.license.Paths.outputFilename +import io.spine.gradle.report.license.Paths.relativePath import java.io.File import org.gradle.api.Project import org.gradle.api.Task @@ -74,6 +76,19 @@ object LicenseReporter { */ private const val mergeTaskName = "mergeAllLicenseReports" + /** + * The reason for opting [projectTaskName] out of the build cache. + * + * The task only re-renders already resolved dependency metadata into a small + * Markdown file, so recomputing it is cheaper than a cache round trip. + * Gradle Doctor consistently reports these tasks as + * [slower from cache](https://runningcode.github.io/gradle-doctor/slower-from-cache/). + * + * Up-to-date checks still apply, so an unchanged project does not re-run the task. + */ + private const val cacheOptOutReason = + "Rendering the license report is faster than fetching and unpacking a cache entry." + /** * Enables the generation of the license report for a single Gradle project. * @@ -82,7 +97,7 @@ object LicenseReporter { */ fun generateReportIn(project: Project) { project.applyPlugin(LicenseReportPlugin::class.java) - val reportOutputDir = project.layout.buildDirectory.dir(Paths.relativePath).get().asFile + val reportOutputDir = project.layout.buildDirectory.dir(relativePath).get().asFile with(project.the()) { outputDir = reportOutputDir.absolutePath @@ -94,21 +109,22 @@ object LicenseReporter { ) configurations = ALL - renderers = arrayOf(MarkdownReportRenderer(Paths.outputFilename)) + renderers = arrayOf(MarkdownReportRenderer(outputFilename)) } // The rendered report embeds the project's Maven coordinates — including its // version — in the report header (see `Template.writeHeader`). The - // `generateLicenseReport` task is a `@CacheableTask` that keys its up-to-date check - // and build-cache entry on the resolved dependencies only, not on the project version. - // Without the version as an explicit input, a version-only change leaves the task - // `UP-TO-DATE` (or restorable from the build cache), so the report keeps the previous - // version while `pom.xml`, produced by an always-running task, is updated. Declaring - // the version as an input invalidates the cached output when it changes, so the report - // is regenerated. The value is read lazily so it reflects the version resolved at - // execution time, regardless of when `project.version` is assigned during configuration. + // `generateLicenseReport` task keys its up-to-date check on the resolved + // dependencies only, not on the project version. Without the version as an explicit + // input, a version-only change leaves the task `UP-TO-DATE`, so the report keeps the + // previous version while `pom.xml`, produced by an always-running task, is updated. + // Declaring the version as an input invalidates the output when it changes, so the + // report is regenerated. The value is read lazily so it reflects the version resolved + // at execution time, regardless of when `project.version` is assigned + // during configuration. project.tasks.generateLicenseReport.configure { inputs.property("projectVersion", project.provider { project.version.toString() }) + outputs.doNotCacheIf(cacheOptOutReason) { true } } } @@ -171,7 +187,7 @@ object LicenseReporter { val paths = sourceProjects .map { val buildDir = it.layout.buildDirectory.asFile.get() - "$buildDir/${Paths.relativePath}/${Paths.outputFilename}" + "$buildDir/$relativePath/$outputFilename" }.filter { val exists = File(it).exists() if (!exists) { @@ -181,7 +197,7 @@ object LicenseReporter { } println("Merging the license reports from all projects.") val mergedContent = paths.joinToString("\n\n\n") { (File(it)).readText() } - val output = Paths.outputFile(rootProject.rootDir, Paths.outputFilename) + val output = Paths.outputFile(rootProject.rootDir, outputFilename) output.parentFile.mkdirs() output.writeText(mergedContent) } From c3793838126b1f1cf9b25e0919d91699cdc3384b Mon Sep 17 00:00:00 2001 From: Alexander Yevsyukov Date: Tue, 4 Aug 2026 14:12:11 +0100 Subject: [PATCH 2/2] Fix the antecedent of `cacheOptOutReason` KDoc The paragraph establishes a singular subject ("The task"), so "these tasks" had no plural antecedent. "Such tasks" refers to the kind of task instead. Co-Authored-By: Claude Opus 5 --- .../kotlin/io/spine/gradle/report/license/LicenseReporter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt index 75ffa87b..7908b10b 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt @@ -81,7 +81,7 @@ object LicenseReporter { * * The task only re-renders already resolved dependency metadata into a small * Markdown file, so recomputing it is cheaper than a cache round trip. - * Gradle Doctor consistently reports these tasks as + * Gradle Doctor consistently reports such tasks as * [slower from cache](https://runningcode.github.io/gradle-doctor/slower-from-cache/). * * Up-to-date checks still apply, so an unchanged project does not re-run the task.