Common configuration issues with multiproject

Hi,

I’m having problems defining some common configuration within a multiproject setup.

Here’s the subproject part of the parent build script:

subprojects {
    repositories {
        maven { url "http://my.repository/content/repositories/releases/" }
        mavenCentral()
        mavenLocal()
    }

    apply plugin: 'java'

    defaultTasks 'clean', 'buildModule'
    task buildModule() {}
}

Here’s one of the subproject build scripts:

dependencies {
    runtime "com.company:dependency-1:1.0.0:@zip", "com.company:dependency-2:1.0.0:@zip"
}

def configureDependencies(file)  {
    return tasks.create(name: "${file.name}", type: Copy) {
        from zipTree(file)
        into(OUTPUT_DIRECTORY)
    }
}

buildModule {
    configurations.runtime.getFiles().each { file ->
        configureDependencies(file)
        dependsOn("${file.name}")
    }
}

I have a number of subprojects that have identical configuration to the above, except for the subproject dependencies. I want to avoid having to duplicate the configureDependencies method and the buildModule task configuration in every subproject.

I’ve tried moving configureDependencies as an extension property into the parent, but this fails to resolve the subproject task paths. Similarly moving the buildModule configuration into the parent fails saying it cannot change the dependencies of the subproject runtime configuration after it has been resolved.

Can you suggest the best way to simplify this and avoid duplication? I’d like to maintain the subprojects as some may require additional configuration.

Thanks

You are resolving dependencies at configuration time. This will slow down all your builds, even if the dependencies are not needed and also leads to the other problem your described.

What you probably want is:

task extractDependencies(type: Copy) {
  inputs.files configurations.runtime
  into OUTPUT_DIRECTORY
  from configurations.runtime.collect { zipTree(it) }
}

buildModule.dependsOn(extractDependencies)

You’d have to add it as an extension to all sub-projects, so it will be executed in their context. But with the above solution it’s no longer necessary.

Thanks Stefan. This has helped speed things up.

One follow up question. If I place this into the parent project build file, and the dependencies in each subproject, I get the following error:

A problem occurred evaluating project ':subproject1'.
> Cannot change dependencies of configuration ':subproject1:runtime' after it has been resolved.

I guess the task is resolving the runtime dependencies of the subproject1 during configuration. Perhaps this can be delayed until the subproject1 dependencies are defined somehow?

Thanks

Yes, my bad. The example should have wrapped the copy inputs in a Closure to defer the computation until the task is actually about to run.

task extractDependencies(type: Copy) {
  inputs.files configurations.runtime
  into OUTPUT_DIRECTORY
  from { configurations.runtime.collect { zipTree(it) } }
}

Small difference, big effect :wink:

Brilliant. Works a treat now. Thanks.

Now to read up a bit on lazy configuration.