Do Gradle `Copy` tasks depend automatically on tasks in its `from` blocks? - gradle

Let's say there is a Gradle task that produces an artifact. For example, a Zip task:
tasks.register("myZip", Zip) {
...
}
Would the following task of type Copy automatically gain a dependency on task myZip?
task copyMyZips(type: Copy) {
from { subprojects.findAll { it.tasks.findByName('myZip') }.myZip }
into '/tmp'
}
Note the really convoluted way of referring to the task myZip.

Yes. The Copy tasks do gain dependency on task outputs and tasks which are mentioned in their from blocks.
Per documentation of method from in class Copy as of Gradle 7.4:
AbstractCopyTask from(Object sourcePath, Closure c)
Specifies the source files or directories for a copy and creates a child CopySourceSpec. The given source path is evaluated as per Project.files(java.lang.Object[]).
in turn, documentation of Project.files(java.lang.Object[]) (emphasis mine):
Returns a ConfigurableFileCollection containing the given files. You can pass any of the following types to this method:
[...most of the list snipped...]
A Task. Converted to the task's output files. The task is executed if the file collection is used as an input to another task.
A TaskOutputs. Converted to the output files the related task. The task is executed if the file collection is used as an input to another task.
Unfortunately, the documentation of Copy does not refer to what is passed into method from as "inputs". However, judging by the code of AbstractCopyTask.java every change to the CopySpec of the Copy task is propagated to the inputs via a ChildSpecListener. The listener is added to the field CopySpecInternal rootSpec .

Related

Gradle copies all the files if at least one is not up-to-date

For instance, I've got the following task:
task testCopy(type: Copy) {
from ("folder/copy_from")
into ("folder/copy_to")
eachFile {println it.name}
}
Unless the inner files of the folder copy_from are touched, task works fine. As soon as I change, let's say one file in the folder copy_from, then Gradle begins to copy all the files from copy_from folder into copy_to instead of copying only one changed/added file.
Is this behaviour expected? Is there a way to make Gradle copy only changed/added file?
Yes based on this github issue and gradle discuss:
The build is incremental in the sense that the copy task only executes
when things have changed but it isn’t incremental itself, in that it
only copies inputs that have been modified.
I couldn't find a propper solution, but one solution is just splitting your task into smaller ones with specific types.
task copy1(type: Copy) {
into 'build/out'
from ('src') {
include 'docs/*.txt'
}
eachFile {println it.name}
}
task copy2(type: Copy) {
into 'build/out'
from ('src') {
include 'docs/*.md'
}
eachFile {println it.name}
}
task copy3 {
dependsOn copy1, copy2
}
It's not exactly what you want but it improves the performance by reducing files to copy.
when you change a text file and run gradle copy3 it just copies the text files not md files.
UPDATE:
Ant copy task doesn't have this problem
from it's documentation:
By default, files are only copied if the source file is newer than the destination file, or when the destination file does not exist. However, you can explicitly overwrite files with the overwrite attribute
So you can use ant copy task instead, as we can use ant tasks from gradle:
task copyFromAnt {
doLast {
ant.copy(todir: 'build/out') {
fileset(dir: 'src')
}
}
}
ant logs the files it copies so you can check the log with help of gradle -d and grep:
gradle copyFromAnt -d | grep "\[ant:copy\]"
and to see just the files it copies with out up-to-dat and etc. you can use the below command:
gradle copyFromAnt -d | grep "\[ant:copy\] Copying"

How can I create a task in Gradle that depends on all files in a directory?

I have a directory, let's call it inputs. I need to create a task that will:
read in all of the files in the inputs directory, ie: inputs/*.
produce a new file somewhere else that combines these files in some way. (For the sake of example, you can assume I just want to concatenate all of the files into a single output file.)
How do I set up the task correctly such that the following requirements are met?
requesting the task a second time when nothing has changed doesn't execute the task ("Already up to date").
adding, editing, or deleting files in the inputs directory causes the task to be "out of date", and so requesting it after any of those has happened will cause it to re-execute.

Gradle copy task isn't overwriting file

I've got the following task:
preBuild.dependsOn "copyConfigFile"
task copyConfigFile(type: Copy) {
from 'ConfigSources/VersionInfo.java'
into 'src/main/java/com/company/gradleexperiments/'
expand([changeset: "12345",
changeset_time: "the time",
changeset_date : "the date"])
}
Its works as expected except after the file has been initially copied, if I change one of the values in the expand and build again, then the file is not being overwritten (the creation timestamp of the file in the target destination remains that last time it was built).
I did some googling and searching of past questions on this topic, however the answer I got was that the gradle copy task is by default always supposed to overwrite the file. If that is the case, then why is it not doing so for me?
This is a very old bug: the copy task doesn't consider the values passed to expand as inputs, and thus considers it's up-to-date even though these values have changed.
You can vote on this issue here.
This should be relatively easy to circumvent by adding the expanded values to the input explicitely. For example:
task copyConfigFile(type: Copy) {
from 'ConfigSources/VersionInfo.java'
into 'src/main/java/com/company/gradleexperiments/'
def values =
['changeset': '12345',
'changeset_time': 'the time',
'changeset_date': 'the date'];
inputs.properties(values);
expand(values);
}

How to remove an element from gradle task outputs?

is it possible to exclude an element from the output files of a Task in order to not consider it for the up-to-date check? In my case I have a copy task that automatically set the destination directory in outputs variable, but I'd like to remove it and set only some of the copied files.
Or, as alternative, is it possible to overwrite the entire outputs variable?
Thanks,
Michele.
Incremental tasks create snapshots from input and output files of a task. If these snapshots are the same for two task executions (based on the hash code of file content), then Gradle assumes that task is up-to-date.
You are not able to remove some files from output and expect Gradle to forget about them, simply because the hash codes will be different.
There is an option that allows you to manually define the logic of up-to-date checks.
You should use a method upToDateWhen(Closure upToDateClosure) in TaskOutputs class.
task myTask {
outputs.dir files('/home/user/test')
outputs.upToDateWhen {
// your logic here
return true; // always up-to-date
}
}
I've found the solution:
task reduceZip(type: Copy) {
outputs.files.setFrom(file("C:/temp/unzip/test.properties"))
outputs.file(file("C:/temp/unzip/test.txt"))
from zipTree("C:/temp/temp.zip")
into "C:/temp/unzip"
}
Outputs.files list could be modified only register new elements, not removing (for what I know). So I need to reset the list and then eventually add other files. The outputs.files.setFrom method reset the outputs.files list so it is possible add other file. In the example above I reduce the up-to-date check only to the test.properties and test.txt files.

Rename files in assemble task in gradle

I need to write a simple task to create a zip from the source code. I need to include a dir called 1-dir or 2-dir depending on a system property. But the name of the directory in the resulting zip should always be dir. So basically, I want to include a dir in a zip (conditionally) and rename it.
I tried the rename method but that does not work.
Any pointers?
It will be:
task zipDir(type: Zip) {
def fromDir = project.hasProperty('from') ? project.from : 'dir1'
from(fromDir)
into('dir')
}
It can be run in the following way gradle zipDir -Pfrom=dir2. If no from property is passed dir1 will be zipped.
If you need system property instead of gradle property, pass -Dfrom=dir2 and use System.properties['from'] instead of project.from.

Resources