@@ -173,22 +173,16 @@ class DependencyLockTaskConfigurer {
173173 TaskProvider<SaveLockTask > saveLockTask = project. tasks. register(SAVE_LOCK_TASK_NAME , SaveLockTask )
174174
175175 saveLockTask. configure { saveTask ->
176- saveTask. doFirst {
177- // Skip global lock check if global locks are disabled
178- if (isGlobalLockDisabled()) {
179- return
180- }
181- // TODO: address Invocation of Task.project at execution time has been deprecated.
182- DeprecationLogger . whileDisabled {
183- SaveLockTask globalSave = project. rootProject. tasks. findByName(SAVE_GLOBAL_LOCK_TASK_NAME ) as SaveLockTask
184- if (globalSave && globalSave. outputLock. isPresent() && globalSave. outputLock. get(). asFile. exists()) {
185- throw new GradleException (' Cannot save individual locks when global lock is in place, run deleteGlobalLock task' )
186- }
187- }
188- }
189176 // Set input and output files using Property API
190177 saveTask. generatedLock. set(project. layout. buildDirectory. file(lockFilename ?: extension. lockFile. get()))
191178 saveTask. outputLock. set(project. layout. projectDirectory. file(lockFilename ?: extension. lockFile. get()))
179+
180+ // Configuration cache compatible: Capture global lock file path to check at execution time
181+ if (! isGlobalLockDisabled()) {
182+ saveTask. globalLockFile. set(
183+ project. rootProject. layout. projectDirectory. file(extension. globalLockFile)
184+ )
185+ }
192186 }
193187 configureCommonSaveTask(saveLockTask, lockTask, updateTask)
194188
@@ -198,7 +192,7 @@ class DependencyLockTaskConfigurer {
198192 private static void configureCommonSaveTask (TaskProvider<SaveLockTask > saveLockTask , TaskProvider<GenerateLockTask > lockTask ,
199193 TaskProvider<UpdateLockTask > updateTask ) {
200194 saveLockTask. configure { saveTask ->
201- saveTask . notCompatibleWithConfigurationCache( " Dependency locking plugin tasks require project access. Please consider using Gradle's dependency locking mechanism " )
195+ // Configuration cache compatible: SaveLockTask uses only Property API
202196 saveTask. mustRunAfter lockTask, updateTask
203197 saveTask. outputs. upToDateWhen {
204198 def generated = saveTask. generatedLock. get(). asFile
@@ -255,8 +249,6 @@ class DependencyLockTaskConfigurer {
255249
256250 private void setupLockProperties (TaskProvider<GenerateLockTask > task , DependencyLockExtension extension , Map overrideMap ) {
257251 task. configure { generateTask ->
258- generateTask. notCompatibleWithConfigurationCache(" Dependency locking plugin tasks require project access. Please consider using Gradle's dependency locking mechanism" )
259-
260252 // Set skipped dependencies
261253 generateTask. skippedDependencies. set(extension. skippedDependencies)
262254
@@ -272,12 +264,39 @@ class DependencyLockTaskConfigurer {
272264
273265 // Set overrides
274266 generateTask. overrides. set(overrideMap)
267+
268+ // Wire properties for configuration cache compatibility
269+ generateTask. projectDirectory. set(project. layout. projectDirectory)
270+ generateTask. globalLockFileName. set(extension. globalLockFile)
271+ generateTask. dependencyLockIgnored. set(project. provider { shouldIgnoreDependencyLock(project) })
272+
273+ // Wire Resolution API properties (Approach 1 - Official Gradle APIs) for regular locks
274+ // Use zip() to ensure both properties are evaluated together at execution time
275+ generateTask. resolutionResults. set(
276+ generateTask. configurationNames. zip(generateTask. skippedConfigurationNames) { configNames , skippedNames ->
277+ def lockableConfs = GenerateLockTask . lockableConfigurations(project, project, configNames, skippedNames)
278+
279+ lockableConfs. collectEntries { conf ->
280+ [(conf. name): conf. incoming. resolutionResult. rootComponent]
281+ }
282+ }
283+ )
284+
285+ generateTask. peerProjectCoordinates. set(project. provider {
286+ project. rootProject. allprojects. collect { p ->
287+ String group = p. group?. toString() ?: ' '
288+ String name = p. name
289+ " ${ group} :${ name} " . toString()
290+ }
291+ })
275292 }
276293 }
277294
278295 private TaskProvider<GenerateLockTask > configureGlobalLockTask (TaskProvider<GenerateLockTask > globalLockTask , String lockFilename ,
279296 DependencyLockExtension extension , Map overrides ) {
280- setupLockProperties(globalLockTask, extension, overrides)
297+ // Global lock uses the OLD API (not configuration cache compatible)
298+ // It relies on conventionMapping to set configurations dynamically at execution time
299+ // Do NOT call setupLockProperties for global lock - it interferes with conventionMapping
281300 globalLockTask. configure { globalGenerateTask ->
282301 globalGenerateTask. notCompatibleWithConfigurationCache(" Dependency locking plugin tasks require project access. Please consider using Gradle's dependency locking mechanism" )
283302 globalGenerateTask. doFirst {
@@ -290,17 +309,36 @@ class DependencyLockTaskConfigurer {
290309 // Set output file
291310 globalGenerateTask. dependenciesLock. set(project. layout. buildDirectory. file(lockFilename ?: extension. globalLockFile. get()))
292311
312+ // Wire minimal properties needed for basic checks in lock() method
313+ globalGenerateTask. projectDirectory. set(project. layout. projectDirectory)
314+ globalGenerateTask. globalLockFileName. set(lockFilename ?: extension. globalLockFile. get())
315+ globalGenerateTask. dependencyLockIgnored. set(project. provider { shouldIgnoreDependencyLock(project) })
316+ globalGenerateTask. skippedDependencies. set(extension. skippedDependencies)
317+ globalGenerateTask. overrides. set(overrides)
318+ globalGenerateTask. filter = extension. dependencyFilter
319+
320+ // Capture peer coordinates to avoid accessing project at execution time
321+ globalGenerateTask. peerProjectCoordinates. set(project. provider {
322+ project. rootProject. allprojects. collect { p ->
323+ String group = p. group?. toString() ?: ' '
324+ String name = p. name
325+ " ${ group} :${ name} " . toString()
326+ }
327+ })
328+
293329 // TODO: Refactor this to not use conventionMapping. The global lock's configuration logic is complex
294330 // because it creates aggregate configurations at execution time. This needs a proper Property-based solution.
295331 // For now, keeping conventionMapping for this specific case to maintain functionality.
296332 globalGenerateTask. conventionMapping. with {
333+ includeTransitives = { extension. includeTransitives. get() }
297334 configurations = {
298335 def subprojects = project. subprojects. collect { subproject ->
299336 def ext = subproject. getExtensions(). findByType(DependencyLockExtension )
300337 if (ext != null ) {
301338 Collection<Configuration > lockableConfigurations = lockableConfigurations(project, subproject, ext. configurationNames. get(), extension. skippedConfigurationNamesPrefixes. get())
302339 Collection<Configuration > configurations = filterNonLockableConfigurationsAndProvideWarningsForGlobalLockSubproject(subproject, ext. configurationNames. get(), lockableConfigurations)
303- Configuration aggregate = subproject. configurations. create(" aggregateConfiguration" )
340+ // Use unique name to avoid conflicts if evaluated multiple times
341+ Configuration aggregate = subproject. configurations. create(" aggregateConfiguration_${ System.currentTimeMillis()} _${ subproject.path.replace(':', '_')} " )
304342 aggregate. setCanBeConsumed(true )
305343 aggregate. setCanBeResolved(true )
306344 configurations
@@ -367,7 +405,6 @@ class DependencyLockTaskConfigurer {
367405 TaskProvider<Delete > deleteLockTask = project. tasks. register(' deleteLock' , Delete )
368406
369407 deleteLockTask. configure { it ->
370- it. notCompatibleWithConfigurationCache(" Dependency locking plugin tasks require project access. Please consider using Gradle's dependency locking mechanism" )
371408 it. delete saveLock. map { it. outputLock }
372409 }
373410
@@ -377,7 +414,6 @@ class DependencyLockTaskConfigurer {
377414 TaskProvider<Delete > deleteGlobalLockTask = project. tasks. register(' deleteGlobalLock' , Delete )
378415
379416 deleteGlobalLockTask. configure { it ->
380- it. notCompatibleWithConfigurationCache(" Dependency locking plugin tasks require project access. Please consider using Gradle's dependency locking mechanism" )
381417 it. delete saveGlobalLock. map { it. outputLock }
382418 }
383419 }
@@ -399,7 +435,6 @@ class DependencyLockTaskConfigurer {
399435 TaskProvider<DiffLockTask > diffLockTask = project. tasks. register(DIFF_LOCK_TASK_NAME , DiffLockTask )
400436
401437 diffLockTask. configure { diffTask ->
402- diffTask. notCompatibleWithConfigurationCache(" Dependency locking plugin tasks require project access. Please consider using Gradle's dependency locking mechanism" )
403438 diffTask. mustRunAfter(project. tasks. named(GENERATE_LOCK_TASK_NAME ), project. tasks. named(UPDATE_LOCK_TASK_NAME ))
404439
405440 // Set file properties
@@ -413,6 +448,17 @@ class DependencyLockTaskConfigurer {
413448 // Set output properties
414449 diffTask. outputDir. set(project. layout. buildDirectory. dir(" dependency-lock" ))
415450 diffTask. diffFile. set(diffTask. outputDir. file(" lockdiff.${ diffTask.diffFileExtension()} " ))
451+
452+ // Wire resolution results for path-aware diff (configuration cache compatible!)
453+ diffTask. resolutionResults. set(
454+ extension. configurationNames. zip(extension. skippedConfigurationNamesPrefixes) { configNames , skippedNames ->
455+ def lockableConfs = GenerateLockTask . lockableConfigurations(project, project, configNames, skippedNames)
456+
457+ lockableConfs. collectEntries { conf ->
458+ [(conf. name): conf. incoming. resolutionResult. rootComponent]
459+ }
460+ }
461+ )
416462 }
417463
418464 project. tasks. named(SAVE_LOCK_TASK_NAME ). configure { save ->
0 commit comments