I have a question about best practices for loading...
# community-support
a
I have a question about best practices for loading properties from a custom file. I was thinking that it might be better to move this functionality to the settings.gradle.kts file in a gradle.lifecycle.beforeProject scope, which would be during the initialization phase instead of the configuration phase. This would make it accessible to each project without having to call rootProject (having some weird condition where findProperty isn't finding something in the rootProject from a subproject). But I was thinking this would alleviate any race conditions when moving to Isolated Projects, when they configure in parallel. Is this a better way to load a separate property file? And what impact would this have on performance/caching?
j
If I understand this correctly, that is what I would do nowadays if I want to avoid loading the file multiple times. Option 1 (I think this is what you suggest) • Load file in settings.gradle and put content into an object that you register as extension in all projects in
gradle.lifecycle.beforeProject
• Then you can access all information through that single-instance extension in all subprojects. (There should be no issue with isolation, because the loading itself is done in the init phase which runs before the parallel configuration of projects.) • Disadvantage: potentially more boilerplate/complexity (creating extension, registering it in one place, accessing it in anothers) Option 2 • Load the custom properties individually in each subproject • Setup can be simpler without extension -> load properties and use them directly • Disadvantage: Multi loading of the same file. Gradle may still only load once if you load through
providers.fileContents
(not sure if that is true, would like to know). I am usually doing Option 2 but on the long run Option 1 feels cleaner.
v
Another option, have a shared build service that does the file parsing and that you ask for the properties.
But all these options have one major problem
The whole file is a configuration cache input, even if you edit a comment in that file, configuration needs to be redone as you read the file contents at configuration time.
If you only have properties in that file that are anyway read at configuration time this might be irrelevant.
But if you have properties in there that are just given to tasks that use them at execution time, there might be other options necessary like having a
ValueSource
per property, or one for the properties you need at configuration time and one for the others and so on.
Not sure whether with that you will be able to only read the file once, as afair you cannot use a shared build service within the value source that could provide the file contents.
But it might also be worth reading the file once per property if in exchange you can reuse configuration cache more often, but that is highly situation specific.
a
that is an interesting thought, as preventing the configuration cache from being invalidated could be more effective. A side thought: I'm actually surprised that Gradle doesn't currently detect (or maybe they plan to) see if you access a variable. I am currently able to send a
-PvariablethatIneveruse=whatever
that I never use and it affects the configuration cache. I would have hoped that it keeps track of that usage as inputs, but I guess it is much simpler than that. One issue I ran into with Option 1, I was unable to cache the data outside of the scope
gradle.lifecycle.beforeProject
, so unclear how to prevent reloading for each project. I got a serialization issue.
Copy code
* What went wrong:
Failed to notify build listener.
> Failed to isolate 'GradleLifecycle' action: cannot serialize Gradle script object references as these are not supported with the configuration cache.
Copy code
var j = 1
gradle.lifecycle.beforeProject {
    println("Configuring project ${j++}: ${this.path}")
}
as an example
makes sense, but unclear how you would prevent reloading the same file over and over for each project
v
I got a serialization issue.
Your
j
is a top-level property in the script, that practically means it is a property of the instance of the class implementing the Script logic and that instance cannot be serialized. You need to break this chain, for example like
Copy code
object DoesNotNeedAScriptReference {
    var j = 1
}
gradle.lifecycle.beforeProject {
    println("Configuring project ${DoesNotNeedAScriptReference.j++}: ${this.path}")
}
thank you 1
a
interesting, didn't realize it became part of the class instance
v
I am currently able to send a
-PvariablethatIneveruse=whatever
that I never use and it affects the configuration cache.
As far as I remember there is a ticket to improve this somewhere, but I don't find it right now.
a
glad to hear that, that would be a nice improvement
Planned for 9.2.0 currently