This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch GROOVY-12019
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 8fa4f511ac2b05e6a303cf8db3a8b17ce7de52ba
Author: Daniel Sun <[email protected]>
AuthorDate: Tue May 19 01:04:03 2026 +0900

    GROOVY-12019: Enable gradle configuration cache
---
 .../groovy/org.apache.groovy-asciidoctor.gradle    |  7 +++
 .../src/main/groovy/org.apache.groovy-base.gradle  |  9 +++-
 .../src/main/groovy/org.apache.groovy-core.gradle  | 30 +++++------
 .../groovy/org.apache.groovy-distribution.gradle   | 38 +++++++++-----
 .../groovy/org.apache.groovy-documented.gradle     |  8 +--
 .../main/groovy/org.apache.groovy-tested.gradle    | 48 +++++++++---------
 .../org/apache/groovy/gradle/JarJarTask.groovy     | 34 ++++++++++---
 .../groovy/gradle/PerformanceTestsExtension.groovy | 58 ++++++++++++++++++----
 .../groovy/gradle/SharedConfiguration.groovy       | 25 ++++++++--
 build.gradle                                       |  8 +--
 gradle.properties                                  |  8 +++
 subprojects/groovy-ant/build.gradle                |  9 +++-
 subprojects/groovy-binary/build.gradle             | 27 +++++-----
 subprojects/groovy-groovydoc/build.gradle          |  2 +-
 14 files changed, 218 insertions(+), 93 deletions(-)

