Chaining together file/directory providers

I thought that one of the things I’d be able to do with directory and file Propertys and Providers is to be able to chain them together and determine the root of the structure later.

For example, here’s a build.gradle snippet:

def dirA = layout.directoryProperty()
def dirAB = dirA.dir("B")
def dirABC = dirAB.dir("C")
def fileABCD = dirABC.file("D")

dirA.set(new File("/foo/bar"))
println "fileABCD ==> ${fileABCD.dump()}"
println "fileABCD = ${fileABCD.get.asFile}"

The goal here is to be able to set up fileABCD as occurring at B/C/D under the root, but to be able to defer what that root is. There might be other things under B and C as well, which is one reason why the path isn’t constructed all at once. Once dirA has a value set to /foo/bar, we should be able to find out that fileABCD is at /foo/bar/B/C/D. And if we actually reset dirA to be /baz/qux, we would then get a value of /bar/qux/B/C/D for fileABCD.

Is this not possible? Can I only set up path immediately off of the DirectoryProperty objects?

1 Like

@Daniel_L I think this is a very good point and I’m surprised that DirectoryProperty.dir doesn’t return a DirectoryProperty again. Only one level of chaining is possible, which is very limiting.

1 Like

You are right @rjbell4. We should be able to chain relative directories based on other. At the moment you could try to use Provider#map method. We are missing something like DirectoryProvider that would have specialized method for dir and file. Those basically “map” the provider to another DirectoryProvider. I opened this issue to track this feature.

@Daniel_L I looked at that a bit before, but failed. This seems to be working, though:

def subdirTransformer(subdir) {
    new Transformer<Directory, Directory>() {
        Directory transform(Directory parentDir) { parentDir.dir(subdir) }
    }
}

def fileTransformer(filename) {
    new Transformer<RegularFile, Directory>() {
        RegularFile transform(Directory parentDir) { parentDir.file(filename) }
    }
}

def dirA = layout.directoryProperty()
def dirAB = dirA.map(subdirTransformer("B"))
def dirABC = dirAB.map(subdirTransformer("C"))
def fileABCD = dirABC.map(fileTransformer("D"))

dirA.set(new File("/foo/bar"))

println "fileABCD = ${fileABCD.get().asFile}"

Interestingly, I did try to do the same with just closures instead of explicit Transformers (I’m not 100% sure how that works, but it seems like it does), but was not able to get that to work, as it seems to automatically get() the Provider that I was trying to call map on, thereby resolving it before it’s ready. It’s a shame, because using closures would have been more terse (an “inline” solution), and not a bad alternative to baked-in support.

As it is, the create-a-Transformer approach works, but feels a bit like rolling my own infrastructure.

You are right @rjbell4. It should be working. It seems this is related to this issue. For now, your workaround is appropriate but not ideal.

If new Provider is created with something like this:

def fileABCD = provider {
    dirA.get().dir('B').dir('C').file('D')
}

are there any drawbacks?