When different versions of dependencies are declared in the top-level build.gradle and sub-module build.gradle, which one takes precedence?
For example if in my top level build.gradle I had junit:junit:4.8.2 but in a sub-module had junit:junit:4.10 ?
Also, what does declaring a dependency in the top level build.gradle do? Should all dependencies just be declared in sub-modules?
Having
// root build.gradle
subprojects {
dependencies {
compile "junit:junit:4.10"
}
}
and
// submodule build.gradle
dependencies {
compile "junit:junit:4.8.2"
}
is essentially the same as just having
// submodule build.gradle
dependencies {
compile "junit:junit:4.8.2"
compile "junit:junit:4.10"
}
This means you have two version of junit declared in your submodule. Now Gradle conflict resolution kicks in here. The default resolution for
dependency conflicts is to choose the newer version. So junit 4.10 will be picked. To change this behaviour you can configure the resolution strategy.
Related
I am trying to add a subproject to my main one in Android Studio. I have it compiling in the build gradle of the whole project. When ever I try to build the project or compile it it gives me out this error.
Error:(9, 0) Could not find method compile() for arguments [project ':subProject.exude'] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.
Open File
Here is the code for the build.gradle:
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
compile project('subProject.exude')
I think this can be a solution to your problem. Let's say there are two modules in your project, typically named as app and lib. Now you want to use lib module in your app module. So, you need to add it to your build.gradle(app module).
compile project(':lib')
I have it compiling in the build gradle of the whole project.
It may imply two cases:
You're adding compile project('subProject.exude') to your root/project build.gradle
You're adding compile project('subProject.exude') to all of your module build.gradle
In first case, you must not add the compile project to your root build.gradle. Because it not belong there.
In second case, you're incorrectly adding the classpath to your dependencies block in your module build.gradle. This is incorrect, because you're adding classpath for dependencies:
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
compile project('subProject.exude')
...
}
This is corrrect:
dependencies {
compile project('subProject.exude')
...
}
Module dependencies should not exist in root/project build.gradle.
I want to reuse the dependencies defined in this pom.xml file It's quite a long list of dependices. I want to reuse it for convenience.
so I added a line in my build.gradle file like this.:
dependencies {
// unfortunately, Gradle seems just ignore this line.
compile("org.activiti:activiti-ui-root:6.0.0")
}
But it seems that gradle just ingore this line. What's the best way of reusing a pom file if I don't want to rewrite a long list of dependencies?
Please give me some advise, many thanks.
==============================
Thanks, all you guys. finally I find "io.spring.gradle:dependency-management-plugin" helps to solve my problem.
#madhead is right. pom.xml just provide the version of jars, but will not import any file into my project.
That pom.xml does not actually define any dependencies, it only defines versions of artifacts in dependencyManagement block. Thus, depending on this artifact in Gradle does not bring any transitive dependencies in your project (neither it will in Maven, btw).
but it's OK. the version information is enough.
This segment is what I use to solve my problem.
buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath "io.spring.gradle:dependency-management-plugin:1.0.3.RELEASE"
} }
.......
project(':editor-image-generator') {
dependencyManagement {
imports {
// import the pom.xml from outside.
mavenBom "org.activiti:activiti-ui-root:6.0.0"
}
}
dependencies {
// It's good to see that, you don't need to specify the version here.
// with the mavenBom imported above, you can always get the right version.
compile("org.activiti:activiti-bpmn-model")
testCompile("org.activiti:activiti-bpmn-converter")
compile("org.activiti:activiti-image-generator")
compile("org.imgscalr:imgscalr-lib:4.2")
compile("org.slf4j:slf4j-api")
testCompile("commons-io:commons-io")
testCompile("junit:junit")
}
}
That pom.xml does not actually define any dependencies, it only defines versions of artifacts in dependencyManagement block. Thus, depending on this artifact in Gradle does not bring any transitive dependencies in your project (neither it will in Maven, btw).
What you can try in Spring's dependency management plugin for Gradle. It allows to reuse that dependencyManagement block definition for BOMs in Gradle.
If I have module A and module B that both needs a JSON library (say GSON),
and then I have application C which includes module A & B. There is a chance I will get two different versions of the JSON library if module A and B use different versions? Or does gradle remove one of them and just use one of the versions?
What if I have even more modules, updating them to use the same version of a dependency seems like a lot of work. In this case, letting Application C decide which version to use over all modules would be nice (but not always working I guess because of backwards compatability). Anyway to achieve this?
So my questions is how to best handle dependencies which are very common in many modules. Should I have a Json wrapper that I inject in all my modules instead, letting the Application define what to use?
I guess logging could be a similar dependency
Yes. If you specifically ask for different versions of the same library in projects A and B, you might end up with different versions of the same direct dependency.
As to transient dependencies, the default behaviour is to settle on the newest version of the requested dependency. Please mind the word newest as opposed to highest version requested. This is fine as long as the versions are backward compatible with the lowest version your project actually expects.
Luckily, gradle has several built in methods to settle dependency conflicts.
I have written extensively on the subject here: http://www.devsbedevin.net/android-understanding-gradle-dependencies-and-resolving-conflicts/
TL;DR
You may choose to fail on conflicts:
configurations.all {
resolutionStrategy {
failOnVersionConflict()
}
}
Force a specific dependency:
configurations.all {
resolutionStrategy {
force 'asm:asm-all:3.3.1', 'commons-io:commons-io:1.4', 'com.parse.bolts:bolts-android:1.+'
}
}
Prefer your own modules:
configurations.all {
resolutionStrategy {
preferProjectModules()
}
}
Replace all instances of libary X with Y (either a library, a module or a project):
configurations.all {
resolutionStrategy {
dependencySubstitution {
substitute module('commons-io:commons-io:2.4') with project(':my-commons-io')
}
}
}
Exclude all transient dependencies for a specific library and add the necessary libraries by manually:
dependencies {
compile('com.android.support:appcompat-v7:23.1.0') {
transitive = false
}
}
Exclude a specific transitive dependency:
dependencies {
compile('com.android.support:appcompat-v7:23.1.0') {
exclude group: 'com.parse.bolts'
}
}
Force your project to use a specific version regardless of the actual dependency requirements:
dependencies {
compile('com.parse.bolts:bolts-android:1.+') {
force = true
}
}
Consider a Gradle build that has two modules/projects. The settings.gradle file:
include 'modA', 'modB'
Let's say that both use commons-lang3-3.6, and modA uses gson-2.8.1, whereas modB uses gson-2.2.4. One could configure this in the build.gradle file at the root:
subprojects { p ->
apply plugin: 'java'
repositories {
jcenter()
}
dependencies {
compile 'org.apache.commons:commons-lang3:3.6'
}
if (p.name == 'modA') {
dependencies {
compile 'com.google.code.gson:gson:2.8.1'
}
} else if (p.name == 'modB') {
dependencies {
compile 'com.google.code.gson:gson:2.2.4'
}
}
}
The desired config can be confirmed with:
$ gradle :modA:dependencies
$ gradle :modB:dependencies
I am unable to force a version of a dependency using Gradle. My goal is to use version 0.20.0.RELEASE of the Spring HATEOAS library, but despite all my efforts it keeps resolving to 0.19.0.RELEASE.
I have attempted a number of strategies, both in isolation and in combination with one another. These strategies include, but are possibly not limited to, the following (note that in all cases $springHateoasVersionis defined in the gradle.properties file that resides in the directory that is the parent of the directory for the module declaring the Spring HATEOAS dependency):
#1 (in the build.gradle file for the module that declares the dependency)
apply plugin: 'io.spring.dependency-management'
dependencyManagement {
dependencies {
dependency group:'org.springframework.hateoas', name:'spring-hateoas', version:"$springHateoasVersion"
}
}
#2 (in the build.gradle file for the module that declares the dependency)
compile ("org.springframework.hateoas:spring-hateoas:$springHateoasVersion") { force = true }
#3 (in the build.gradle file of the parent directory)
subprojects {
configurations.all {
resolutionStrategy {
force "org.springframework.hateoas:spring-hateoas:$springHateoasVersion"
}
}
}
I have done my best to research this problem:
This question has an accepted answer, but doesn't seem like an exact match for the problem that I'm experiencing: How can I force Gradle to set the same version for two dependencies?
Neither of these questions seem to have accepted answers: 1) Gradle is not honoring resolutionStrategy.force, 2) Forcing a module version has no effect on generated org.eclipse.wst.common.component.
In addition to the fact that my project is broken (because I'm using the wrong version of Spring HATEOAS), I can explicitly see that Gradle is "consciously" selecting the incorrect dependency version despite all my protestations. When I run ./gradlew dependencyInsight --dependency spring-hateoas, I see the following output:
org.springframework.hateoas:spring-hateoas:0.19.0.RELEASE (selected by rule)
org.springframework.hateoas:spring-hateoas:0.20.0.RELEASE -> 0.19.0.RELEASE
\--- project :Commons
\--- compile
Despite the name, the dependencyInsight task provides surprisingly little insight into which rule caused Gradle to select the inappropriate dependency version, let alone how I might go about circumventing said rule.
I found the solution to this problem here. Of course this was the one thing I didn't try because it "didn't seem material". :-/
In order to get things working, I added the following to the build.gradle file of the parent directory (relative to the directory for the module that declared the dependency on Spring HATEOAS).
subprojects {
apply plugin: 'io.spring.dependency-management'
dependencyManagement {
applyMavenExclusions false
}
ext['spring-hateoas.version'] = "$springHateoasVersion"
}
honored by e.g.
allprojects {
repositories {
exclusiveContent {
filter {
includeGroup "com.facebook.react"
}
forRepository {
maven {
url "$rootDir/../node_modules/react-native/android"
}
}
}
}
...
}
ref to https://github.com/facebook/react-native/issues/35204#issuecomment-1304740228
Within my Gradle project I have many different versions of the same dependencies.
For example at my top level gradle build file I have declared a certain version of a dependency, but the sub-modules have delcared a different version.
Ideally I would like to have have the dependencies declared once at the top level so that all modules are using the same version of the dependency.
Is it possible to do this so that all sub-modules are using only the dependencies stated in the top level file?
If so, do I just update the top-level dependencies to the latest version and delete the dependencies declared within the sub-modules?
You can put all your dependencies in a project property in the main build file
ext.libraries = [
commons_io: "commons-io:commons-io:2.4"
, jasperreports: dependencies.create('net.sf.jasperreports:jasperreports:5.1.0') {
exclude group: 'com.lowagie'
exclude group: 'jfree'
}
, cxf: [
'org.apache.cxf:cxf-rt-frontend-jaxws:3.1.4'
, 'org.apache.cxf:cxf-rt-transports-http:3.1.4']
]
and then in the subproject build.gradle
dependencies {
compile libraries.commons_io
compile libraries.jasperreports
compile libraries.cxf
}
Well the simple answer is yes. Maven is also giving similar kind of facility where you should have one parent file where you can declare a < DependencyManagement > block and over there you can define all your dependencies.
Now, every child project or different modules are dependent on this parent project. So, these all modules have to declare dependency without the version, because we have declared this version in < DependencyMangement > block.
So, at the end what we say here is we are declaring the dependency at one location and using it where ever we need it.
Remember here that declaring dependencies in < DependencyManagement > blcok would not actually resolve the dependencies it will just declare it and child modules using < Dependency > can use that w/o defining version.