Jendrik Johannes
07/01/2025, 11:41 AMFileTree
?
Input is:
src
└── a
├── b
│ └── c
│ └── x.tmp
└── z.txt
Some task using matching
:
val src = layout.projectDirectory.dir("src").asFileTree.matching {
include("**/*.txt")
}
tasks.register<Sync>("sync") {
from(src)
into(layout.buildDirectory.dir("target"))
}
Then I get (I find it surprising that **/*.txt
matches the directories 🤷♀️ )
build/target
└── a
├── b
│ └── c
└── z.txt
How do I get?
build/target
└── a
└── z.txt
(Wrong solutions in Thread)Jendrik Johannes
07/01/2025, 11:42 AM.matching { exclude { it.isDirectory } }
...is wrong as it excludes everything. Also the files that are inside directories (in the example a/z.txt
)Jendrik Johannes
07/01/2025, 11:44 AM.filter { !it.isDirectory }
...is wrong too. Yes, it excludes the directory entries, but filter { }
in general throws away the relative path of the file tree elements (also TIL for me). So a/z.txt
becomes z.txt
.Jendrik Johannes
07/01/2025, 11:46 AMtasks.register<Sync>("sync") {
includeEmptyDirs = false
...
}
Yes this can be done in the context of a Sync
/ Copy
task, but I want to use the FileTree
in another context. This still shows the issue though: it does not exclude the empty directories from the input tracking. If I rename one of the unimportant directories, the task will rerun although the output is the same.Vampire
07/01/2025, 11:50 AMThomas Broyer
07/01/2025, 12:30 PM@IgnoreEmptyDirectories
https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/IgnoreEmptyDirectories.htmlMartin
07/01/2025, 12:32 PMJendrik Johannes
07/01/2025, 12:35 PMJendrik Johannes
07/01/2025, 12:36 PMWhat is the snapshot value of a directory?I think it is only the (relative) path and the fact that it is there. As each file inside would be tracked separately. But I also do not know the details and that (empty) directories are tracked at all was also a surprise to me in this case.
Martin
07/01/2025, 12:39 PMMartin
07/01/2025, 12:41 PMsrc.forEach {}
doesn't show the directories...Martin
07/01/2025, 12:42 PMsrc.visit {}
does, which is probably what the Sync task is using)Jendrik Johannes
07/01/2025, 12:43 PM**/*.txt
matching directories that do not include *.txt
files. I think this is not an intuitive behavior. But I would think that this would be mentioned somewhere already... I mean it must be like this since gradle-0.1
.
(Someone knows about an issue or forum post on this?)Martin
07/01/2025, 12:48 PMJendrik Johannes
07/01/2025, 12:53 PM**
includes (which I never used much)
• The realization that empty directories are tracked as part of a FileTreeJendrik Johannes
07/01/2025, 12:55 PM@IgnoreEmptyDirectories
works in so far that changing something unrelated would no longer lead to the task being out-of-date. What would still happen is that the empty directories are created in the state they are in in the first run. But that's expected (they are ignored, not removed). But then you can combine it with includeEmptyDirs = false
to get it all.Martin
07/01/2025, 12:56 PMJendrik Johannes
07/01/2025, 12:57 PMval src = layout.projectDirectory.dir("src").asFileTree.matching {
include("**/*.txt")
}
tasks.register<CustomSync>("sync") {
inFiles.from(src)
outDir = layout.buildDirectory.dir("target")
}
abstract class CustomSync : DefaultTask() {
@get:Inject
abstract val files: FileSystemOperations
@get:InputFiles
@get:IgnoreEmptyDirectories // !!!
abstract val inFiles: ConfigurableFileCollection
@get:OutputDirectory
abstract val outDir: DirectoryProperty
@TaskAction
fun syncOp() {
files.sync {
includeEmptyDirs = false // !!!
from(inFiles)
into(outDir)
}
}
}
Vampire
07/01/2025, 1:16 PMVampire
07/01/2025, 1:16 PMval src = layout.projectDirectory.dir("src").asFileTree.matching {
include("**/*.txt")
exclude { it.isDirectory && it.file.walk().none { it.isFile && (it.extension == "txt") } }
}
Vampire
07/01/2025, 1:19 PMval src = layout.projectDirectory.dir("src").asFileTree.matching {
include {
(!it.isDirectory && (it.file.extension == "txt"))
|| (it.isDirectory && it.file.walk().any { it.isFile && (it.extension == "txt") })
}
}
Adam
07/01/2025, 1:26 PM