I am using Java 26 and Gradle 9.5.0. I have an application that consists of 23 modules. I have a common task that I need to run after all 23 subproject modules have been built.
Help forums have given me this construct: dependsOn subprojects.tasks.collect { it.findByName(‘build’) }.findAll { it } which I have placed in the common task.
The problem is my common task commences when only 18 or 19 (it varies) of the subproject modules have finished and then fails. If I run the build again at this pint, it completes successfully.
How can I make ALL 23 module builds finish before this common task is started?
That snippet is horribly bad practice and you should never use it or anything similar. Besides being unreliable, it also reaches into the model of other projects which you should never do. It also works against some more sophisticated features, so if you would ever want to use configure on demand, or isolated projects, this would also never work or even be a hard error.
Besides reaching into the other project, it also is eager and thus only gets the tasks already registered the moment you call it, and it breaks task configuration avoidance for all those tasks.
Also, you should never use an explicit dependsOn anyway except with a lifecycle task on the left-hand side which your task is not, that is also bad practice.
How to do it properly heavily depends on what you try to do. So you might be at an XY problem. Take a step back and tell what you try to do / what that task is doing.
My apologies for using ‘horribly bad practices’. I am an absolute beginner at Groovy and Gradle and have been using queries to AI to try to get a solution. I am converting an Ant build sequence to Gradle. The Ant sequence creates 23 module jars, in sequence. The common step is the installer; all 23 jars are combined into a folder, to which the installer adds a couple of .bat files and some configuration files. An NSIS script then creates the installation executable. I haven’t been able to get the NSIS plugin working either but that was a question I was going to ask after I resolved my synchronization issue.
So please, excuse my ignorance, tell me what I am doing wrong and how I might fix it.
It’s not your fault, it is the fault of the one giving you that snippet.
and have been using queries to AI to try to get a solution.
AIs are prefectly good in giving answers that look correct, but are horribly bad in giving answers that are correct.
You always have to understand what the AI gives you and need to fix the non-sense it makes.
You cannot really trust anything the AI produces.
It is not suitable for things that you could not do or know yourself.
It is perfectly fine to use it if you are too lazy (the good kind of lazy) and let the AI do something for you, but you always have to be able to understand and evaluate and fix what it produced.
Also there is many bad or also outdated advice in the training data like internet forums,
and AIs are trash-in trash-out, so if it is trained with bad things, it will also produce bad things.
Also, AIs often hallucinate and for example use API that simply does not exist, never existed, and will never exist because ooooverly simplified AIs are “just” next-word-guessers.
tell me what I am doing wrong and how I might fix it.
In your project that builds the installer, declare normal project-dependencies on the other projects.
If your task that builds the installer then properly has the configuration that you use to get the actual files declared as input, you automatically get the necessary task dependencies and also the installer task can be up-to-date or taken from cache if it is a cacheable task, if the built jars did not change.
The AI solutions weren’t my only source of information. Besides other java/gradle/groovy forums, I’ve also seen quite a few youtube videos. I do try to understand the solutions and work out why they do (or don’t do) what they are supposed do. I do not just blindly accept the AI answer; a lot of forum answers are badly out of date as well.
I’m sorry, I do not understand a word of your last paragraph.
Anyway, I found my solution in stackoverflow, very simple:
It is not good anyway.
Your task is not a lifecycle task, so it should never have an explicit dependsOn.
Also, you depend on the build task in all projects which is not what you want, as you waste time.
To build the installer you wait until all that build depends on is executed, including executing tests and so on which is just a waste of time for building the installer.
One of the greatest strengths for Gradle is, to avoid unnecessary work, but only if you wire things properly and not adding dependencies that are unnecessary.
How do you get the jars to pack into the installer.
You hopefully not just get the jars by file-system path, that would also be bad practice.
As I said, you should anyway have dependencies on the subprojects to get the jars into a configuration and if you properly consume that configuration as inputs, you automatically have the necessary task dependencies.
For example with this:
dependencies {
subprojects.forEach { runtimeOnly(it) }
}
val collectRuntimeClasspath by tasks.registering(Sync::class) {
from(configurations.runtimeClasspath)
into(layout.buildDirectory.dir("runtimeClasspath"))
}
you automatically have task dependencies from collectRuntimeClasspath to all jar task which is the only dependency you need for that task, and get the jars and their dependencies collected in one directory.
…
I know I have no idea what I am doing at the moment; I can also see you and I are talking completely different languages. “lifecycle task”? “wire things properly”? “configurations”? “collectRuntimeClasspath”?
I have also come a cropper on the copy task. Silly me thought that include *.jar" meant copy only the jar file; somehow I finished up with the WHOLE directory structure in my output path.
Let me research what you have thrown at me and I’ll ask another question if I need more clarification.