diff --git a/build-logic/src/main/groovy/org.apache.groovy-asciidoctor.gradle 
b/build-logic/src/main/groovy/org.apache.groovy-asciidoctor.gradle
index f269a28fdf..3ac2b10a89 100644
--- a/build-logic/src/main/groovy/org.apache.groovy-asciidoctor.gradle
+++ b/build-logic/src/main/groovy/org.apache.groovy-asciidoctor.gradle
@@ -40,6 +40,13 @@ configurations {
 }
 
 tasks.withType(AbstractAsciidoctorTask).configureEach {
+    // asciidoctor-gradle-jvm 4.0.5 stores configuration objects 
(DefaultDependencyScopeConfiguration
+    // etc.) inside its extension, which are not serializable by the 
configuration cache.
+    // Remove this call once the plugin ships a CC-compatible release.
+    notCompatibleWithConfigurationCache(
+        'asciidoctor-gradle-jvm 4.0.5 is not configuration-cache compatible ' +
+        '(captures Gradle dependency-management state in its extension)'
+    )
     outputs.cacheIf { true }
     
usesService(ConcurrentExecutionControlBuildService.restrict(AbstractAsciidoctorTask,
 gradle))
     configurations 'asciidocExtensions'
diff --git a/build-logic/src/main/groovy/org.apache.groovy-base.gradle 
b/build-logic/src/main/groovy/org.apache.groovy-base.gradle
index b3135b82e3..8d00a80225 100644
--- a/build-logic/src/main/groovy/org.apache.groovy-base.gradle
+++ b/build-logic/src/main/groovy/org.apache.groovy-base.gradle
@@ -36,7 +36,6 @@ plugins {
     id 'org.apache.groovy-common'
     id 'org.apache.groovy-internal'
     id 'org.apache.groovy-tested'
-    id 'org.apache.groovy-asciidoctor'
 }
 
 /**
@@ -47,6 +46,13 @@ if (sharedConfiguration.hasCodeCoverage.get()) {
     pluginManager.apply(JacocoPlugin)
 }
 
+if (sharedConfiguration.isDocumentationBuild && 
layout.projectDirectory.dir('src/spec/doc').asFile.isDirectory()) {
+    // The latest available Asciidoctor Gradle plugins still emit a Gradle 9 
deprecation
+    // during apply, so only documentation builds enable them and suppress 
that noise.
+    gradle.startParameter.warningMode = 
org.gradle.api.logging.configuration.WarningMode.None
+    pluginManager.apply('org.apache.groovy-asciidoctor')
+}
+
 def groovyLibrary = project.extensions.create('groovyLibrary', 
GroovyLibraryExtension, sharedConfiguration, java)
 
 java {
@@ -184,6 +190,7 @@ tasks.withType(Jar).configureEach { jar ->
 
 tasks.register('jarjar', JarJarTask) {
     String projectName = project.name
+    osgiExtension = rootProject.extensions.getByName('osgi')
     from = jar.archiveFile
     repackagedLibraries.from 
configurations.runtimeClasspath.incoming.artifactView {
         componentFilter { component ->
diff --git a/build-logic/src/main/groovy/org.apache.groovy-core.gradle 
b/build-logic/src/main/groovy/org.apache.groovy-core.gradle
index e3726d6510..6e1b71299a 100644
--- a/build-logic/src/main/groovy/org.apache.groovy-core.gradle
+++ b/build-logic/src/main/groovy/org.apache.groovy-core.gradle
@@ -92,9 +92,9 @@ def bootstrapJar = tasks.register('bootstrapJar', Jar) {
 
 // The main Groovy compile tasks has a special setup because
 // it uses the "bootstrap compiler"
-tasks.withType(GroovyCompile).configureEach {
+tasks.withType(GroovyCompile).configureEach { groovyCompileTask ->
     groovyClasspath = files(bootstrapJar, groovyClasspath)
-    if (it.name == 'compileGroovy') {
+    if (groovyCompileTask.name == 'compileGroovy') {
         classpath = files(bootstrapJar, classpath)
     }
     options.incremental = true
@@ -112,6 +112,8 @@ interface CoreServices {
 }
 
 def execOperations = objects.newInstance(CoreServices).execOperations
+def bridgerClasspath = files(rootProject.configurations.tools)
+def classesToBridge = groovyCore.classesToBridge
 
 tasks.named('compileJava') {
     options.fork = true
@@ -120,9 +122,9 @@ tasks.named('compileJava') {
 
     doLast {
         execOperations.javaexec { spec ->
-            spec.classpath(rootProject.configurations.tools)
+            spec.classpath(bridgerClasspath)
             spec.mainClass = 'org.jboss.bridger.Bridger'
-            spec.args(groovyCore.classesToBridge.asList().collect { 
it.absolutePath })
+            spec.args(classesToBridge.asList().collect { it.absolutePath })
         }
     }
 }
@@ -159,16 +161,16 @@ def generateGrammarSourceTask = 
tasks.named("generateGrammarSource") {
 
     doLast {
         def parserFilePattern = 'Groovy*'
-        def outputPath = generateGrammarSource.outputDirectory.canonicalPath
-        def parserPackagePath = 
"${outputPath}/${PARSER_PACKAGE_NAME.replace('.', '/')}"
-        file(parserPackagePath).mkdirs()
-        copy {
-            from outputPath
-            into parserPackagePath
-            include parserFilePattern
-        }
-        delete fileTree(outputPath) {
-            include parserFilePattern
+        File outputPath = outputDirectory
+        File parserPackagePath = new File(outputPath, 
PARSER_PACKAGE_NAME.replace('.', '/'))
+        parserPackagePath.mkdirs()
+        outputPath.listFiles()?.findAll { it.name.startsWith('Groovy') }?.each 
{ file ->
+            java.nio.file.Files.copy(
+                    file.toPath(),
+                    new File(parserPackagePath, file.name).toPath(),
+                    java.nio.file.StandardCopyOption.REPLACE_EXISTING
+            )
+            file.delete()
         }
     }
 }
diff --git a/build-logic/src/main/groovy/org.apache.groovy-distribution.gradle 
b/build-logic/src/main/groovy/org.apache.groovy-distribution.gradle
index 46ad218108..4d46ba5897 100644
--- a/build-logic/src/main/groovy/org.apache.groovy-distribution.gradle
+++ b/build-logic/src/main/groovy/org.apache.groovy-distribution.gradle
@@ -26,10 +26,15 @@ plugins {
     id 'org.apache.groovy-common'
     id 'org.apache.groovy-aggregating-project'
     id 'org.apache.groovy-doc-aggregator'
-    id 'org.asciidoctor.jvm.pdf'
 }
 
 def distributionExtension = project.extensions.create('distribution', 
DistributionExtension, project)
+def documentationBuild = sharedConfiguration.isDocumentationBuild && 
layout.projectDirectory.dir('src/spec/doc').asFile.isDirectory()
+
+if (documentationBuild) {
+    pluginManager.apply('org.apache.groovy-asciidoctor')
+    pluginManager.apply('org.asciidoctor.jvm.pdf')
+}
 
 configurations {
     baseProjects {
@@ -133,6 +138,9 @@ def distBin = tasks.register('distBin', Zip) {
 
 def distSdk = tasks.register("distSdk", Zip) {
     def groovyBundleVersion = sharedConfiguration.groovyBundleVersion.get()
+    // Capture project.version at configuration time: accessing project at 
execution time
+    // is an "interrupting" CC problem in Gradle 9.x and causes a 
Gradle-internal exception.
+    def projectVersion = project.version.toString()
     description = 'Generates the binary, sources, documentation and full 
distributions'
     archiveBaseName = 'apache-groovy'
     duplicatesStrategy = DuplicatesStrategy.EXCLUDE
@@ -149,10 +157,9 @@ def distSdk = tasks.register("distSdk", Zip) {
         with distributionExtension.srcSpec
     }
     doFirst {
-        def av = project.version.toString()
-        if ((av.endsWith('SNAPSHOT') && 
!groovyBundleVersion.endsWith('SNAPSHOT'))
-                || (!av.endsWith('SNAPSHOT') && 
groovyBundleVersion.endsWith('SNAPSHOT'))) {
-            throw new GradleException("Incoherent versions. Found 
groovyVersion=$av and groovyBundleVersion=${versions.groovyBundle}")
+        if ((projectVersion.endsWith('SNAPSHOT') && 
!groovyBundleVersion.endsWith('SNAPSHOT'))
+                || (!projectVersion.endsWith('SNAPSHOT') && 
groovyBundleVersion.endsWith('SNAPSHOT'))) {
+            throw new GradleException("Incoherent versions. Found 
groovyVersion=$projectVersion and groovyBundleVersion=${versions.groovyBundle}")
         }
     }
 }
@@ -198,21 +205,28 @@ tasks.register("installGroovy", Sync) {
 }
 
 tasks.register("doc") {
-    dependsOn 'javadocAll', 'groovydocAll', 'docGDK', 'asciidocAll', 
'asciidoctorPdf'
+    dependsOn 'javadocAll', 'groovydocAll', 'docGDK'
+    if (documentationBuild) {
+        dependsOn 'asciidocAll', 'asciidoctorPdf'
+    }
 }
 
 tasks.register("asciidocAll", Copy) {
     from configurations.allAsciidoc
-    from tasks.named('asciidoctor')
+    if (documentationBuild) {
+        from tasks.named('asciidoctor')
+    }
     into layout.buildDirectory.dir("asciidocAll/html5")
     duplicatesStrategy = DuplicatesStrategy.EXCLUDE
 }
 
-tasks.named('asciidoctorPdf') {
-    baseDirFollowsSourceFile()
-    logDocuments = true
-    sourceDir = file('src/spec/doc')
-    outputDir = layout.buildDirectory.dir("asciidocAll/pdf")
+if (documentationBuild) {
+    tasks.named('asciidoctorPdf') {
+        baseDirFollowsSourceFile()
+        logDocuments = true
+        sourceDir = file('src/spec/doc')
+        outputDir = layout.buildDirectory.dir("asciidocAll/pdf")
+    }
 }
 
 // The Groovy distribution module isn't a Java library
diff --git a/build-logic/src/main/groovy/org.apache.groovy-documented.gradle 
b/build-logic/src/main/groovy/org.apache.groovy-documented.gradle
index 3b43cdde34..4cde3d6929 100644
--- a/build-logic/src/main/groovy/org.apache.groovy-documented.gradle
+++ b/build-logic/src/main/groovy/org.apache.groovy-documented.gradle
@@ -63,9 +63,11 @@ tasks.withType(Javadoc).configureEach {
         if (JavaVersion.current() >= JavaVersion.VERSION_21) {
             addStringOption('-link-modularity-mismatch', 'info')
         }
-        addStringOption('tag', 'apiNote:a:"API Note:"')
-        addStringOption('tag', 'implSpec:a:"Implementation Requirements:"')
-        addStringOption('tag', 'implNote:a:"Implementation Note:"')
+        tags(
+            'apiNote:a:"API Note:"',
+            'implSpec:a:"Implementation Requirements:"',
+            'implNote:a:"Implementation Note:"'
+        )
         windowTitle = "Groovy ${versions.groovy}"
         docTitle = "Groovy ${versions.groovy}"
         classpath += project.file('src/main/java') // to pick up package.html
diff --git a/build-logic/src/main/groovy/org.apache.groovy-tested.gradle 
b/build-logic/src/main/groovy/org.apache.groovy-tested.gradle
index afc0cf7a22..c76139adcc 100644
--- a/build-logic/src/main/groovy/org.apache.groovy-tested.gradle
+++ b/build-logic/src/main/groovy/org.apache.groovy-tested.gradle
@@ -57,9 +57,9 @@ def aggregator = TestResultAggregatorService.register(
 // so a developer's polluted ~/.groovy/grapes doesn't leak into tests.
 // Enable on CI with: ./gradlew test -Pgroovy.grape.bridge-cache=true
 def grapeBridgeCache = (findProperty('groovy.grape.bridge-cache') ?:
-        System.properties['groovy.grape.bridge-cache']) == 'true'
+        providers.systemProperty('groovy.grape.bridge-cache').orNull) == 'true'
 
-tasks.withType(Test).configureEach {
+tasks.withType(Test).configureEach { testTask ->
     def fs = objects.newInstance(TestServices).fileSystemOperations
     def grapeDirectory = new File(temporaryDir, '.groovy')
     def options = ['-ea', "-Xms${groovyJUnit_ms}", "-Xmx${groovyJUnit_mx}",
@@ -80,7 +80,7 @@ tasks.withType(Test).configureEach {
     systemProperty 'http.agent',
         "Apache-Maven/3.9.14 (Java ${System.getProperty('java.version')}; 
${System.getProperty('os.name')} ${System.getProperty('os.version')})"
     systemProperty 'groovy.force.illegal.access', 
findProperty('groovy.force.illegal.access')
-    def testdb = System.properties['groovy.testdb.props']
+    def testdb = providers.systemProperty('groovy.testdb.props').orNull
     if (testdb) {
         systemProperty 'groovy.testdb.props', testdb
     }
@@ -88,22 +88,21 @@ tasks.withType(Test).configureEach {
     // to the test JVM. Lets users override Grape behaviour — debug flags, 
future
     // settings — without adding one-off plumbing per flag. Project properties 
(-P)
     // win over system properties (-D) when both are set, matching Gradle 
convention.
+    // providers.systemPropertiesPrefixedBy() is CC-tracked (unlike 
System.properties),
+    // so changes to -D groovy.grape.* flags correctly invalidate the cache 
entry.
     def grapeProps = new LinkedHashMap<String, String>()
     project.properties.each { k, v ->
         if (k.toString().startsWith('groovy.grape.') && v != null) {
             grapeProps[k.toString()] = v.toString()
         }
     }
-    System.properties.each { k, v ->
-        if (k.toString().startsWith('groovy.grape.') && v != null) {
-            grapeProps.putIfAbsent(k.toString(), v.toString())
-        }
+    providers.systemPropertiesPrefixedBy('groovy.grape.').get().each { k, v ->
+        grapeProps.putIfAbsent(k, v)
     }
     grapeProps.each { key, value ->
         systemProperty key, value
     }
-    def headless = System.properties['java.awt.headless']
-    if (headless == 'true') {
+    if (providers.systemProperty('java.awt.headless').orNull == 'true') {
         systemProperty 'java.awt.headless', 'true'
     }
     systemProperty 'apple.awt.UIElement', 'true'
@@ -132,7 +131,7 @@ tasks.withType(Test).configureEach {
     scanForTestClasses = true
     ignoreFailures = false
     classpath = files('src/test/groovy') + classpath
-    exclude buildExcludeFilter(it.name == 'test')
+    exclude buildExcludeFilter(testTask.name == 'test')
     ext.resultText = ''
 
     testLogging {
@@ -144,6 +143,7 @@ tasks.withType(Test).configureEach {
     useJUnitPlatform()
     usesService(aggregator)
     usesService(ConcurrentExecutionControlBuildService.restrict(Test, gradle, 
2))
+    File projectDirectory = project.layout.projectDirectory.asFile
 
     doFirst {
         fs.delete {
@@ -164,7 +164,7 @@ tasks.withType(Test).configureEach {
                     into(new File(grapeDirectory, 'grapes'))
                     exclude '**/*.lck', '**/*.lastUpdated'
                 }
-                logger.lifecycle "Bridged ~/.groovy/grapes -> 
${grapeDirectory}/grapes"
+                testTask.logger.lifecycle "Bridged ~/.groovy/grapes -> 
${grapeDirectory}/grapes"
             }
             // Also bridge ~/.m2/repository so the test JVM's localm2 Ivy 
resolver
             // (configured as file:${user.home}/.m2/repository/) finds 
artifacts
@@ -181,10 +181,10 @@ tasks.withType(Test).configureEach {
                     into(m2Target)
                     exclude '**/*.lastUpdated', '**/_remote.repositories'
                 }
-                logger.lifecycle "Bridged ~/.m2/repository -> ${m2Target}"
+                testTask.logger.lifecycle "Bridged ~/.m2/repository -> 
${m2Target}"
             }
         }
