Dynamic dependency definitions using a project property/configuration

I’m trying to use dynamic dependencies in a project such that different libraries sources are added depending on the customer needs. Currently my idea is that I’ll have a gradle task retrieve a source list from a JSON endpoint and then add the necessary dependencies to the project. I’m having trouble with my order of operations though, because I can’t seem to hook a task up to run before my dependencies block is evaluated.

Here is what my dependencies block looks like:

dependencies {
    compile configurations.customDependencies
    // OR (both lead to similar 'property not defined' errors)
    compile project.customDependencies
}

This is set in a task that looks like the following. For development I’m just pulling from a local JSON file right now. I’ve defined the preBuild task to depend on my retrieveDepencies task to try to get it to run as early as possible, but no luck.

tasks.preBuild.dependsOn('retrieveDependencies')

import groovy.json.*
task retrieveDependencies {
    doLast{
        def sourceManifest = file("${rootProject.buildDir}/sources/manifest.json")
        def slurper = new JsonSlurper()
        def json = slurper.parse(sourceManifest)

        project.configurations.customDependencies = json.collect {
            [
                group: it.get('group'),
                name: it.get('name'),
                version: it.get('version')
            ]
        }
    }
}

The error I get is the following:

* What went wrong:
A problem occurred evaluating project ':app'.
> Could not get unknown property 'customDependencies' for configuration container.

I understand this is caused because the task’s logic is in the execution phase (doLast). But dependencies is defined in the configuration phase. If I remove the doLast block, and perform the logic directly in the task, an error occurs because the project is trying to be evaluated a bit too early (I’m using the android plugin).

Failed to notify ProjectEvaluationListener.afterEvaluate(), but primary configuration failure takes precedence.
java.lang.IllegalStateException: buildToolsVersion is not specified.

Does anybody have any ideas on how something like this would be done?

These post helped me a lot.


One thing I realized I needed to do was hook into the gradle.projectsLoaded event and retrieve the data for my dependencies there. In this closure I could set extra properties on my app project that contained the manifest information.

gradle.projectsLoaded { gradle ->
    def project = gradle.rootProject.allprojects.find { it.name == 'app' }
    project.ext.CUSTOM_DEPENDENCIES = [ 
        [group: 'com.my.group', name: 'awesome_lib', version: '1.0.0']
       // In practice I build this structure from a JSON file.
    ]

Then inside the dependencies block I was able to use that extra property to add dependencies dynamically.

dependencies { 
    project.ext.CUSTOM_DEPENDENCIES.each {
         compile "$it.group:$it.name:$it.version"
    }
 }