This message was deleted.
# plugin-development
s
This message was deleted.
a
I think it depends on whether it's a FileCollection (which can contain anything) or FileTree (which have a customisable root dir). But in practice it's usually relative to the current project dir. So if you have a project
/full/path/to/my-project
with a subproject
/full/path/to/my-project/subproject-x/
that has a task that creates an output file
/full/path/to/my-project/subproject-x/build/foo/output.txt
then
RELATIVE
will relativise it to be
build/foo/output.txt
m
So if I have a set of source files under
~/java
, 2 builds in 2 different directories won't be able to share their build cache?
Copy code
$HOME/java  # <- sources
$HOME/project
$HOME/otherDir/cloneOfProject
If I do
configurableFileCollection.from("$HOME/java")
, I think I'd expect the normalization to work relative to
"$HOME/java"
, irrespective of my checkout folder
a
maybe, if you created a FileTree with the root dir set to be
~/java
and then added that to your CFC
or maybe the task input needs to be a ConfigurableFileTree, and you set the rootdir to be
~/java
👀 1
m
But then what if I do:
Copy code
configurableFileCollection.from("$HOME/java")
configurableFileCollection.from("$HOME/java/dir/Foo.java")
Is it going to throw?
I'm almost sure you can have
configurableFileCollection
from multiple
FileTree
, it'd be quite the limitation otherwise
a
do you mean throw an exception?
m
I would expect the normalizer to throw an exception if the same path is reachable through 2 roots like in
Copy code
configurableFileCollection.from("$HOME/java")
configurableFileCollection.from("$HOME/java/dir/Foo.java")
a
I think your code would add both the dir
~/java
and the file (assuming you're setting the home dir manually because I don't think
$HOME
works in Java)
I'd be surprised if it did throw an exception
m
I think your code would add both the dir
~/java
and the file
So it's going to compile my
Foo.java
file twice? Maybe... I can try
a
I think it will just de-dupe the file
m
But if it "dedupes", what is the normalized path?
Given
Foo.java
is now reachable via 2 roots
a
configurableFileCollection.from("$HOME/java")
will add
Foo.java
and then the second operation will 'overwrite' the first with the second
m
Use the last?
Most probably. I guess it's undefined behaviour right now
a
oh, I think the normalization is done at a different phase, so maybe we're talking past each other
m
https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/PathSensitivity.html#RELATIVE
Copy code
The property is an input directory. 
* The path of the files in the input directory are considered relative to the input directory.
I guess my question is: what if the property is a
ConfigurableFileCollection
a
at some point Gradle inspects the task inputs to determine the fingerprint of the input. And if it sees a collection of files it will fingerprint it based on the path sensitivity. But that's unrelated to how files get added into a CFC, which I assume works as a 'last file wins' kind of thing
yes, the Javadoc phrasing is ambiguous 😬
m
But that's unrelated to how files get added into a CFC
I don't think it is because I would expect the normalised path to be the relative path inside the CFC.
Unless like you said in the beginning, the "root" of a CFC is always
project.rootDir
but I'd find this quite surprising
a
I think a CFC will contain Java File objects, so there won't be a 'root' dir, and the files are added as plain Java Files - no modification. A FileTree is a subtype of a FileCollection, with an additional property: the root dir. So the path sensitivity setting doesn't have any input how a CFC works. There's no path norming until Gradle starts fingerprinting task inputs.
m
Interesting. So the below would make no sense, right?
Copy code
@get:InputFiles
  @get:PathSensitive(PathSensitivity.RELATIVE)
  abstract val files: ConfigurableFileCollection
a
that makes sense to me :) You've got a collection of files that might contain directories or files. At some point Gradle will traverse the directories and get the files contained. So if you add these to a CFC:
Copy code
/full/path/java/
/full/path/java/Z.Java
Then you'll have a CFC that contains some files:
Copy code
/full/path/java/some/package/X.Java
/full/path/java/some/other/package/Y.Java
/full/path/java/Z.Java
And then when the task run Gradle tries to fingerpint the task inputs. Gradle knows which subproject owns the task, so it knows the project directory. And Gradle can also see the pathsensitivity setting. So it will get all the files in the CFC, and if the path sensitive is RELATIVE then it will be something like this.
Copy code
../../../java/some/package/X.Java
../../../java/some/other/package/Y.Java
../../../java/Z.Java
Then it will hash the paths (and the file contents) to get the fingerprint
at least, that's my working theory! I could be wrong
m
if the path sensitive is RELATIVE then it will be something like this
Fair assumption. My issue if it works like this is that if I clone my repo in a subdir, the normalized paths will now become (notice the extra
../
)
Copy code
../../../../java/some/package/X.Java
../../../../java/some/other/package/Y.Java
../../../../java/Z.Java
And my build cache doesn't work any more
a
yes, that makes sense to me
m
I'll run a few experiments and report back
a
maybe if you create a FileTree
Copy code
objects.fileTree().apply { 
    this.setDir("$HOME/java")
}
and add it to the CFC then Gradle will be cleverer - but I suspect it will just treat the FileTree as a list of java.io.Files and you'll have the same problem - the CFC will contain a lot of files (with absolute paths dependent on the project location) that will later be normalized. Maybe if you have a DirectoryProperty as a task input then Gradle will normalize the files contained in the dir relative to the provided dir? (Similar for a ConfigurableFileTree). Pure guesses. I have no idea what will actually work. I'm curious what you find!
m
So it turns out I can share my build cache so Gradle is clever . (gist)
a
great! Haha so all of my theorising was nonsense?
m
I think a CFC is "rooted"
Which is kind of surprising but also handy at the same time
If I do
Copy code
cfc.from("/Users/mbonnin/tmp/sub/b.java")
cfc.from("/Users/mbonnin/tmp2/b.java")
I can also share the build cache so looks like the normalized path is "b.java" for both and Gradle is smart enough to know that order doesn't matter
Actually, let me try to swap the files
Yep, I can swap the files fine
Also, CFC is deduped indeed:
Copy code
val ft = objects.fileTree().from("/Users/mbonnin/tmp")
cfc.from(ft)
cfc.from("/Users/mbonnin/tmp/sub/b.java")
So I'm guessing if the same file is reachable through 2 roots, it's only a matter of having a predictable algorithm like "always use shorted normalized path"
a
I think this is the relevant bit code that fingerprinting files when RELATIVE is used https://github.com/gradle/gradle/blob/v8.5.0/platforms/core-execution/snapshots/src/main/java/org/gradle/internal/fingerprint/impl/RelativePathFingerprintingStrategy.java#L57-L82 I think it works by iterating through all of the files within any file collection and will compute a common root-dir shared between all of them, and then use that to compute the relative paths of all files to the root dir. So you could move the directory
~/java/
anywhere on the filesystem - the relative paths would still all be the same relative to the new location of
<...>/java/
.
thank you 1
m
FWIW, the roots is gotten from https://github.com/gradle/gradle/blob/fc63c5e5edb023351e25c73188fa0d9386972b0e/sub[…]internal/fingerprint/impl/DefaultFileCollectionSnapshotter.java My understanding is that internally
FileCollection
contains
FileTrees
and the roots of those
FileTrees
are used for normalization. Alas I haven't found a way to access those roots using only public API
1