Resolve dependencies in project with Gradle-Modules.

Answered

Hi,

We are checking the use of Grade instead of Maven. But the Gradle integration in IntelliJ seems to leak one major feature that the Maven integration provides: Resolve dependencies in project.

Background: We are developing several libraries with dependencies between each other. The libraries are located in separate GIT repositories and are mainly developed independently. However, sometime a new feature requires changes in several libraries.

With Maven this is not a problem: Create a project and import the libraries as Maven modules. When the version number fits, the project modules are resolved instead of the Maven repository binaries. So “Go to source” and refactoring are working well.

But the same scenario with Gradle is not working: only the Maven repository binaries are resolved and so there is no support to go to the actual code or to perform a refactoring.

To get a similar behaviour as with Maven, I only see the “composite builds” feature of Gradle. But this has several disadvantages:

  •  In the “settings.gradle.kts” there must be the path to the other build. As the libraries are in several GIT repositories, every developer has to check it out in an equal folder hierarchy to let it work. Also each developer has to check out all GIT repositories, even if he only works on one library module.
  • Also the version number seams not to be respected: the referenced build is always used, even if the given version number in the dependency declaration is different. The Maven repository binaries should be used in such a case.

Is my observation correct, or did I something wrong. If I am right, are there any plans to improve the Gradle integration in IntelliJ to fix this?

11 comments
Comment actions Permalink

Note: Images and example projects available at:

https://cloud.ivi.fraunhofer.de/s/L4ZR4DMa5YwdGqN

With Maven

There are two libraries: “lib1” and “lib2”. “lib1” is dependent of “lib1”, but normally developed independently from “lib1”.

You can see the two Maven projects in the images “1 - with Maven - lib1.png” and “2 - with Maven - lib2.png”. Both were published to the local Maven artefact repository.

As you can see, “lib2” loads the dependency “lib1” from the artefact repository.

Now for a large refactoring both libraries are loaded into a new empty project:

[File]->[New]->[Project…]->[Empty Project]

Both libraries are added as modules to the project (see “both” project):

[File]->[New]->[Module from existing sources]

The dependency “lib1” of “lib2” is now automatically resolved in the project and not loaded from the artefact repository (see “4 - with Maven - both projects.png”). Therefore, inter-module refactoring and “go to source” is working for both libraries.

Additionally, if I change the version of “lib1” (e.g. 2.0.0), IntelliJ is so smart, that it now resolves “lib1” from the artefact repository in the “lib2” module after a new Maven synchronisation (see image “5 - with Maven - both projects - 2.png”

 

Now the same with Gradle

You can see the two Gradle projects in the images “6 - with Gradle - libG1.png” and “7 - with Gradle - libG2.png”. Both were published to the local Maven artefact repository.

As you can see, “libG2” loads the dependency “libG1” from the artefact repository.

Now for a large refactoring both libraries are loaded into a new empty project:

[File]->[New]->[Project…]->[Empty Project]

Both libraries are added as modules to the project (see “bothG” project):

[File]->[New]->[Module from existing sources]

But now IntelliJ does not recognized that the dependency “libG2” of “libG1” is in the same project and therefore IntelliJ loads “libG1” from the artefact repository (see image “8 - with Gradle - bothG.png”). Therefore, inter-module refactoring and “go to source” is NOT working!

Is there a way (in Gradle) to keep the single library projects independent from each other, but also have a project where both libraries are imported as modules, where inter-module refactoring and “go to source” is working?

1
Comment actions Permalink

Thank you for the sample projects!

Currently such setup is not supported for Gradle projects indeed. There is however a hidden option that you may try to set: 

external.system.substitute.library.dependencies = true

via Help | Edit Custom Properties and restart. This option however has not been tested for quite some time, but it should support following cases:

GradleProject1 depends on GradleProject2
MavenProject1 depends on GradleProject2

The corresponding request for this is https://youtrack.jetbrains.com/issue/IDEA-134885

1
Comment actions Permalink

IDE will resolve dependencies to module sources if you specify dependency as a project dependency type in dependencies section.

How exactly you specify such dependencies?

0
Comment actions Permalink

The dependencies are defined like 

   compile("org.gradle.sample:myproj:1.0.0").

You mean

   compile(project(":myproj"))

 

But this needs that the libraries are always in the same IntelliI project.

But as I said: The libraries are mainly edited isolated. In this case depended libraries are loaded from the Maven Repository.

Only for big new features several libraries are edited in parallel.

0
Comment actions Permalink

>But this needs that the libraries are always in the same IntelliI project.

As with Maven: you need to have the modules present in same project for IDE to configure the dependency on a module instead of a library jar.

0
Comment actions Permalink

You missed the word "always". I want to edit a single library in a project that only contain this single library code (that's why they are in separate GIT repositories). But additionally I want to have a project were I have references to several of the libraries to implement inter-library features and do refactoring.

0
Comment actions Permalink

I will prepare some example projects and screenshots to explain what I mean by the end of the week...

0
Comment actions Permalink

Andrey Dernov, any ideas?

0
Comment actions Permalink

Cool! At least for my example projects it's working! Thank you! Now we could start transforming our projects to Koltlin Multiplatform projects (which has no Maven support until now...)

Just one problem I have noticed: If the version number does not match, it still resolves the dependency in the project (which it should not) after a new sync. In Maven its working correctly (see my comment above). With a changed groudID or artifactID it is correctly not resolved in project after a new sync.

 

0
Comment actions Permalink

>Just one problem I have noticed: If the version number does not match, it still resolves the dependency in the project (which it should not) after a new sync. In Maven its working correctly (see my comment above). With a changed groudID or artifactID it is correctly not resolved in project after a new sync.

I have added your feedback to YouTrack issue. Please follow it for updates.

0
Comment actions Permalink

There is also the possibility to implement the substitution in a master build script where you look for the modules you would like to substitute with local gradle subprojects. Gradle provides such functionality out-of-the-box by altering the dependency resolution and this might also be the mechanism that IntelliJ is using behind the scenes to achieve the same goals. Here is a snippet of some code we use to replace dependencies with gradle subProjects. This way you are not forced to use IntelliJ to run those gradle tasks/tests where you need to substitute the modules with subProjects and the solution works outside of IntelliJ too.

allprojects {
configurations.all {
resolutionStrategy.dependencySubstitution.all { DependencySubstitution dependency ->
if (dependency.requested instanceof ModuleComponentSelector
&& dependency.requested.group in ["my.group1", "my.group2"]) {
def targetProject = findProject(searchModuleByName(dependency.requested.module))
if (targetProject != null) {
dependency.useTarget targetProject
}
}
}
}
}

def searchModuleByName(String name) {
HashMap<String, String> mapping = new HashMap<>()
mapping.put("libName", ":mygradle:subProject")
...
return mapping.get(name)
}

 

0

Please sign in to leave a comment.