Issues with diamond Dependency with git submodules

I’m very new to Gradle, and I am making an app, let’s call it A, which depends on 2 other Gradle modules, called B and C. Both modules B and C depend on a fourth module, called D. B and C are git submodules of A, and D is a git submodule of B and, separately, C.

The file structure looks like this:

A
| settings.gradle.kts
| build.gradle.kts
| src
|-B
| | settings.gradle.kts
| | build.gradle.kts
| | src
| |-D
| | | settings.gradle.kts
| | | build.gradle.kts
| | | src
|-C
| | settings.gradle.kts
| | build.gradle.kts
| | src
| |-D
| | | settings.gradle.kts
| | | build.gradle.kts
| | | src

A/settings.gradle.kts:

rootProject.name = "A"
includeBuild("B")
includeBuild("C")

B/settings.gradle.kts:

rootProject.name = "B"
includeBuild("D")

C/settings.gradle.kts:

rootProject.name = "C"
includeBuild("D")

D/settings.gradle.kts:

rootProject.name = "D"

the build process fails before reaching any of the build.gradle.kts

When building B or C alone, there is no issue, but when I build A, I receive the error message Module version '<domain>:D:<version>' is not unique in composite: can be provided by [project :B:D, project :C:D].

So I’m here to ask help on how to solve this issue

A couple things I would like to note:

  • All modules must be able to be built as standalones
  • I’m the owner of all of the repositories, so I can make changes to any of them
  • I would rather keep any source code or binaries private, so I would avoid uploading them to any publicly accessible repository (they are already in a private GitHub repo)
  • I also want to keep the repositories liked, as they are developed separately, so updating the sources across the projects is easy and consistent
  • Cloud CI/CD is not an option

Thanks a lot for the help

  • Alex

Update:

I’ve found a solution, although it isn’t the best:
I’ve surrounded every includeBuild("D") with a check if the parent is null, so if it isn’t, it will not include module D.

The issues with this solution are:

  • A MUST include module D manually, or else it won’t work
  • Since I added a copy of D in the root folder (to keep things neat) , the copy of D within each module B and C is unused

Apart from that, everything seems to work quite well, and there’s no issues with any of the build.gradle.kts files.

However, if you have a better solution, please feel free to share! It would be very appreciated.

Thanks in advance,

  • Alex

If it were diamond you wouldn’t have a problem. The point is, that you do not have a diamond. Those two Ds are separate builds in separate directories as far as Gradle is concerned. Much like if you would just copy the directory which you effectively do. So you have two individual builds in the graph that produce the artifacts with the same coordinates and that’s a problem. Those two Ds also are not necessarily equal, you could have different commits in the two directories. If you would for example includeBuild the same directory with D from B and C, you would not have any problem. So you could for example in C check whether ../B/D is present and then include that, otherwise the own D or similar. But you should probably also add some check, that both work with the same state of D.

Thank you very much for your reply - it now makes a lot of sense.

However, I would like to ask you what would you recommend as a way to avoid this problem, avoiding hard-coded checks (e.g. checking if the parent module includes module D), even if it requires significant restructuring (maybe a mavenLocal?).

Again, thanks for the help,

  • Alex

As I said, with included builds it will probably only work if you make sure to include the same filesystem location, otherwise they are effectively different builds.

Yes, of course you could use mavenLocal or some dedicated local repository, but then you always have to build D manually if you changed something and also do not have it open in the IDE alongside the other projects automatically. If you use mavenLocal, make sure to use some narrow repository content filtering to mitigate the problems you typically earn from using it