Reference copyspec variable in parent that is defined in children?

I have a multi-module gradle project where the children repackage their dependencies with things like configuration. All of these children will define a copyspec variable like so:

def fileSpec = project.copySpec { ... }

Then I want to create both a jar and an rpm that use this filespec:

jar {
    with filesSpec
}

ospackage {
    packageName = project.name
    version = project.version.toString().replace '-', '.'
    arch = I386
    os = LINUX
    with filesSpec
}

This pattern will be identical for all of the subprojects, so I would like to be able to move those tasks to the subprojects closure in the parent build.gradle. While the behavior is the same, the content of the filespec is specific to the subproject. Is there a way that I can achieve what I want? E.g., is there a way that I can declare the filesSpec variable in the parent’s subprojects closure and then define it in the subproject itself so that I don’t get an error about the unknown variable in the parent? Then I could abstract the jar and ospackage tasks to the parent to avoid code duplication.

You don’t want a plain variable here, the variables are only visible within the local scope.

You want to add an extra property to the project.

Something like this should work:

// in root build.gradle
subprojects {
   ext.filesSpec = copySpec {}
}

// in subproject's build.gradle
configure(filesSpec) {
   // configure the CopySpec
}
1 Like

Thank you! This worked perfectly. If you don’t mind, could you explain what’s happening, exactly? How is the configuration in the subproject happening in time to satisfy the configuration of the parent?

The parent is evaluated first, which creates filesSpec in each subproject. The Copy-like task’s with() method just add the filesSpec to the list of CopySpecs to use later.

When the subproject is evaluated, filesSpec already exists, so you’re just configuring an existing CopySpec. So when it’s time for one of the Copy-like tasks to execute,filesSpec is already configured by the subproject.

Alternatively, I think this could also work (in each subproject) without a filesSpec in the parent:

configure([ jar, ospackage ]) {
   // configure copySpec
}

I think that would only be preferable if it was likely that the contents of the jar and ospackage were going to be different eventually because you could split them.

You just have to be careful that extra properties don’t get out of hand and become too magical.