Gradle gotcha: duplicate project names

Assume the following project structure:

Product
├──build.gradle
├──settings.gradle
├──FeatureA
│   ├── Foo
│   │   ├── build.gradle
│   ├── Bar
│   │   ├── build.gradle
├──FeatureB
│   ├── Foo
│   │   ├── build.gradle
│   ├── Quf
│   │   ├── build.gradle

I don’t think this is a terribly unreasonable structure: we have a product with a bunch of features, and each feature is split into a couple of modules. This works just fine in gradle. As long as FeatureB/Foo doesn’t have a dependency on FeatureA/Foo. Gradle doesn’t seem to be terribly fond of multi-module projects. At least not when you have a deeper, nested structure.

As soon as FeatureB/Foo has a dependency on FeatureA/Foo, gradle goes belly up. It won’t tell you, though. It’ll just quietly carry on. It won’t resolve the dependency, and you’ll end up with compile errors.

FeatureB/Foo/build.gradle:

dependencies {
	compile project(':FeatureA:Foo')
}

The reason is actually quite simple. Gradle uses the directory name as the project name. Instead of looking at the whole path in the tree, which would be a lot more sensible, it just looks at the last leaf in the tree. So we have two projects named Foo, and gradle chokes. Not giving an error/warning strikes me as a bug, though, but still. Not terribly unreasonable.

Thankfully, there’s a fix. It involves renaming your projects via settings.gradle in the root project:

settings.gradle:

// List our projects/subprojects
include ':FeatureA:Foo'
include ':FeatureA:Bar'
include ':FeatureB:Foo'
include ':FeatureB:Quf'
 
// Rename each child project to its path: Foo becomes FeatureA-Foo or FeatureB-Foo, depending on the Foo.
rootProject.children.each { p ->
        p.children.each { module ->
                module.name = p.name + '-' + module.name
        }
}

Now you can add a project dependency and things will Just Work™:

dependencies {
	compile project(':FeatureA:FeatureA-Foo')
}

All fixed. Much better.

One Reply to “Gradle gotcha: duplicate project names”

  1. Thanks for this write-up. It was exactly the problem I hit in my project and tricky to track down. With a custom configuration name (newConfig), the duplicate project tries to resolve the configuration on itself, and creates an error stating the configuration does not exist in the dependent project.

    Could not resolve all dependencies for configuration ‘:FeatureA:Foo’.
    > Project :FeatureA:Foo declares a dependency from configuration ‘compile’ to configuration ‘newConfig’ which is not declared in the descriptor for project :FeatureA:Foo.

Leave a Reply

Your email address will not be published. Required fields are marked *