I wrote the original post below before I realized that
Task#getOutputs() seems to provide a simpler version of what I proposed. When @st_oehme mentioned in his previous post that the
patching configuration need to be inputs to the task for it to be correct
I thought that he just used the term to loosely describe
JavaCompile#getClasspath(); now that I know about
Task#getInputs(), I will investigate its proper usage. I imagine that dependencies, dependsOn, etc. add to the inputs, and the outputs of each task are defined by the type of task and how it’s configured. I assume that any input I to task T requires that all other tasks whose outputs contain I must be executed before T can be execute. I imagine that ynputs & outputs only specify what must be available for a task to consume, and what the task produces, respectively, but doesn’t specify how the inputs are consumed, or how the outputs are produced. I imagine that the manner in which an input is consumed is a task-specifc; so, e.g.,
AbstractCompile#getClasspath() indicates that an input is consumed as a class-path component. I imagine that removing a component from a task’s class-path also removed it from the task’s inputs, but I haven’t verified this. (I suspect this, though, because tasks ran out of order when using Chainsaw, and I noticed that Chainsaw clears the class-path, but didn’t notice Chainsaw removing or modifying any inputs or outputs, besides adding one text property input that was also performed by
jigsaw-experimental, the latter of which didn’t suffer from the same task ordering issues).
Is the above correct?
My proposal below would track the consumption methodology in a standardized manner for all inputs, rather than leaving it ad hoc. It probably could be added in the background using facades to still present the existing input, output, class-path, and other interfaces, but it would have to support unknown consumption methodologies until you were comfortable that all Gradle code and enough plugins where refactored to use the complete new API to allow breaking backwards compatibility.
From a higher perspective, I imagine that the class-path dependencies are moved to the module-path (or patch-modules) in
Task#doFirst() actions because removing the class-path dependencies in the configuration phase would somehow ruin the dependencies, and there’s no existing place to put module-path or patch-module dependencies that preserve the dependencies for the rest of Gradle.
I imagine the nicest way to support modules would be to have the initial dependencies setup be able to specify a type of dependency (class-path, module-path, patch-module, etc.), so that dependencies would be in the appropriate location from the start, instead of having to transfer them from the class-path to the correct location at a later time. That, however, is well outside my knowledge of Gradle. If this wouldn’t require too much refactoring in Gradle, I’d be happy to help revamp Gradle core so that it could support modules this way, then add cleaner module support on top of the refactoring.
I’d imagine that this might be implemented by having an interface named something like
Dependency which would have implementations like
dependenciesByType property would be added to
AbstractCompile, which would be a
Multimap<? extends Class<T extends Dependency>, T>, i.e., it would map from a
Dependency sub-type to all dependencies of that sub-type for
this. So, e.g.,
getDependenciesByType().get(ClassPathDependency.class) would return all the class-path dependencies.
JavaCompile#classpath would initially be deprecated and backed by
getDependenciesByType().get(ClassPathDependency.class)) (or by
getDependenciesByType().values(), depending on if existing code must see all dependencies, or just class-path ones), and eventually would be removed.
if anything like this proposed solution were ever to be implemented, it would likely break some third-party plugins / builds, so it would probably be best to include it in a major release like 5.0 instead of a minor release like 4.10.