Skip to content

Replace dependsOn with source set configuration#49537

Closed
Sineaggi wants to merge 2 commits intospring-projects:mainfrom
Sineaggi:fix-build-info-depends-on
Closed

Replace dependsOn with source set configuration#49537
Sineaggi wants to merge 2 commits intospring-projects:mainfrom
Sineaggi:fix-build-info-depends-on

Conversation

@Sineaggi
Copy link

@Sineaggi Sineaggi commented Mar 9, 2026

Fixes the use of dependsOn in the spring boot gradle plugin. See Best Practices for Tasks - Avoid DependsOn

Unfortunately Gradle isn't smart enough to know that a dependsOn allows other tasks that might use sourceset outputs as their inputs, we should use Gradle's implicit input/output tracking instead.

Without this, if one uses for example a task that uses the main output resources, such as Google's protobuf gradle plugin, and another task that shares resources like test fixtures or additional test suites, you'll end up with issues with undefined dependencies.

3: Task failed with an exception.
-----------
* What went wrong:
A problem was found with the configuration of task ':app:extractIncludeIntegrationTestProto' (type 'ProtobufExtract').
  - Gradle detected a problem with the following location: '/Users/cwalker/source/spring-boot-build-info-issue/app/build/resources/main'.
    
    Reason: Task ':app:extractIncludeIntegrationTestProto' uses this output of task ':app:bootBuildInfo' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed.
    
    Possible solutions:
      1. Declare task ':app:bootBuildInfo' as an input of ':app:extractIncludeIntegrationTestProto'.
      2. Declare an explicit dependency on ':app:bootBuildInfo' from ':app:extractIncludeIntegrationTestProto' using Task#dependsOn.
      3. Declare an explicit dependency on ':app:bootBuildInfo' from ':app:extractIncludeIntegrationTestProto' using Task#mustRunAfter.

Signed-off-by: Clayton Walker <clayton.m.walker@gmail.com>
@Sineaggi Sineaggi force-pushed the fix-build-info-depends-on branch from 9f1c3b1 to c10796f Compare March 9, 2026 18:55
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Mar 9, 2026
Signed-off-by: Clayton Walker <clayton.m.walker@gmail.com>
@Sineaggi Sineaggi force-pushed the fix-build-info-depends-on branch from 237b665 to 4520150 Compare March 9, 2026 21:00
@wilkinsona
Copy link
Member

Thanks for the PR. Unfortunately, this approach will leave the main source set with an output dir configured as a src dir. I think that cycle is likely to cause problems. Please open an issue with a minimal sample that reproduces the failure described above so that we can investigate some alternatives.

Given the javadoc of SpringBootExtension.buildInfo() and SpringBootExtension.buildInfo(Action<BuildInfo>), I think we may have to change the documented contract. In the meantime, it may be possible to work around the issue using buildInfo(Action<BuildInfo>) and configuring the task with a different destination directory. We'll know more once we have a sample and can see the exact failure.

@wilkinsona wilkinsona closed this Mar 10, 2026
@wilkinsona wilkinsona added status: declined A suggestion or change that we don't feel we should currently apply and removed status: waiting-for-triage An issue we've not yet triaged labels Mar 10, 2026
@Sineaggi
Copy link
Author

this approach will leave the main source set with an output dir configured as a src dir.

Can you explain what this means? That's normally exactly how generated sources should be hooked up in gradle, you have a generating task and it's output directory is either added to the java srcDirs or the resources srcDirs. This way any users of the output of classes correctly wire up the producing tasks.

@Sineaggi
Copy link
Author

Simplest reproducer:

springBoot {
    buildInfo()
}

tasks.register("resourceProcessor") {
    // this task does *something* with the resources
    val resourceProcessorInput = objects.directoryProperty()
    resourceProcessorInput.fileProvider(tasks.processResources.map { it.destinationDir })
    inputs.dir(resourceProcessorInput)
    doFirst {
        println(resourceProcessorInput.get().asFile)
    }
}

Now run gradle bootBuildInfo resourceProcessor and you should see

2: Task failed with an exception.
-----------
* What went wrong:
A problem was found with the configuration of task ':bootBuildInfo' (type 'BuildInfo').
  - Gradle detected a problem with the following location: 'C:\Users\Clayton\Source\gradle-generated-file-simplest-reproducer\build\resources\main\META-INF'.
    
    Reason: Task ':resourceProcessor' uses this output of task ':bootBuildInfo' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed.
    
    Possible solutions:
      1. Declare task ':bootBuildInfo' as an input of ':resourceProcessor'.
      2. Declare an explicit dependency on ':bootBuildInfo' from ':resourceProcessor' using Task#dependsOn.
      3. Declare an explicit dependency on ':bootBuildInfo' from ':resourceProcessor' using Task#mustRunAfter.

I should instead be able to run resourceProcessor and see all resources in the build resources ouput directory.

@wilkinsona
Copy link
Member

Can you explain what this means?

When SpringBootExtension creates a BuildInfo task, it configures it to use the resources dir of the main source set's output as its destination dir. Given this, the change proposed here will result in the source set using one of its own output dirs as a src dir.

@Sineaggi
Copy link
Author

Sineaggi commented Mar 10, 2026

Ah, ok I think I see what's happening. The spring boot plugin is writing directly into the output resources directory. That's owned by processResources, and two tasks shouldn't be writing into the same output directories.

As I understand it, the bootBuildInfo task should have it's output directory set to something like layout.buildDirectory("generated/boot-build-info") which should be marked as an @OutputDirectory and whose input should be marked as sourceSets.main.configure { resources.srcDirs(bootBuildInfo) }.
Edit: Then the bootBuildInfo task would take the output dir, append META-INF + filename then output the properties.

@Sineaggi
Copy link
Author

it may be possible to work around the issue using buildInfo(Action) and configuring the task with a different destination directory.

This is what we tried doing in the meantime, however we'd need something like this to make it all correct.

springBoot {
    buildInfo {
        destinationDir = layout.buildDirectory.dir("generated/boot-build-info")
        doLast {
            // destinationDir needs to point to the directory containing META-INF, but the bootBuildInfo task writes directly into destinationDir.
            val path = destinationDir.get().asFile.toPath()
            Files.move(
                path.resolve("build-info.properties"),
                Files.createDirectories(path.resolve("META-INF")).resolve("build-info.properties"),
                StandardCopyOption.REPLACE_EXISTING
            )
        }
    }
}

sourceSets.main {
    resources.srcDirs(tasks.named("bootBuildInfo"))
}

@Sineaggi
Copy link
Author

Please open an issue with a minimal sample that reproduces the failure described above so that we can investigate some alternatives.

Done!

#49547

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: declined A suggestion or change that we don't feel we should currently apply

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants