I have a simple custom task in a project's (lets call it projectA
) build script that creates a file. The task looks like this:
task createFile() {
def outputFile = rootProject.file("${buildDir}/tmp/outputFile.txt")
outputs.file(outputFile)
outputs.upToDateWhen { false }
doLast {
outputFile.parentFile.mkdirs()
outputFile.text = "Hello World"
}
}
The project is part of a multiproject build and I need the file generated by this task as input for a Copy task in projectB
. To achieve this I created a configuration in projectB
and a corresponding dependency:
configurations {
mySourceConfig
}
dependencies {
mySourceConfig project(path: ':projectA', configuration: 'myDistConfig')
}
task copySourceDependencies(type: Copy) {
from configurations.mySourceConfig
into contextDir
}
In projectA
I got a myDistConfig
configuration and want to use the output file of the createFile
task as artifact for this configruation:
configurations {
myDistConfig
}
artifacts {
myDistConfig createFile
}
If I do this, gradle tells me that a task cannot be converted to a ConfigurablePublishArtifact
:
> Cannot convert the provided notation to an object of type ConfigurablePublishArtifact: task ':projectA:createFile'. The following types/formats are supported: - Instances of ConfigurablePublishArtifact. - Instances of PublishArtifact. - Instances of AbstractArchiveTask, for example jar. - Instances of Provider<RegularFile>. - Instances of Provider<Directory>. - Instances of Provider<File>. - Instances of RegularFile. - Instances of Directory. - Instances of File. - Maps with 'file' key
So I tried createFile.outputs.files.singleFile
as artifact. This stops gradle from complaining, but fails to setup the dependency between :projectB:copySourceDependencies
and :projectA:createFile
and if projectB:copySourceDependencies
is executed on a clean workspace it is simply skipped with status NO-SOURCE
.
Is it somehow possible to use a custom task as artifact similar to a Zip task, to make gradle aware of the dependency?!
Update 2020-02-28:
Based on the very good answer by @BjørnVester I implemented the following task in a separate gradle file util.gradle
:
class CreateFile extends DefaultTask {
@OutputFile
RegularFileProperty outputFile = project.objects.fileProperty()
@TaskAction
void createFile() {
def tOutFile = outputFile.get().asFile
tOutFile.parentFile.mkdirs()
tOutFile.text = "Hello World"
}
}
rootProject.ext.CreateFile = CreateFile
In projectA's build.gradle
this looks like this:
apply from 'util.gradle'
task createFile(type: CreateFile) {
outputFile = rootProject.file("${buildDir}/tmp/outputFile.txt")
}
artifacts {
myDistConfig createFile.outputFile
}
Now gradle is aware of the correct dependencies and it works like a charm!
When using a task as the artifact notation, it has to be of type AbstractArchiveTask. So for other types of tasks, you will have to done something else.
The approach of using createFile.outputs.files.singleFile
is good as it is a supported type (as you can see from the error output). But the reason Gradle doesn't create a dependency to the producing task is because a File
does not carry that information. For that, it would have to be a Provider
. But a quick way to fix it is to just explicitly configure what task produces the artifact:
artifacts.add("myDistConfig", createFile.outputs.files.singleFile) {
builtBy("createFile")
}
Alternatively, you can also change the outputFile to a Provider<File>
or RegularFileProperty
using an ObjectFactory as those carry the task producer information. I've never used them as part of the DSL, and it might be more suited for working with classes. (And while that looks like more work, it makes the code more readable once it grows to a certain level.) Here is an example in Groovy:
class MyFileCreator extends DefaultTask {
@OutputFile
RegularFileProperty outputFile = project.objects.fileProperty().convention(project.layout.buildDirectory.file("tmp/outputFile.txt"))
@TaskAction
void createFile() {
File outFile = outputFile.get().asFile
outFile.parentFile.mkdirs()
outFile.text = "Hello World"
}
}
MyFileCreator createFileTask = tasks.create("createFile", MyFileCreator)
configurations {
myDistConfig
}
artifacts {
myDistConfig createFileTask.outputFile
}