How do I move files in a copy task, ignoring their...
# community-support
m
How do I move files in a copy task, ignoring their parent directory?
Copy code
processResources {
        val webpackTask = project(":site-frontend").tasks.getByName("webBrowser${if (project.production) "" else "DevelopmentExecutable"}Distribution")
        // Goal: move from "<task_output>/path/to/font.ttf" to "<resources>/static/fonts/font.ttf>"
        // Currently moves to "<resources>/static/fonts/path/to/font.ttf"
        into("static/fonts") {
            from(webpackTask) {
                include("**/*.ttf")  
            }
        }
    }
All of the suggestions I can find online use FileTree, but that doesn't seem to work on task inputs.
v
That whole construct is highly problematic actually. You should not reach into another project's model like that. Just like cross-project configuration this introduces project coupling which disturbs some more sophisticated Gradle features and optimizations. And also it only works if
site-frontend
is configured before that project. And even then, using
getByName
you break task-configuration avoidance. And even then, you miss the necessary task dependency and get either no files or stale files unless the task happens to accidentally run first.
See https://docs.gradle.org/current/userguide/cross_project_publications.html for proper ways to share things cross-project
Hm, link changed in latest docs, in previous it is: https://docs.gradle.org/8.10.2/userguide/cross_project_publications.html
t
Vampire is right on all counts, but to answer your question (setting these issues aside for now), use eachFile:
Copy code
eachFile { it.path = "" }
(unless you declare each file as an artifact published by the other project, in which case the problem would disappear, IIRC)
m
I don't think I understand those docs and how they apply to my situation (I'm exporting static files, not stuff that's on classpath configurations) Putting that aside, I have the same issue with a custom single-project task where I can't ignore the path of the file being copied.
v
I don't think I understand those docs and how they apply to my situation (I'm exporting static files, not stuff that's on classpath configurations)
No matter what you share. Never reach into another project's model, neither for configuring something, nor to retrieve some information or much worse task outputs. Actually one detail of what I said was wrong, the necessary task dependency should be there as you use the task as
from
argument, but everything else applies. If you need things from another project, use proper cross-project publication. Whether it is static files, or anything else does not really matter. And especially if it is about outputs of a task. Just make sure you read the second link, in the 8.11 docs this section was lost somehow, I hope accidentally. Other than that, what Thomas said. Either use for example
eachFile
to set the
path
or
relativePath
, or the solution you found using file tree would probably be something like
webpackTask.map { it.outputs.asFileTree.files }
.
asFileTree
gives a file tree which means when before directories were output it resolves to the single files in them but still preserving the tree, i.e. relative path, and then using
.files
flattens it out to only the files. The
.map
makes sure you do not loose the task dependency. I wrote this from the top of my head, so it might not be 100% correct.
m
Are there any example projects I could take a look at for reference of how to do these artifacts? I feel like the Gradle docs are too technical and require in-depth knowledge of how it works, which I don't have.
m
So for every task that has outputs I want to use elsewhere, I need to make a new shared configuration?
v
If you want to use it in another project, yes. Project dependencies are the way to safely share things between projects. If "every task" results in a high amount, you might to consider a refactoring of the build logic though. 🙂
m
I don't think it's necessary now, but for future reference, what build logic refactoring would you suggest?
v
That is impossible to answer generically, sorry. Just sounds like maybe a smell if you need many of those.
m
Hmm, I'm not quite sure I'm doing it right:
Copy code
// :site-frontend (provider)
artifacts {
    add(sharedConfiguration.name, tasks.named("copyFonts"))
    add(sharedConfiguration.name, tasks.named("copyIcons"))
    add(sharedConfiguration.name, tasks.named("webBrowser${if (project.production) "" else "DevelopmentExecutable"}Distribution"))
}

// :site-backend (consumer)
tasks {
    processResources {
        into("static/js") {
            from(sharedConfiguration) {
                include("*.js")
                include("*.wasm")
                if (!production) {
                    include("*.map")
                }
            }
        }
        into("static/fonts") {
            from(sharedConfiguration) {
                eachFile {
                    path = "static/fonts/${path.split('/').last()}"
                }
                include("**/*.ttf")
            }
        }
        into("static/icons") {
            from(sharedConfiguration) {
                eachFile {
                    path = "static/icons/${path.split('/').last()}"
                }
                include("**/*.svg")
            }
        }
    }
}
This doesn't seem to copy any of the files from any of the 3 tasks added to artifacts.
v
Hard to guess from this small excerpt. You die example do not show your configurations and dependencies. Maybe share an MCVE