Sharing task outputs across projects in a multi-project build Sample
You want to share a file made by a task in one project with a task in another project. For example, one task makes a file, and the other task reads the file and uses some information inside it. This is one way you can share information across project boundaries. (Another way is to use extension objects.)
This demonstrates the simple version of sharing information across project boundaries, by explicitly specifying which producer project’s consumable configuration to use for a locally available artifact. When the producer publishes an artifact to a repository, to retrieve that artifact you will need to use the advanced version of variant aware dependency resolution. This method will also work locally. |
Example
rootProject.name = "sharing-outputs"
include("producer")
include("consumer")
rootProject.name = "sharing-outputs"
include("producer")
include("consumer")
val makeFile = tasks.register("makeFile") {
val sharedFile = layout.buildDirectory.file("some-subdir/shared-file.txt")
outputs.file(sharedFile)
doFirst {
sharedFile.get().asFile.writeText("This file is shared across Gradle subprojects.")
}
}
val sharedConfiguration by configurations.creating {
isCanBeResolved = false
}
artifacts {
add(sharedConfiguration.name, makeFile)
}
def makeFile = tasks.register("makeFile") {
def sharedFile = layout.buildDirectory.file("some-subdir/shared-file.txt")
outputs.file(sharedFile)
doFirst {
sharedFile.get().asFile << "This file is shared across Gradle subprojects."
}
}
configurations {
sharedConfiguration {
canBeResolved = false
}
}
artifacts {
sharedConfiguration(makeFile)
}
val sharedConfiguration: Configuration by configurations.creating {
isCanBeConsumed = false
}
dependencies {
sharedConfiguration(project(path = ":producer", configuration = "sharedConfiguration"))
}
tasks.register("showFile") {
val sharedFiles: FileCollection = sharedConfiguration
inputs.files(sharedFiles)
doFirst {
logger.lifecycle("Shared file contains the text: '{}'", sharedFiles.singleFile.readText())
}
}
configurations {
sharedConfiguration {
canBeConsumed = false
}
}
dependencies {
sharedConfiguration(project("path": ":producer", "configuration": "sharedConfiguration"))
}
tasks.register("showFile") {
FileCollection sharedFiles = configurations.getByName("sharedConfiguration")
inputs.files(sharedFiles)
doFirst {
logger.lifecycle("Shared file contains the text: '{}'", sharedFiles.singleFile.text)
}
}
See also:
Anti-patterns:
A frequent anti-pattern to declare cross-project dependencies is below.
This publication model is unsafe and can lead to non-reproducible and hard to parallelize builds. By declaring a dependency in this way, the task ordering between consumers and producers is not known to Gradle at the time when it is deciding the order of tasks for a given build. This means that potentially, consumers of the file "someOtherJar" can execute before the producer task that creates the jar! This would lead to builds that are either totally broken, or worse, broken is a way that is subtle, flaky, and difficult to debug.
dependencies {
// This publication model can make your build flaky and broken!
implementation project(":other").tasks.someOtherJar
}