Replacement for evaluationDependsOn?

I have a task in the root project where the evaluation depends on properties of the subprojects, i.e. evaluation is only possible after the subprojects have been evaluated.

I have solved this be using

evaluationDependsOn(":org.jdrupes.jsonb.beans")
evaluationDependsOn(":org.jdrupes.jsonb.jmx")

Note that evaluationDependsOn is not marked as deprecated in the API docs.

But now I get a warning with a ridiculous hint about what to do:

The Provider.forUseAtConfigurationTime method has been deprecated. This is scheduled to be removed in Gradle 9.0. Simply remove the call. Consult the upgrading guide for further information: https://docs.gradle.org/8.10.2/userguide/upgrading_version_7.html#for_use_at_configuration_time_deprecation
        at build_263b35zqyq1m40npntwrn1pi0.run(/home/mnl/devel/jdrupes/jdrupes-jsonb/build.gradle:25)
        (Run with --stacktrace to get the full stack trace of this deprecation warning.)

I cannot “Simply remove the call” because this reintroduces the problem thart I solved by adding the call.

There are two points.

  1. The pure need for that call is a very strong sign that you do something very bad practice. You should really strongly consider decoupling your build logic so that you neither have evaluation dependencies, nor cross-project configuration or any other reaching into the model of other projects. This all is bad practice, and works against some more sophisticated Gradle features and optimizations.

  2. That call is not the problem. Have a look at the full --stacktrace to see where it really is coming from, probably from either of those projects you depend evaluation on, or some plugin you apply in those. The warning also did not suggest to remove that call, but to remove the no-op (in that version) forUseAtConfigurationTime() call.

First of all, thank’s for your answer!

  1. The pure need for that call is a very strong sign that you do something very bad practice.

    While in general I like using gradle, this kind of hint isn’t helpful, because the gradle documentation never goes beyond trivial examples, far away from real life projects. In this particular case, the use of evaluationDependsOn is mentioned in the documentation without any indication that this might be bad practice or indicating what might be a “better practice”.

    I’m using the axion release plugin to set the versions in the subprojects – based on information from git – using a “convention”, which is supposed to be the preferred way. I have a task in the root project which has a configuration that depends on the versions of the subprojects. (Some things are done differently if the subprojects have snapshot versions.) This task is related to the whole project tree, i.e. it cannot be split and executed by the subprojects individually.

    I don’t think that this is an unusual situation. But googling doesn’t provide any “best practice” solution for this, only the one I’m using: evaluationDependsOn.

  2. I’ll have a look at this, I didn’t because I assumed to get a message related to the build.gradle of the subproject if the cause can be found in the subproject.

Thanks again!

this kind of hint isn’t helpful

You don’t find it helpful that I tell you that you are following bad practice and what you should do instead?
How comes you find that not helpful? o_O

because the gradle documentation never goes beyond trivial examples

I don’t get how that is a “because” and I tend to strongly object. The docs are imho very big and also show some very advanced things. And in my opinion the Gradle docs are very good for a software tool’s doc.

In this particular case, the use of evaluationDependsOn is mentioned in the documentation without any indication that this might be bad practice or indicating what might be a “better practice”.

It also still shows to use task("...") to create task at some places which is bad practice and shows that you can use afterEvaluate { ... } even though it is highly discouraged, or how legacy script plugins work that have many quirks and should be avoided.
Generally any documentation is almost always almost instantly outdated, that’s just how the world works unfortunatley.
And things that are bad-practice are often not flagged as such in any documentation.
That doesn’t change that they are bad practice and should be avoided. :wink:

The documntation shows that the evaluationDependsOn exists and how to use it if really needed, how it also shows many other things that you could do, as Gradle is extremely powerful and flexible. But with great power for the user, comes great responsibility for the user to use it wisely. :wink:

You are here in the forum to get help from people that are maybe more experienced. And one of these users told you it is bad practice to use that method and what better to do. Take the advice or ignore it, that’s fully up to you, it is your build and you have to live with the consequences like slower builds, harder to adopt isolated project once they become stable and thus waste muuuch of the time of anyone interacting with the build, and so on.

set the versions in the subprojects – based on information from git

Imho, that indeed is a quite unusual situation and a very bad idea.
I know a few poeple doing such things, but I personally think this is a very bad idea.
This ties the buildability to being in a Git worktree and if JGit is used even only to the main worktree and not any additional worktrees.
And if you ever change the VCS, you cannot checkout an old state and build properly, or if you export the sources to an archive and try to build from that wihtout Git available, you also cannot build properly.

But to everyone his self-created hell. :slight_smile:

I have a task in the root project which has a configuration that depends on the versions of the subprojects. (Some things are done differently if the subprojects have snapshot versions.) This task is related to the whole project tree, i.e. it cannot be split and executed by the subprojects individually.

