This question already has answers here:
What's the difference between implementation, api and compile in Gradle?
(12 answers)
Closed 1 year ago.
I have a project A which depends on library B, I am using gradle composite builds.
Project B contains several common dependencies such as "org.apache.commons:commons-lang3"
Project A uses "org.apache.commons:commons-lang3" as well but transitive dependencies resolution does not work as I would expect, I have to declare again "org.apache.commons:commons-lang3" in the dependencies block of project A build.gradle in order to make it work.
Project A build.gradle:
group = 'org.example.app'
version = '0.1.0'
plugins {
id 'java'
id 'application'
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(11))
}
}
dependencies {
implementation 'org.example.libs:B'
}
Project A settings.gradle
includeBuild '../../libs/B'
Project B build.gradle:
group = 'org.example.libs'
version = '0.1.0
plugins {
id 'java-library'
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(11))
}
}
dependencies {
implementation 'org.apache.commons:commons-lang3:3.12.0'
}
repositories {
mavenCentral()
}
Project B compiles well as standalone but I can't compile project A without adding again
"org.apache.commons:commons-lang3:3.12.0" in its build.gradle. Isn't it supposed to be resolved as transitive dependency from project B ?
Project A Compilation throws errors such as :
error: package org.apache.commons.lang3 does not exist
What am I missing ?
This probably has been answered many times, but I cannot find a good answer.
To make transitive dependencies available, you have to use api rather than implementation in project B.
dependencies {
api 'org.apache.commons:commons-lang3:3.12.0'
}
If you have previous experience with maven, you'll need to unlearn a lot of stuff as gradle is a very different beast and reading the manual is well worth it (even if it's a bit tedious)
Related
The essence of the problem.
I have several services.
Starter - to start and stop the rest
Service_for_calc - for calculating some operations
Service_sample - service for example
Common_Service-a service for storing models and utils
According to my idea, I will run a starter that will run the rest of the services. Each service will have the same endpoints and models. For example, take the WhoIs functionality.
Each service must tell you who it is.
I don't want to create a model and #Service in every service (module).
I want to create this in Common_service and just import the ready-made logic.
I tried to do this via gradle
to do this in the root settings. gradle I wrote
include 'Service_for_calc'
include 'Common_Service'
include 'Service_sample'
include 'Starter '
and in the service (module) I prescribed it
implementation project(':hub.common')
But I ran into some problem, I can't even describe it clearly, because each time it looked different, but here are the errors that I got when trying to work this way
The module does not see classes from common or does not see the package (despite the fact that the IDE began to suggest them to me)
Some kind of trouble started with the dependency (specifically, Spring dependencies will start working every other time)
Sometimes gradle did not see and threw an error on the implementation project (': hub. common'), with the error that there is no such project ( the name was correct)
After I removed the dependencies, reloaded Gradle and installed it, it worked, but when I tried to open this project on someone else's computer, point 1 was repeated
Maybe I'm doing something wrong, or maybe I shouldn't do it at all.
Is this approach practiced? Was it worth doing it via gradle or should I try it via classpath?
Is it worth doing whole services in a common project?
I will be glad to have a detailed answer!
You have module name conflict, if you have a module named include 'Common_Service' then you should implementation project(':Common_Service')
PS: Here git repo with multi-module, maybe this helps you.
I solve it by root build.gradle
buildscript {
ext {
springBootVersion = '2.4.4'
dependencyManagementVersion = '1.0.11.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
classpath "io.spring.gradle:dependency-management-plugin:${dependencyManagementVersion}"
}
}
allprojects {
group = 'omegabi.back.hub'
version = '0.0.1-SNAPSHOT'
}
subprojects {
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
sourceCompatibility = 11
repositories {
mavenCentral()
}
dependencies {
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation 'org.springframework.boot:spring-boot-starter-test'
}
}
project(':hub.sample_service') {
dependencies {
compile project(':hub.common_service')
}
}
I solved this problem by deleting settings.gradle in every project except the root one!
I am new to both Gradle and JavaFX. I have added the JavaFX plugin to my build.gradle following this and this. However, my main class Library.java is not able to detect the Application class of JavaFX when I am trying to extend it.
build.gradle
plugins {
// Apply the java-library plugin to add support for Java Library
id 'java-library'
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.8'
}
repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
mavenCentral()
}
dependencies {
// This dependency is exported to consumers, that is to say found on their compile classpath.
api 'org.apache.commons:commons-math3:3.6.1'
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
implementation 'com.google.guava:guava:28.0-jre'
// Use JUnit test framework
testImplementation 'junit:junit:4.12'
}
jar {
manifest {
attributes 'Main-Class': 'Chess.Library'
}
}
mainClassName = 'Chess.Library'
Screenshot of Library Class
There's no Application from javafx package at all. What am I missing here?
I am using Spring Tool Suite 4.0 as my IDE with Buildship Gradle plugin if that's of any help. I am also running on Oracle Java 13
Edit 1:
I have added the changes suggested and this is how my build.gradle now looks
plugins {
// Apply the java-library plugin to add support for Java Library
id 'java-library'
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.8'
}
repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
mavenCentral()
}
dependencies {
// This dependency is exported to consumers, that is to say found on their compile classpath.
api 'org.apache.commons:commons-math3:3.6.1'
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
implementation 'com.google.guava:guava:28.0-jre'
// Use JUnit test framework
testImplementation 'junit:junit:4.12'
}
jar {
manifest {
attributes 'Main-Class': 'Chess.Library'
}
}
javafx {
version = "13"
modules = [ 'javafx.controls' ]
}
mainClassName = 'Chess.Library'
But the problem is still there
I also checked my Project and External Dependencies, there are all the libraries except for javafx
I fixed the issue myself although not sure what was causing it, but my project's buildpath had an unbounded Java 13. Fixing that and restarting the IDE took care of it
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
I'm pretty new to Gradle and am having an issue getting my a module that is dependant on another module to build properly.
So I have the following configuration for my modules.
subprojects {
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
testCompile 'junit:junit:4.10'
}
}
project(':web-service') {
apply plugin: 'war'
dependencies {
compile project(':core')
compile('com.sun.jersey:jersey-server:1.7')
compile('com.googlecode.json-simple:json-simple:1.1.1')
}
}
project(':core') {
dependencies {
compile('log4j:log4j:1.2.17')
}
}
If I try to build my core project everything succeeds as expected.
However, if I try to build the web-service project with the following command:
gradle :web-service:build
It appears to build the core project first as expected but then encounter build errors that indicate that classes that exist in the core module cannot be found.
What gives?
Turns out this was completely my fault. I dug deeper on the error messages that I was getting and found some package does not exist messages at the top. Turns out that my directory structure was not inline with my package names.