Hi Gary,
I've been anticipating these questions - they're good ones. :) Let me take them in order:
1. Why not a separate transformation task prior to the copy?
Mostly because I need to modify the destination files *after* the copy, rather than the original files. So why not a separate transform after the copy? That's possible, but raises headaches with failure modes and incremental builds. I'm copying and modifying whole file trees, so if the (separate) transform task fails in the middle of the tree, it will not know where to resume later on - I have to delete the whole destination tree and rerun the entire transform *and* copy tasks. This is solved by combining the copy and transform inside a single tree walk - then any files left in the destination tree are guaranteed to be successfully modified, making incremental builds easy.
This is always going to be an issue with in-place transformations. Writing outputs to a different destination or filename is always preferable, but in my case I don't have that option. One of the tools I'm dealing with it Microsoft signtool, which only does in-place signing. As I explained to Sterling, I could do (and have done) this with intermediate files, but that just feels ugly - it creates a third copy of and a second copy operation for every file.
2. I'm not sure I like the idea of adding additional non-copy-related stuff onto Copy.
I understand the objection and to a degree, I agree. I might counter that the same could be said of the few text transforms already built into the task, but I think the real answer is: it's convenient. In essence, I'm combining copy + transform to simulate the "write the output to a different location" functionality that's missing from my external tools. Ideally the tree-walking behavior (CopySpec) embedded in the Copy task would be reusable outside the context of an AbstractCopyTask, but that's not the current reality. It may make more sense for me to create all new tasks instead of modifying copy, but I still need CopySpec-like functionality and the actual file copy operation, so it seems a real shame to duplicate all of that existing code. I'm leaning towards thinking we should really have an FileTreeTask instead, with Copy simply being one of the actions that can be applied to it.
3. You were proposing an afterEachFile hook, and not necessarily a transformation-specific action.
Right. I prefer generic and reusable when possible. :) I'm looking for minimal required modification to the Gradle core here. My build-specific work would be encapsulated in an Action. As for examples, I've already mentioned signtool. Another example is icon modification. We compile a binary once, but then inject different icons into it depending on what product it's going into, what OEM we're white-labelling it for, etc. (We really do this, yes.) In all these cases, I need to modify a destination (copied) file, but leave the original compiled binary pristine. eachFile is unfortunately unsuitable, since it fires before the copy has happened.
One other point I've neglected to mention up until now, because I'm shooting for generic, is that the builds I'm dealing with aren't Java. They're Windows binaries that were compiled from C++ or C# in an earlier, separate build step. (Not with Gradle - yet.) I'm trying to get to a point where we can use Gradle across the whole company, and this is just the first roadblock I'm trying to get past.
roger