Well, I cannot give a concrete advice as you did not share enough information.
For example what is done with that information?
Do you really need the configuration at configuration time?
If the task just does different things depending on those versions, you can for example create a configuration, depend on the other projects, give the resolution result of that configuration as input to the task, and then at execution time check if there are any snapshot versions in project dependencies. This can even be done in a configuration-cache safe way, …
Just to explain one of the possibilities, depending on concrete situation.

It’s the “instead” that I’m frequently missing in answers – which shouldn’t simply be “apply best practices”.

You obviously don’t, but I do like to think “about SCM as ultimate source of truth about project version.” We are all tying our projects to a lot of things: a programming language (some even use unstable ones), imported libraries, availability of maven central (or some other repository) for building, a build tool :wink: etc. Requring a GIT worktree for building seems like a minor binding to me.

You’re right that I didn’t provide details. That’s because I don’t expect anybody to dig into the details of my projects. I always try to ask a question in general terms. In this case this was:

What differs is handling of the generated javadoc. If it is not a snapshot version, the javadoc is additionally copied to a “latest-release” subdirectory. Note that gitPublish has to be in the root directory (which is a restriction/deficiency of the plugin I use). But event if there wasn’t this restriction, I wouldn’t like every subproject to do its own checkout/commit/push for performance reasons. So this is clearly an “overall” task to be performed after the subprojects have been built (or at least the javadoc has been generated).

Of course, I could, in this particular case, prepare the complete tree of files to be committed by gitPublish in the subprojects (copying them into some directory in the root project’s build directory). But this would involve copying every file twice. First by the task in the subproject to the directory in the root project and then from this directory into the checked out git tree (done by the git publishing plugin).

The double copying is something I’d like to avoid. It wouldn’t matter for this small project, but I have larger ones (using the same approach) where there are dozens of subprojects (of course, I use a loop for those in gitPublish) and each produces hundreds of javadoc files.

It’s the “instead” that I’m frequently missing in answers – which shouldn’t simply be “apply best practices”.

Hopefully not in answers by me.
If so, that’s unintentional.
I always try to give concrete suggestions how to do it better.
In the case here it first was just a relatively vague “decouple your build logic”, but more suggestion was not possible as you did not share the concrete use-case.
Later when you shared minimally more about the use-case I also gave a more concrete suggestion even if it is based on some conditions and assumptions but the best that was possible for me with the information you provided.

That’s because I don’t expect anybody to dig into the details of my projects. I always try to ask a question in general terms.

That’s perfectly fine and good.
But sometimes too general questions can only produce vague answers.
Especially if the problem is an XY-problem. :slight_smile:

copying them into some directory in the root project’s build directory

That’s also not the best idea actually.
Your problem is that you are mixing many bad practices together in that build.
For example any explicit dependsOn that does not have a lifecycle task on the left-hand side is a code-smell and usually hints at you not properly wiring task outputs to task inputs or doing something other unfortunate.

And reaching into the project model of another project (for example to get the version or the build directory) is practically always a very bad idea, introducing project coupling and works against some more sophisticated Gradle features and optimizations as I said. For example the upcoming isolated projects can save you a massive amount of time during configuration and IDE sync, especially in those larger ones you mentioned.

If you for example would use withSourcesJar() and withJavadocJar() instead of manually creating tasks for that and - also bad practice - adding manual atrifacts to the publication, you would save some of the boilerplate you have and additionally you would get proper variants for the sources and javadocs. So instead of reaching into the build dir of another project manually and depending on tasks of another project manually, you could simply depend on the javadoc variant of the projects and just get the result including any necessary task dependencies automatically. And as you would probably don’t like that it would then be packed to a jar that you then again have to unpack, you could easily add a secondary variant for the files directly and thus get the built files without any copying around but in a safe and idiomatic way, just like you can get the compiled classes of other projects when using the java-library plugin.

For the latest-release stuff, I’d probably instead have a convention plugin that prepares the needed structure according to the version within the respective projects as another outgoing variant or outgoing confiugration or maybe two (one for normal, one for latest if no snapshot or empty if snapshot), and then just depend on those variants from your root project. This way you should also easily get around without extra copying around, without reaching into project models of other projects, without explicit dependsOns, and all in a safe and idiomatic way.

Thanks! I’ll have a closer look at all your points.

The problem here is that gradle is changing fundamental approaches frequently. E.g. creating tasks for sources jar and javadoc jar used to be the only/recommended solution once (see e.g. here – you did “flag” this particular one, but only in 2023). When I started using gradle, I was happy to have that one solved. You cannot research every few month for alternative approaches for the build scripts.

The problem here is that gradle is changing fundamental approaches frequently.

