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() } }
