I kind of know what is going on, I only want to know how to prevent it.
I have a multi-project build with the project Top, Middle, Bottom. Top has a compile dependency on Middle. Middle has a compile dependency on Bottom.
I created a task that defines ‘dependsOn: compileJava’. I only need the sources to be compiled. I don’t need any jar files for this task to execute properly! Still, Gradle insists on building the jars for Middle and Bottom. Since I have to call this task very often and those jars are very big, I would like to prevent this unnecessary step. I guess Gradle sets an implicit jar-dependency for Middle and Bottom due to the compile-dependencies mentioned above. How do I prevent this when executing this task?
I never understood why the Gradle compilation process has compile-dependent projects in the form of ‘ExternalModuleDependency’ (a.k.a. jars) on the compile class path. It seems very wasteful since the class files and resources inside those jars already exist in known folders and compileJava could just as well use those on its compile class path instead. Creating those jars seems unnecessary at this point. I always assumed they decided to do it this way since it is easier conceptually for developers to think of projects after a certain point only in the form of a single jar instead of a bunch of .class files and resources. Maybe it makes sense to change this in the near future since the latest Gradle changes seem to have a strong focus on improving performance and not doing unnecessary things (incremental compilation, on demand configuration, …)
Using Jars has its advantages: they are what will be published and used at runtime, they can be easily identified/checksummed, etc. Also, it’s not uncommon to add extra files to the Jar that don’t exist in the classes/resources dir. It may be worthwhile to reconsider this decision in the light of incremental compilation (since Jar generation time is now more likely to dominate compile time), but it won’t be a trivial change.
No doubt. Maybe the change could be kept small be restricting it somehow to compileJava or classes. Everything after that (jar, build, etc.) keeps relying on jars. Maybe one could make a change around ProjectDependency, which in a sort of ‘unresolved’ state offers the compiler output in its natural form but can be ‘resolved’ at a later point which triggers jar creation.
Maybe it would even make sense to go one step further and treat ExternalModuleDependency like both a jar and a set of files (using an ‘extracted’ state). Some external jars are extracted by Gradle if I am not mistaken. Still I cannot use those pre-extracted files. Therefore, when building a fat-jar on has to ‘fuse’ all those jars (huge overhead?) into a single one compared to just adding a lot of files on disk into a new jar.
P.S.: I misspoke in my first answer. I meant ProjectDependency and not ExternalModuleDependency. Both are jars under the hood though
It’s likely that upcoming work on the new JVM-component plugins will improve this situation. These plugins introduce the concept of different binary outputs for the same set of sources (a single ‘component’ in the new model). A common example of multiple binary outputs for a JVM Library would be jars targeting different JVMs, but these outputs can also vary on packaging, such as jar-file vs class-directory.
So in the future Gradle should be able to resolve a project dependency to a ClassDirectoryBinary instead of a JarFileBinary, avoiding the need to build the jar file for compilation.