I don’t really agree.
Also how those javadoc and sources jars are handled is not fundamental.
You can as well create them manually and add variants for them manually, you just don’t have to as Since 5 years Gradle can do it for you with a one-liner conveniently.

The post you quoted is simply only half-correct.
That some user posts some seeming solution to a forum does not necessarily mean it is the right way to do it.
Using artifact(...) on a publication is almost always the wrong thing to do, but you should in most cases get the component you are publishing and add variants to it for the artifacts you want to publish, so that they are also properly represented in the Gradle Module Metadata.

Those one-liners I mentioned were in no way any fundamental change how things work.
They are just convenience one-liners that do it for you the proper way you should have used before already.

When I started using gradle, I was happy to have that one solved. You cannot research every few month for alternative approaches for the build scripts.

Noone expects you to, use whatever makes you happy and works for you.
You just have to live with the consequences.
I also don’t get why you are attacking me instead of saying “thank you for showing me the modern way to replace various custom lines by just two standard lines which also fixes the bad practice I used and also helps with my current use-case”, at least mentally, not necessarily actually.

If you don’t want to get proper recommendations to solve your problems the best possible way, maybe consider stating that in the beginning of your question, then I can simply skip it instead of trying to help you the right way. :wink:

I apologize if you got that impression and would like to point out that I did start my last response with “Thanks!” (which could also have been “Thanks again!”, see my first response in this thread) and stated “that I’ll have a closer look at all your points”.

The remark following that first paragraph was in no way directed at you personally. I just attempted to point out – in general (!) (after all, the forum is called “Help/Discuss”) – that it isn’t easy to follow all changes in gradle for the “casual user”. Those changes may not seem fundamental to you – and maybe “fundamental” isn’t the right term anyway. But anything that forces me to revisit my build scripts seems like a fundamental change to me.

You’re obviously deeply involved in gradle development, while I just need a tool for building my projects. I’ve started using gradle in – I think – 2017 or even before this. And while you obviously feel different, I think it was easier to use then than it is now. Basically all you had to know about was how to define your tasks and maybe some dependencies. Some things – like creating the additional maven artifacts – were not covered in the documentation then and therefore I had to resort to solutions that I could find in the net or think of something myself.

Once the build was working, there was actually no motivation for me to change my scripts. With one notable exception: new gradle versions complaining about things that wouldn’t be compatible with the next version. And while I would have been perfectly happy with never upgrading gradle, this isn’t possible because we have these changing Java versions and you need newer versions of gradle for building with newer versions of Java. Therefore …

… isn’t fully true. I cannot use the build scripts from 2017 with a current version of gradle although I was quite happy with them. (Just for comparison: a current version of make processes my more than 30 years old Makefiles without any complaints.)

I actually enjoyed the discussion and apologize again if you got the impression that I was attacking you. Let me repeat my previous answer (without any addendum):

I apologize if you got that impression

All good, I might be a bit too sensitive right now, I’m recovering from a surgery, sorry.

But anything that forces me to revisit my build scripts seems like a fundamental change to me.

I don’t necessarily disagree.
But the point why that specific change is not fundamental is, that if you would have done it right back then, you would not need to revisit the build script, but you can exchange quite some lines for just two and Gradle does the actual work for you.
The problem why you “need” to revisit is, because you unluckily followed a bad advice, not because something changed in Gradle. :slight_smile:

I don’t say that there are not cases where you are right. Gradle is still pretty well evolving and there is no significant evolution without change. :slight_smile:

You’re obviously deeply involved in gradle development, while I just need a tool for building my projects.

“Obviously” not, I’m just a user like you, sending a contribution to Gradle now and then.
Other than that, I’m just deeply into using it.

use whatever makes you happy and works for you

… isn’t fully true. I cannot use the build scripts from 2017 with a current version of gradle although I was quite happy with them.

How is it “not fully true” then?
It hits the “works for you” condition.
As it does not work, you cannot use it, so my statement still holds. :smiley:

Just for comparison: a current version of make processes my more than 30 years old Makefiles without any complaints.

And so would a Maven POM most likely.
Because those tools are not evolving or if so, only very slowly.
On the other hand Gradle is fastly evolving, providing improvements and new features.
And as I said, no significant evolution without change. :slight_smile:

And you can still use this discouraged bad-practice way of doing it, no problem.
It still works with current Gradle version.
It just even back then was not a good idea to do it like that.
And the use-case we talked about would benefit from doing it properly as I described.
Of course you are are free to use that bad code and solve the use-case in another bad practice way that then later bites you again somehow.
That’s what I meant with “use whatever makes you happy and works for you”. :wink:

Let me also share one concluding thing, that might make Gradle easier to use again in the future for the average user: Declarative Gradle :slight_smile: