Skip to content

Commit 3d4c6ea

Browse files
committed
Fix Task.project invocation at execution time deprecations
Fix deprecated Task.project access at execution time (scheduled for removal in Gradle 10) by capturing project properties at configuration time. Changes: - SbomPlugin: Capture projectName, projectPath, and buildDate at configuration time; refactor pickLicense() to accept these values instead of task reference - PublishPlugin: Capture PublishingExtension at configuration time before doLast block - GrailsGradlePlugin: Capture buildDir at configuration time for buildProperties task; capture antBuilder at configuration time for native2ascii task - GrailsProfileGradlePlugin: Replace project.sync in doLast with Sync task type Tested output before/after changes: - cyclonedxBom: Identical (except expected timestamp/UUID differences) - savePublishedArtifacts: Identical - processProfileResources: Identical - buildProperties: Functionally identical (minor formatting: one less blank line) - grails-doc docs: Identical (verified Grails BOM.html)
1 parent 8a85313 commit 3d4c6ea

File tree

4 files changed

+72
-58
lines changed

4 files changed

+72
-58
lines changed

build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/PublishPlugin.groovy

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,14 @@ class PublishPlugin implements Plugin<Project> {
9292
task.group = 'publishing'
9393
task.outputs.dir(artifactsDir)
9494
task.dependsOn(project.tasks.withType(Jar))
95+
96+
// Capture publishing extension at configuration time to avoid Task.project access at execution time
97+
// See: https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:requirements:use_project_during_execution
98+
def publishingExtension = project.extensions.getByType(PublishingExtension)
99+
95100
task.doLast {
96101
Map<String, String> artifacts = [:]
97-
project.extensions.getByType(PublishingExtension).publications.withType(MavenPublication).each { MavenPublication publication ->
102+
publishingExtension.publications.withType(MavenPublication).each { MavenPublication publication ->
98103
publication.artifacts.each { MavenArtifact artifact ->
99104
if (!artifact.file.exists() || artifact.file.name in ['grails-plugin.xml', 'profile.yml']) {
100105
return

build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -206,21 +206,25 @@ class SbomPlugin implements Plugin<Project> {
206206

207207
// cyclonedx does not support "choosing" the license placed in the sbom
208208
// see: https://github.com/CycloneDX/cyclonedx-gradle-plugin/issues/16
209+
// Capture project name at configuration time to avoid deprecated Task.project access at execution time
210+
// See: https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:requirements:use_project_during_execution
211+
def projectName = project.name
212+
def projectPath = project.path
213+
ZonedDateTime buildDate = lookupProperty(project, 'buildDate')
209214
doLast {
210215
// json schema is documented here: https://cyclonedx.org/docs/1.6/json/
211216
def rewriteSbom = { File f ->
212217
def bom = new JsonSlurper().parse(f)
213218

214219
// timestamp is not reproducible: https://github.com/CycloneDX/cyclonedx-gradle-plugin/issues/292
215-
ZonedDateTime buildDate = lookupProperty(project, 'buildDate')
216220
bom['metadata']['timestamp'] = DateTimeFormatter.ISO_INSTANT.format(buildDate.truncatedTo(ChronoUnit.SECONDS))
217221

218222
// components[*]
219223
def comps = (bom instanceof Map && bom.components instanceof List) ? bom.components : []
220224
comps.each { c ->
221225
// .licenses => choose a license that is compatible with ASF policy if multiple licensed
222226
if (c instanceof Map && c.licenses instanceof List && !(c.licenses as List).empty) {
223-
def chosen = pickLicense(task, c['bom-ref'] as String, c.licenses as List)
227+
def chosen = pickLicense(logger, projectName, c['bom-ref'] as String, c.licenses as List)
224228
if (chosen != null) {
225229
c.licenses = [chosen]
226230
}
@@ -253,7 +257,7 @@ class SbomPlugin implements Plugin<Project> {
253257

254258
f.setText(JsonOutput.prettyPrint(JsonOutput.toJson(bom)), StandardCharsets.UTF_8.name())
255259

256-
logger.info('Rewrote JSON SBOM ({}) to pick preferred license', project.relativePath(f))
260+
logger.info('Rewrote JSON SBOM ({}) to pick preferred license', projectPath)
257261
}
258262

259263
sbomOutputLocation.get().with { rewriteSbom(it.asFile) }
@@ -263,29 +267,39 @@ class SbomPlugin implements Plugin<Project> {
263267
}
264268
}
265269

270+
/**
271+
* Picks the most appropriate license for a dependency from a list of license choices.
272+
* This method is called at execution time and should not access Task.project.
273+
*
274+
* @param logger the logger to use for logging
275+
* @param projectName the name of the project (captured at configuration time)
276+
* @param bomRef the bom reference for the dependency
277+
* @param licenseChoices the list of license choices
278+
* @return the chosen license
279+
*/
266280
@CompileDynamic
267-
private static Object pickLicense(CycloneDxTask task, String bomRef, List licenseChoices) {
281+
private static Object pickLicense(org.gradle.api.logging.Logger logger, String projectName, String bomRef, List licenseChoices) {
268282
if (!bomRef) {
269-
throw new GradleException("No bomRef found for a dependency of ${task.project.name}, cannot pick license")
283+
throw new GradleException("No bomRef found for a dependency of ${projectName}, cannot pick license")
270284
}
271285

272-
task.logger.info('Picking license for {} from {} choices', bomRef, licenseChoices.size())
286+
logger.info('Picking license for {} from {} choices', bomRef, licenseChoices.size())
273287
if (LICENSE_MAPPING.containsKey(bomRef)) {
274288
// There are several reasons that cyclone will get the license wrong, usually due to upstream not publishing information or publishing it incorrectly
275289
// see the licenseMapping map above for details
276290
def licenseId = LICENSE_MAPPING[bomRef]
277-
task.logger.lifecycle('Forcing license for {} to {}', bomRef, licenseId)
291+
logger.lifecycle('Forcing license for {} to {}', bomRef, licenseId)
278292

279293
def licenseBlock = LICENSES[licenseId]
280294
if (!licenseBlock) {
281-
throw new GradleException("Cannot find license information for id ${licenseId} to use for bomRef ${bomRef} in project ${task.project.name}")
295+
throw new GradleException("Cannot find license information for id ${licenseId} to use for bomRef ${bomRef} in project ${projectName}")
282296
}
283297

284298
return licenseBlock
285299
}
286300

287301
if (!(licenseChoices instanceof List) || licenseChoices.isEmpty()) {
288-
throw new GradleException("No License was found for dependency: ${bomRef} in project ${task.project.name}")
302+
throw new GradleException("No License was found for dependency: ${bomRef} in project ${projectName}")
289303
}
290304

291305
def licenseIds = licenseChoices.findAll { it instanceof Map && it.license instanceof Map && it.license.id }
@@ -297,13 +311,13 @@ class SbomPlugin implements Plugin<Project> {
297311
def defaultLicense = licenseChoices[0] // pick the first one found
298312
def defaultLicenseId = defaultLicense.license.id as String
299313
if (defaultLicenseId == null) {
300-
throw new GradleException("Could not determine License id for dependency: ${bomRef} in project ${task.project.name} for value ${defaultLicense}")
314+
throw new GradleException("Could not determine License id for dependency: ${bomRef} in project ${projectName} for value ${defaultLicense}")
301315
}
302316
if (!(defaultLicenseId in PREFERRED_LICENSES)) {
303-
def projectLicenseExemptions = LICENSE_EXCEPTIONS[task.project.name] ?: [:]
317+
def projectLicenseExemptions = LICENSE_EXCEPTIONS[projectName] ?: [:]
304318
def permittedLicense = projectLicenseExemptions.get(bomRef) == defaultLicenseId
305319
if (!permittedLicense) {
306-
throw new GradleException("Unpermitted License found for bom dependency: ${bomRef} in project ${task.project.name} : ${defaultLicenseId}")
320+
throw new GradleException("Unpermitted License found for bom dependency: ${bomRef} in project ${projectName} : ${defaultLicenseId}")
307321
}
308322
}
309323

grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/core/GrailsGradlePlugin.groovy

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -380,13 +380,20 @@ ${importStatements}
380380

381381
buildPropertiesTask.inputs.properties(buildPropertiesContents)
382382
buildPropertiesTask.outputs.file(buildInfoFile)
383+
384+
// Capture build directory at configuration time to avoid Task.project access at execution time
385+
// See: https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:requirements:use_project_during_execution
386+
def buildDir = project.layout.buildDirectory.asFile.get()
387+
383388
buildPropertiesTask.doLast {
384-
project.buildDir.mkdirs()
385-
ant.mkdir(dir: buildInfoFile.parentFile)
386-
ant.propertyfile(file: buildInfoFile) {
387-
for (me in buildPropertiesTask.inputs.properties) {
388-
entry(key: me.key, value: me.value)
389-
}
389+
buildDir.mkdirs()
390+
buildInfoFile.parentFile.mkdirs()
391+
Properties props = new Properties()
392+
buildPropertiesTask.inputs.properties.each { key, value ->
393+
props.setProperty(key as String, value as String)
394+
}
395+
buildInfoFile.withOutputStream { out ->
396+
props.store(out, null)
390397
}
391398
PropertyFileUtils.makePropertiesFileReproducible(buildInfoFile)
392399
}
@@ -813,12 +820,17 @@ ${importStatements}
813820
@CompileDynamic
814821
protected TaskProvider<Task> createNative2AsciiTask(TaskContainer tasks, src, dest) {
815822
TaskProvider<Task> native2asciiTask = tasks.register('native2ascii').configure {
823+
it.inputs.dir(src)
824+
it.outputs.dir(dest)
825+
826+
// Capture ant builder at configuration time to avoid Task.project access at execution time
827+
// See: https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:requirements:use_project_during_execution
828+
def antBuilder = it.ant
829+
816830
it.doLast {
817-
it.ant.native2ascii(src: src, dest: dest,
831+
antBuilder.native2ascii(src: src, dest: dest,
818832
includes: '**/*.properties', encoding: 'UTF-8')
819833
}
820-
it.inputs.dir(src)
821-
it.outputs.dir(dest)
822834
}
823835

824836
native2asciiTask

grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/profiles/GrailsProfileGradlePlugin.groovy

Lines changed: 19 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import org.gradle.api.model.ObjectFactory
3535
import org.gradle.api.plugins.BasePlugin
3636
import org.gradle.api.plugins.GroovyPlugin
3737
import org.gradle.api.plugins.JavaLibraryPlugin
38+
import org.gradle.api.tasks.Sync
3839
import org.gradle.api.tasks.TaskProvider
3940
import org.gradle.api.tasks.bundling.Jar
4041

@@ -87,48 +88,30 @@ class GrailsProfileGradlePlugin implements Plugin<Project> {
8788
project.configurations.named('runtimeElements')
8889
.configure { it.extendsFrom(runtimeOnlyConfiguration.get()) }
8990

90-
TaskProvider<Task> processProfileResourcesTask = project.tasks.register('processProfileResources')
91-
processProfileResourcesTask.configure { Task task ->
91+
// Use Sync task type instead of project.sync in doLast to avoid Task.project access at execution time
92+
// See: https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:requirements:use_project_during_execution
93+
TaskProvider<Sync> processProfileResourcesTask = project.tasks.register('processProfileResources', Sync)
94+
processProfileResourcesTask.configure { Sync task ->
9295
task.group = 'build'
93-
task.inputs.dir(project.provider {
94-
def directory = project.layout.projectDirectory.dir('commands')
95-
directory.asFile.exists() ? directory : null
96-
}).optional().skipWhenEmpty()
97-
task.inputs.dir(project.provider {
98-
def directory = project.layout.projectDirectory.dir('templates')
99-
directory.asFile.exists() ? directory : null
100-
}).optional().skipWhenEmpty()
101-
task.inputs.dir(project.provider {
102-
def directory = project.layout.projectDirectory.dir('features')
103-
directory.asFile.exists() ? directory : null
104-
}).optional().skipWhenEmpty()
105-
task.inputs.dir(project.provider {
106-
def directory = project.layout.projectDirectory.dir('skeleton')
107-
directory.asFile.exists() ? directory : null
108-
}).optional().skipWhenEmpty()
10996

110-
task.doLast {
111-
project.sync { SyncSpec sync ->
112-
sync.from(project.layout.projectDirectory.dir('commands')) { CopySpec s ->
113-
s.exclude('*.groovy')
114-
s.into('commands')
115-
}
116-
117-
sync.from(project.layout.projectDirectory.dir('templates')) { CopySpec s ->
118-
s.into('templates')
119-
}
97+
task.from(project.layout.projectDirectory.dir('commands')) { CopySpec s ->
98+
s.exclude('*.groovy')
99+
s.into('commands')
100+
}
120101

121-
sync.from(project.layout.projectDirectory.dir('features')) { CopySpec s ->
122-
s.into('features')
123-
}
102+
task.from(project.layout.projectDirectory.dir('templates')) { CopySpec s ->
103+
s.into('templates')
104+
}
124105

125-
sync.from(project.layout.projectDirectory.dir('skeleton')) { CopySpec s ->
126-
s.into('skeleton')
127-
}
106+
task.from(project.layout.projectDirectory.dir('features')) { CopySpec s ->
107+
s.into('features')
108+
}
128109

129-
sync.into(project.layout.buildDirectory.dir('resources/profile/META-INF/grails-profile'))
130-
}
110+
task.from(project.layout.projectDirectory.dir('skeleton')) { CopySpec s ->
111+
s.into('skeleton')
131112
}
113+
114+
task.into(project.layout.buildDirectory.dir('resources/profile/META-INF/grails-profile'))
132115
}
133116

134117
TaskProvider<ProfileCompilerTask> compileTask = project.tasks.register('compileProfile', ProfileCompilerTask)

0 commit comments

Comments
 (0)