-        logger.debug "Grape directory: ${grapeDirectory.absolutePath}"
+        testTask.logger.debug "Grape directory: ${grapeDirectory.absolutePath}"
     }
 
     doLast {
@@ -204,7 +204,7 @@ tasks.withType(Test).configureEach {
             }
         }
         fs.delete {
-            delete(files(".").filter { it.name.endsWith '.class' })
+            delete(projectDirectory.listFiles()?.findAll { 
it.name.endsWith('.class') } ?: [])
         }
     }
 
@@ -215,7 +215,7 @@ tasks.withType(Test).configureEach {
                 def green = '\u001B[32m'
                 def yellow = '\u001B[33m'
                 def reset = '\u001B[0m'
-                logger.lifecycle "${desc.name}: 
${green}${result.successfulTestCount} passed${reset}, " +
+                testTask.logger.lifecycle "${desc.name}: 
${green}${result.successfulTestCount} passed${reset}, " +
                     "${yellow}${result.skippedTestCount} skipped${reset} " +
                     "(${result.testCount} tests)"
             }
@@ -278,15 +278,15 @@ Closure buildExcludeFilter(boolean legacyTestSuite) {
 
 // Warn (rather than silently NO-SOURCE) when the groovy/grape exclusion
 // swallows the entirety of a project's test source set.
-gradle.taskGraph.whenReady { graph ->
-    def testTask = tasks.named('test').get()
-    if (!graph.hasTask(testTask)) return
-    if (providers.systemProperty('junit.network').getOrNull()) return
-    boolean hasGrapeTests = sourceSets.test.allSource.srcDirs.any {
-        new File(it, 'groovy/grape').isDirectory()
-    }
-    if (hasGrapeTests) {
-        logger.warn("WARNING: ${testTask.path} will skip groovy/grape/* tests; 
set -Djunit.network=true to include them")
+boolean hasGrapeTests = sourceSets.test.allSource.srcDirs.any {
+    new File(it, 'groovy/grape').isDirectory()
+}
+def junitNetwork = providers.systemProperty('junit.network')
+tasks.named('test', Test).configure { testTask ->
+    doFirst {
+        if (!junitNetwork.getOrNull() && hasGrapeTests) {
+            testTask.logger.warn("WARNING: ${testTask.path} will skip 
groovy/grape/* tests; set -Djunit.network=true to include them")
+        }
     }
 }
 
diff --git 
a/build-logic/src/main/groovy/org/apache/groovy/gradle/JarJarTask.groovy 
b/build-logic/src/main/groovy/org/apache/groovy/gradle/JarJarTask.groovy
index cc24c8873f..bae7e397f7 100644
--- a/build-logic/src/main/groovy/org/apache/groovy/gradle/JarJarTask.groovy
+++ b/build-logic/src/main/groovy/org/apache/groovy/gradle/JarJarTask.groovy
@@ -28,6 +28,7 @@ import org.gradle.api.file.ConfigurableFileCollection
 import org.gradle.api.file.FileSystemOperations
 import org.gradle.api.file.RegularFileProperty
 import org.gradle.api.java.archives.Manifest
+import org.gradle.api.model.ObjectFactory
 import org.gradle.api.tasks.CacheableTask
 import org.gradle.api.tasks.Classpath
 import org.gradle.api.tasks.Input
@@ -46,19 +47,21 @@ class JarJarTask extends DefaultTask {
 
     @InputFile
     @Classpath
-    final RegularFileProperty from = project.objects.fileProperty()
+    final RegularFileProperty from
 
     @InputFiles
     @Classpath
-    final ConfigurableFileCollection repackagedLibraries = 
project.objects.fileCollection()
+    final ConfigurableFileCollection repackagedLibraries
 
     @InputFiles
     @Classpath
-    final ConfigurableFileCollection jarjarToolClasspath = 
project.objects.fileCollection()
+    final ConfigurableFileCollection jarjarToolClasspath
 
-    final protected osgi = project.rootProject.extensions.osgi
+    @Internal
+    final String projectName
 
-    final protected String projectName = project.name
+    @Internal
+    Object osgiExtension
 
     @Input
     @Optional
@@ -84,7 +87,7 @@ class JarJarTask extends DefaultTask {
     Map<String, String> includedResources = [:]
 
     @OutputFile
-    final RegularFileProperty outputFile = project.objects.fileProperty()
+    final RegularFileProperty outputFile
 
     @Input
     boolean createManifest = true
@@ -94,8 +97,23 @@ class JarJarTask extends DefaultTask {
     private List<Action<? super Manifest>> manifestTweaks = []
 
     @Inject
-    JarJarTask(FileSystemOperations fileSystemOperations) {
+    JarJarTask(ObjectFactory objects, FileSystemOperations 
fileSystemOperations) {
+        // The OSGi BND extension and the manifestTweaks closures capture 
project-level
+        // Gradle domain objects (Project, ConfigurationContainer, 
SourceSetContainer, etc.)
+        // that cannot be serialized by the configuration cache.  Until the 
OSGi plugin is
+        // updated to a CC-compatible release, builds that include this task 
(dist, publish)
+        // run without the configuration cache, while compile/test builds are 
unaffected.
+        notCompatibleWithConfigurationCache(
+            'JarJarTask uses the OSGi BND Gradle extension, which captures 
project state ' +
+            'that is not configuration-cache serializable. Upgrade the OSGi 
plugin to a ' +
+            'CC-compatible release to enable the configuration cache for 
dist/publish tasks.'
+        )
         this.fs = fileSystemOperations
+        this.from = objects.fileProperty()
+        this.repackagedLibraries = objects.fileCollection()
+        this.jarjarToolClasspath = objects.fileCollection()
+        this.outputFile = objects.fileProperty()
+        this.projectName = project.name
     }
 
     void withManifest(Action<? super Manifest> action) {
@@ -164,7 +182,7 @@ class JarJarTask extends DefaultTask {
 
         // Step 3: generate an OSGi manifest referencing the repackaged classes
         if (createManifest) {
-            def mf = osgi.osgiManifest {
+            def mf = osgiExtension.osgiManifest {
                 symbolicName = this.projectName
                 instruction 'Import-Package', '*;resolution:=optional'
                 classesDir = tmpJar
diff --git 
a/build-logic/src/main/groovy/org/apache/groovy/gradle/PerformanceTestsExtension.groovy
 
b/build-logic/src/main/groovy/org/apache/groovy/gradle/PerformanceTestsExtension.groovy
index 35e950c399..883676156d 100644
--- 
a/build-logic/src/main/groovy/org/apache/groovy/gradle/PerformanceTestsExtension.groovy
+++ 
b/build-logic/src/main/groovy/org/apache/groovy/gradle/PerformanceTestsExtension.groovy
@@ -19,6 +19,8 @@
 package org.apache.groovy.gradle
 
 import groovy.transform.CompileStatic
+import org.gradle.process.CommandLineArgumentProvider
+import org.gradle.api.file.ConfigurableFileCollection
 import org.gradle.api.artifacts.Configuration
 import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.artifacts.dsl.DependencyHandler
@@ -28,7 +30,13 @@ import org.gradle.api.attributes.LibraryElements
 import org.gradle.api.attributes.Usage
 import org.gradle.api.file.FileCollection
 import org.gradle.api.file.ProjectLayout
+import org.gradle.api.file.RegularFileProperty
 import org.gradle.api.model.ObjectFactory
+import org.gradle.api.tasks.Classpath
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.PathSensitive
+import org.gradle.api.tasks.PathSensitivity
 import org.gradle.api.tasks.JavaExec
 import org.gradle.api.tasks.SourceSetContainer
 import org.gradle.api.tasks.TaskContainer
@@ -43,7 +51,7 @@ class PerformanceTestsExtension {
     private final DependencyHandler dependencies
     private final SourceSetContainer sourceSets
     private final ProjectLayout layout
-    private final List<File> testFiles = []
+    private final ConfigurableFileCollection testFiles
 
     @Inject
     PerformanceTestsExtension(ObjectFactory objects,
@@ -59,10 +67,11 @@ class PerformanceTestsExtension {
         this.dependencies = dependencies
         this.sourceSets = sourceSets
         this.layout = layout
+        this.testFiles = objects.fileCollection()
     }
 
     void testFiles(FileCollection files) {
-        testFiles.addAll(files.asList())
+        testFiles.from(files)
     }
 
     void versions(String... versions) {
@@ -106,21 +115,52 @@ class PerformanceTestsExtension {
             ].each { conf.dependencies.add(dependencies.create(it)) }
         }
         def outputFile = 
layout.buildDirectory.file("compilation-stats-${version}.csv")
+        def compilationClasspath = objects.fileCollection().from(groovyConf)
+        def performanceArguments = 
objects.newInstance(CompilerPerformanceTestArguments)
+        performanceArguments.outputFile.set(outputFile)
+        performanceArguments.compilationClasspath.from(compilationClasspath)
+        performanceArguments.testFiles.from(testFiles)
         def perfTest = tasks.register("performanceTestGroovy${version}", 
JavaExec) { je ->
             je.group = "Performance tests"
             je.mainClass.set('org.apache.groovy.perf.CompilerPerformanceTest')
             je.classpath(groovyConf, sourceSets.getByName('test').output)
             je.jvmArgs = ['-Xms512m', '-Xmx512m']
             je.outputs.file(outputFile)
-            je.doFirst {
-                def args = [outputFile.get().toString(), "-cp", 
groovyConf.asPath]
-                args.addAll(testFiles.collect { it.toString() })
-                je.setArgs(args)
-                println je.args.asList()
-            }
+            je.argumentProviders.add(performanceArguments)
         }
         tasks.named("performanceTests", PerformanceTestSummary) { pts ->
-            pts.csvFiles.from(perfTest)
+            pts.dependsOn(perfTest)
+            pts.csvFiles.from(outputFile)
+        }
+    }
+
+    @CompileStatic
+    static class CompilerPerformanceTestArguments implements 
CommandLineArgumentProvider {
+        @Internal
+        final RegularFileProperty outputFile
+
+        @Classpath
+        final ConfigurableFileCollection compilationClasspath
+
+        @InputFiles
+        @PathSensitive(PathSensitivity.RELATIVE)
+        final ConfigurableFileCollection testFiles
+
+        @Inject
+        CompilerPerformanceTestArguments(ObjectFactory objects) {
+            outputFile = objects.fileProperty()
+            compilationClasspath = objects.fileCollection()
+            testFiles = objects.fileCollection()
+        }
+
+        @Override
+        Iterable<String> asArguments() {
+            List<String> args = new ArrayList<>(testFiles.files.size() + 3)
+            args.add(outputFile.get().asFile.absolutePath)
+            args.add('-cp')
+            args.add(compilationClasspath.asPath)
+            args.addAll(testFiles.files.collect { File file -> 
file.absolutePath })
+            args
         }
     }
 }
diff --git 
a/build-logic/src/main/groovy/org/apache/groovy/gradle/SharedConfiguration.groovy
 
b/build-logic/src/main/groovy/org/apache/groovy/gradle/SharedConfiguration.groovy
index 81e6aca9fc..f39ffe9f05 100644
--- 
a/build-logic/src/main/groovy/org/apache/groovy/gradle/SharedConfiguration.groovy
+++ 
b/build-logic/src/main/groovy/org/apache/groovy/gradle/SharedConfiguration.groovy
@@ -45,6 +45,7 @@ class SharedConfiguration {
     final Provider<Boolean> hasCodeCoverage
     final Provider<String> targetJavaVersion
     final Provider<String> groovyTargetBytecodeVersion
+    final boolean isDocumentationBuild
     final boolean isRunningOnCI
 
     @Nested
@@ -73,14 +74,19 @@ class SharedConfiguration {
         artifactory = new Artifactory(layout, providers, logger)
         signing = new Signing(this, objects, providers)
         binaryCompatibilityBaselineVersion = 
providers.gradleProperty("binaryCompatibilityBaseline")
+        // Evaluate eagerly at construction time (startParameter is available 
here) so that
+        // no lazy provider captures a StartParameter reference, which Gradle 
would otherwise
+        // need to serialize for the configuration cache.
+        boolean hasJacocoTask = startParameter.taskNames.any { String name -> 
name.contains('jacoco') }
         hasCodeCoverage = providers.gradleProperty("coverage")
                 .map { Boolean.valueOf(it) }
-                .orElse(
-                        providers.provider { startParameter.taskNames.any { it 
=~ /jacoco/ } }
-                )
+                .orElse(hasJacocoTask)
                 .orElse(false)
         targetJavaVersion = providers.gradleProperty("targetJavaVersion")
         groovyTargetBytecodeVersion = 
providers.gradleProperty("groovyTargetBytecodeVersion")
+        isDocumentationBuild = startParameter.taskNames.any { String taskName 
->
+            isDocumentationTask(taskName)
+        }
         File javaHome = new File(providers.systemProperty('java.home').get())
         String javaVersion = providers.systemProperty('java.version').get()
         String userdir = providers.systemProperty('user.dir').get()
@@ -95,6 +101,19 @@ class SharedConfiguration {
         isCi
     }
 
+    private static boolean isDocumentationTask(String taskName) {
+        String normalized = taskName.toLowerCase(Locale.ROOT)
+        normalized.contains('asciidoc') ||
+                normalized.contains('asciidoctor') ||
+                normalized.contains('javadoc') ||
+                normalized.contains('groovydoc') ||
+                normalized.endsWith('doc') ||
+                normalized.endsWith(':doc') ||
+                normalized.contains('docgdk') ||
+                normalized.endsWith('dist') ||
+                normalized.endsWith(':dist')
+    }
+
     static class Artifactory {
         final Provider<String> username
         final Provider<String> password
diff --git a/build.gradle b/build.gradle
index b54602173e..ad1626af24 100644
--- a/build.gradle
+++ b/build.gradle
@@ -256,10 +256,10 @@ licenseReport {
     ]
 }
 
-gradle.taskGraph.whenReady { graph ->
-    if (graph.hasTask(':generateLicenseReport') && 
gradle.startParameter.parallelProjectExecutionEnabled) {
-        throw new GradleException('generateLicenseReport is not compatible 
with parallel builds (Gradle 9+ issue). Please re-run with: ./gradlew 
generateLicenseReport --no-parallel')
-    }
+if (gradle.startParameter.parallelProjectExecutionEnabled && 
gradle.startParameter.taskNames.any {
+    it == 'generateLicenseReport' || it.endsWith(':generateLicenseReport')
+}) {
+    throw new GradleException('generateLicenseReport is not compatible with 
parallel builds (Gradle 9+ issue). Please re-run with: ./gradlew 
generateLicenseReport --no-parallel')
 }
 
 sonarqube {
diff --git a/gradle.properties b/gradle.properties
index 81f71ccbef..2ff6b943fb 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -41,6 +41,14 @@ org.gradle.jvmargs=-Xms1200m -Xmx2g 
-XX:MaxMetaspaceSize=1500m -XX:+UseG1GC
 # enable the Gradle build cache
 org.gradle.caching=true
 
+# enable the Gradle configuration cache
+org.gradle.configuration-cache=true
+# treat CC problems as warnings so that dist/publish builds (which include 
third-party
+# plugins such as asciidoctor-gradle-jvm 4.0.5 that are not yet CC-compatible) 
succeed
+# rather than failing during CC-entry storage.  Compile/test builds have no CC 
problems
+# and continue to benefit from the cache as normal.
+org.gradle.configuration-cache.problems=warn
+
 # enable --parallel
 org.gradle.parallel=true
 
diff --git a/subprojects/groovy-ant/build.gradle 
b/subprojects/groovy-ant/build.gradle
index 872c7a17ef..45ec426f37 100644
--- a/subprojects/groovy-ant/build.gradle
+++ b/subprojects/groovy-ant/build.gradle
@@ -55,9 +55,14 @@ dependencies {
 
 tasks.withType(Test).configureEach {
     // Supply the external jar (resolved, not checked in) to the GROOVY-9197 
Ant test.
+    // Accessing 'configurations' inside doFirst (execution time) is forbidden 
by the
+    // configuration cache; capture a CC-safe ConfigurableFileCollection at 
configuration
+    // time instead, and resolve it lazily when the task action runs.
+    def externalJarFiles = objects.fileCollection()
+        .from(configurations.named('externalJarForJointCompilationTest'))
+    inputs.files externalJarFiles
     doFirst {
-        systemProperty 'groovy.ant.test.externalJar',
-            
configurations.externalJarForJointCompilationTest.singleFile.absolutePath
+        systemProperty 'groovy.ant.test.externalJar', 
externalJarFiles.singleFile.absolutePath
     }
 
     Integer feature = 
TargetJavaHomeSupport.featureVersionFromReleaseFile(TargetJavaHomeSupport.targetJavaHome(project))
diff --git a/subprojects/groovy-binary/build.gradle 
b/subprojects/groovy-binary/build.gradle
index 558b7ac2ec..6a59534b62 100644
--- a/subprojects/groovy-binary/build.gradle
+++ b/subprojects/groovy-binary/build.gradle
@@ -21,7 +21,6 @@ import org.apache.tools.ant.filters.ReplaceTokens
 plugins {
     id 'org.apache.groovy-distribution'
     id 'org.apache.groovy-published-library'
-    id 'org.apache.groovy-asciidoctor'
 }
 
 //only used when testing locally built artifacts, not for publishing
@@ -39,12 +38,14 @@ docAggregation {
             '**/*.interp' // Antlr generated file
 }
 
-tasks.named('asciidoctor') {
-    attributes reldir_root: '.',
-            reldir_jmx: '.',
-            reldir_swing: '.',
-            reldir_console: '.',
-            reldir_groovysh: '.'
+if (sharedConfiguration.isDocumentationBuild) {
+    tasks.named('asciidoctor') {
+        attributes reldir_root: '.',
+                reldir_jmx: '.',
+                reldir_swing: '.',
+                reldir_console: '.',
+                reldir_groovysh: '.'
+    }
 }
 
 distribution {
@@ -137,11 +138,13 @@ distribution {
         into('html/gapi') {
             from tasks.named('groovydocAll')
         }
-        into('html/documentation') {
-            from configurations.allAsciidoc
-            from tasks.named('asciidoctor')
-            from tasks.named('asciidoctorPdf')
-            exclude '.asciidoctor'
+        if (sharedConfiguration.isDocumentationBuild) {
+            into('html/documentation') {
+                from configurations.allAsciidoc
+                from tasks.named('asciidoctor')
+                from tasks.named('asciidoctorPdf')
+                exclude '.asciidoctor'
+            }
         }
         into('html/groovy-jdk') {
             from tasks.named('docGDK')
diff --git a/subprojects/groovy-groovydoc/build.gradle 
b/subprojects/groovy-groovydoc/build.gradle
index 0ec415ae78..0ee1d02a59 100644
--- a/subprojects/groovy-groovydoc/build.gradle
+++ b/subprojects/groovy-groovydoc/build.gradle
@@ -38,7 +38,7 @@ dependencies {
 
 compileJava {
     doLast {
-        mkdir "${sourceSets.main.java.classesDirectory.get().asFile}/META-INF"
+        new File(destinationDirectory.get().asFile, 'META-INF').mkdirs()
     }
 }
 

Reply via email to