Use FileCollection APIs in settings.gradle


(uklance) #1

I’d like to use the FileCollection APIs in a settings.gradle file. Obviously the Project instances aren’t accessible yet so I can’t call Project.files(...) or Project.fileTree(...).

Is there a way to use FileCollection APIs from settings.gradle or do I have to make do with java.io.*?


(Pepper Lebeck-Jobe) #2

I believe your analysis is correct. There doesn’t appear to be a way to get an instance of a FileCollection while evaluating the settings.gradle file. Out of curiosity, what are you trying to do that makes you want to use the FileCollection API in your settings.gradle file? Maybe there is a missing feature we can identify and deal with in a straight-forward manner?


(uklance) #3

I want to dynamically determine the list of subprojects, eg find folders with build.gradle under a root folder. Simple to do with a FileTree, more verbose with java.io.*

Perhaps Settings.files(...) and Settings.fileTree(...) would be nice additions to the API?


(Pepper Lebeck-Jobe) #4

@Lance, sorry for the delay in reply. At this point, I think we’re talking about an actual feature request for Gradle (as opposed to general discussion and help requests.) Would you mind formalizing this request by opening an issue on the https://github.com/gradle/gradle repository?


(Stefan Oehme) #5

You can use files() and friends in settings.gradle just like in a project build script. The basedir will be the settings directory.


(Pepper Lebeck-Jobe) #6

My bad. @st_oehme, is, of course, accurate.

I wonder if we have a documentation bug to file. I think I fell into the same trap as @Lance.

I read these documents:
https://docs.gradle.org/current/userguide/working_with_files.html
https://docs.gradle.org/current/dsl/org.gradle.api.initialization.Settings.html
https://docs.gradle.org/current/javadoc/org/gradle/api/initialization/Settings.html
https://docs.gradle.org/current/javadoc/org/gradle/api/plugins/PluginAware.html

And, missing any documentation about a Settings.files() analogous to the Project.files(), I assumed that it wouldn’t work. In fact, you cannot call Settings.files() but have to just use files() directly. I haven’t yet figured out where this function comes from, but I’ll see if I can figure it out. Then, I’ll open a documentation bug.


(Pepper Lebeck-Jobe) #7

I found it.
So, all gradle build scripts end up being applied using a ScriptTarget inside DefaultScriptPluginFactory:

The ScriptTarget for the settings.gradle file wraps a SettingsInternal typed target, so you can see from the logic above, it’s going to create either a InitialPassSettingsScriptTarget or a SettingsScriptTarget depending on which execution pass Gradle is on. InitialPassSettingsScriptTarget extends from SettingsScriptTarget:

Which tells the DefaultScriptPluginFactory to construct a SettingsScript class to represent the target by returning that class from its getScriptClass() method:

when the DefaultScriptPluginFactory asks for it here:

and here:

The scriptType is used to create ScriptRunner instances based on the scriptType on lines 177 and 192 above.

The ScriptRunner instances end up instantiating instances of the scriptType to run the scripts. Because the SettingScript class extends from DefaultScript …:

… which extends from BasicScript …:

… which implements FileOperations:

… and, because FileOperations defines the files() and file() methods (among others) …:

… you can conclude that you are allowed to use file() and files() directly in your settings.gradle file.

Now, arguably, you are not allowed to use them because the whole FileOperations interface is in an internal package which means Gradle reserves the right to change these implementation details without a deprecation period.

And, it would probably be hard for a user of Gradle to discover how all of this is tied together without walking through the code I just tried to explain above. But, at least we know one way you could find it out.


(Stefan Oehme) #8

They come from SettingsScript extends DefaultScript. These are not part of the public API actually, just some magic we weave into Groovy scripts for convenience.

I think it makes sense to add a statically typed API, but we shouldn’t add all those overloaded methods to Settings. In fact we want to get rid of them on Project in the long run. Instead we could provide something like Settings.getFileOperations() (for lack of a better name).


(uklance) #9

Ah, I had no idea about the weaving. If it’s not in the javadocs it’s hard to discover. It would be nice to access from java too via Plugin<Settings>

Too many times I’ve been burnt by groovy thinking it’d save time when it ultimately end up wasting my time with quirks. I prefer writing gradle plugins in java these days