Home > Net >  Gradle unable to find internal dependency
Gradle unable to find internal dependency

Time:11-18

Problem

I am developing an Android library. This library lives in a multi-module Gradle project with the following structure:

├── legacy
│   ├── src
│   │   └──...
│   └── build.gradle
├── data
│   ├── src
│   │   └──...
│   └── build.gradle
├── feature
│   ├── src
│   │   └──...
│   └── build.gradle
└── settings.gradle

data depends on legacy.

// data/build.gradle
implementation project(":legacy")

feature depends on data and legacy.

// feature/build.gradle
implementation project(":legacy")
implementation project(":data")

feature, data, and legacy are all Android library modules. feature is the published Android library. data and legacy are not used outside this project, and are therefore not published anywhere.

My Android application depends on the published feature AAR artifact.

implementation "feature:<version>" // package omitted for obfuscation

Gradle has no problem finding the feature library. However, Gradle throws an error because it wants to find data and legacy.

Execution failed for task ':mobile-app:mergeDevDebugNativeLibs'.
> Could not resolve all files for configuration ':mobile-app:devDebugRuntimeClasspath'.
   > Could not find feature:data:unspecified.
     Searched in the following locations:
       - ... // omitted
     Required by:
         project :mobile-app > feature:<version>

I don't think it matters, but the mobile-app project is using a locking plugin, Nebula's Gradle Dependency Lock Plugin.


Attempted fixes

I'm a little confused why this error is happening at all. I expected the classes of legacy and data to be included in the feature AAR. That is clearly not the case. If that can be done, I prefer that solution.

Here are some dependency variations I've tried so far.

// feature/build.gradle

// doesn't work; same error
api project(":legacy")
api project(":data")

// doesn't work; mobile-app doesn't complain about missing artifacts, but fails to compile:
// cannot find symbol class <class in legacy>
compileOnly project(":legacy")
compileOnly project(":data")

I can successfully compile and run mobile-app if I use dependency substitution to replace the feature AAR with my local copy of the code repo.

// settings.gradle of mobile-app project
includeBuild('../feature') {    // this is the local relative path to the project
    dependencySubstitution {
        substitute module('feature') using project(':feature')
    }
}

However, this is not an appropriate solution to publish and subsequently enforce upon the entire dev team.

CodePudding user response:

Gradle modules map 1:1, more or less, with Java/Android libraries.

Adding a dependency (implementation or api) to a library does not cause that dependency to get compiled into the library. Otherwise you would get class path collisions if you depended on two libraries which themselves depended on the same library as each other.

Instead, adding a dependency to a library adds that dependency to the library's POM and/or gradle.module file, which gets published along with the JAR or AAR to the repository. Consumers of the library (i.e. other Gradle or Maven projects, like your app) then parse that POM, and thus know which additional dependencies to download.

The way I see it, your options are:

  1. Publish data and legacy to a repository your app can consume, just like feature. Your app won't consume them directly, but they'll be there for Gradle to download when it sees that feature requires them.
  2. Move the code from data and legacy inside of feature (and mark it all as Kotlin internal or Java package-private to prevent it from being public API).
  3. Refactor feature to publicly define all of its data sources as interfaces. Move the data and legacy modules into your app project and refactor them to provide the necessary concrete implementations of feature's required interfaces.
  • Related