dependencySubstitution doesn't seem to work correctly

We’re having some trouble updating our build environment for Android using Gradle 3.5 using the Android Gradle plugin 2.3.0.

Our project structure is pretty straightforward - we have a main application module and several library modules. These library modules can also include other library modules.
One of the requirements is to be able to choose between locally checked out sources and snapshots (generated by continuous integration).

We define our dependencies of all company modules in settings.gradle of each application:

// Define all locally checked out modules
gradle.ext.localModules = [
        "com.company:SomeModule",
        "com.company:CommonModule",
] as String[]

// Define all snapshot modules
gradle.ext.snapshotModules = [
        "com.company:NonLocalModule"
] as String[]


def subp(... names) {
    names.each { fullName ->
        def name = fullName.split(":")[1]
        include 'Libs:' + name
        project(':Libs:' + name).projectDir = new File(settingsDir, '../' + name)
    }
}

subp(gradle.localModules)

In this example SomeModule and CommonModule should be compiled with the local sources and NonLocalModule should be compiled with the latest.integration from our snapshot repository. subp() creates projects for each local module so they appear in our IDE (Android Studio) and are defined as projects (later we replace all snapshots of local modules with these projects).

In our build.gradle file we compile our modules as a project or an artifact depending on the configuration, and we add a custom resolutionStrategy to replace all the artifacts of one of the localModules with the project:

dependencies.ext.compileModules = {
    gradle.localModules.each {
        dependencies.compile dependencies.project(path: ':Libs:' + it.split(':')[1])
    }

    gradle.snapshotModules.each {
        dependencies.compile group + ':' + it.split(':')[1] + ':latest.integration'
    }
}

dependencies {
    compileModules()

    /* Other non-company dependencies */
}

configurations {
    all {
        resolutionStrategy {
            dependencySubstitution {
                if (gradle.hasProperty('localModules')) {
                    gradle.localModules.each {
                        substitute module(it + ":latest.integration") with project(":Libs:" + it.split(':')[1])
                    }
                }
            }
        }
    }
}

To check whether this all worked, we ran gradlew dependencies and it seemed to work correctly. For this example this means that:

  • SomeModule is included as a project and depends on CommmonModule as a project
  • CommonModule is included as a project
  • NonLocalModule is included as an artifact and depends on CommonModule as a project

And it looks something like this:

+--- project :Libs:CommonModule
+--- project :Libs:SomeModule
|    \--- com.company:CommonModule -> project :Libs:CommonModule
+--- com.company:NonLocalModule:latest.integration -> 1234
\    \--- com.company.CommonModule -> project :Libs:CommonModule

Now the issue is that when we add a method in CommonModule and try to use it in SomeModule (or NonLocalModule) the symbol is not found, so for some reason it is still using the artefact.
What did we do wrong, or is there another better way to do this?

What’s the status on this? I have the exact same problem. When I print the dependencies everything seems to work, but the projects do not get compiled and debugging does not work either. All the dependencies (including the substituted ones) get pulled from artifactory (which we use for all our dependencies).

We desperately need this to work correctly as it would simplify our workflow greatly!

We’re also using Gradle 3.5 and Android Gradle Plugin 2.3.3