I am having a problem building some interdependent modules in a gradle based project. What I want to be able to do is have a project (similar to some maven projects I have worked with), where the dependencies are on each others versions, so when deployed to a maven repository, are available, but building a module builds the dependencies.
To illustrate, I have a gradle project with a few submodules.
ProjectA
|- module-a
\- module-b (depends on module-a)
In module b, I specify the dependency using compile "{groupId:module-b:{versionName}, and in the project root build.gradle, I have
project(':module-b') {
dependencies {
compile project(':module-a')
}
}
Another project with a dependency on the other project.
ProjectB
\- module-c (depends on module-b)
When building ProjectB, there are errors about not being able to find ProjectA:module-a:unspecified. The pom generated for module-b does include that dependency.
If I remove the dependency specification in ProjectA's root build.gradle, when I try and build, module-b cannot find module-a (during the dependency resolution phase). module-a would not be deployed anywhere yet, so this makes sense to me.
I would like module-b to depend on it's sibling, but have it's pom show a maven-style version dependency, so that module-c can depend on module-b and transitively get module-a.
(There is no requirement that ProjectB be a gradle project. Either maven or gradle should work.)
Gradle configuration files
settings.gradle
include 'module-a', 'module-b'
build.gradle
project(':module-b') {
apply plugin: 'java'
dependencies {
compile project(':module-a')
}
}
module-a/build.gradle
group 'com.example.coatedmoose'
version '1.0.0-SNAPSHOT'
apply plugin: 'java'
apply plugin: 'maven'
uploadArchives {
repositories {
mavenDeployer {
mavenLocal()
}
}
}
module-b/build.gradle
group 'com.example.coatedmoose'
version '1.0.0-SNAPSHOT'
apply plugin: 'java'
apply plugin: 'maven'
dependencies {
compile 'com.example.coatedmoose:module-a:1.0.0-SNAPSHOT'
}
uploadArchives {
repositories {
mavenDeployer {
mavenLocal()
}
}
}
Related
I've got a Gradle multi-project build for a custom Spring Boot Starter. Following Spring Boot Starter convention, my classes are all in an 'autoconfiguration' project, and I have a separate 'starter' project that ONLY brings in the dependencies needed to use the autoconfiguration.
The multi-project build produces 2 non-runnable jars, 1 for the autoconfiguration sub-project, and one for the starter sub-project. My new project that is using this starter pulls in the starter jar, but when I go to use classes that are from transitive dependencies, my project can't find them anywhere on the classpath.
When I dug into the starter jar, I found that all the dependencies it defines are RUNTIME scoped, which would explain the problem. I can 'fix' the problem by setting all the dependencies in my starter as 'compile' instead of 'implementation', but it's my understanding that 'compile' is on its way out, and that 'implementation' dependencies should be compile-scoped anyway. Can someone tell me what additional config may be necessary to define the starter dependencies as 'implementation' without them being scoped as 'runtime' in the resulting jar?
My starter/autoconfigure multi-project root gradle file:
plugins {
id 'org.springframework.boot' version '2.1.4.RELEASE' apply false
id 'io.spring.dependency-management' version '1.0.7.RELEASE' apply false
}
wrapper {
gradleVersion = '5.2.1'
}
repositories {
mavenLocal()
// private repo info omitted
mavenCentral()
jcenter()
}
subprojects {
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'maven-publish'
apply plugin: 'java-library'
group = 'com.mystarter'
repositories {
mavenLocal()
// private repo info omitted
mavenCentral()
jcenter()
}
dependencies {
annotationProcessor "org.springframework.boot:spring-boot-autoconfigure-processor"
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
}
bootJar {
enabled = false
}
jar {
enabled = true
}
javadoc {
failOnError = false
options.addStringOption('Xdoclint:none', '-quiet')
}
task sourcesJar(type: Jar) {
from sourceSets.main.allJava
classifier = 'sources'
}
task javadocJar(type: Jar) {
from javadoc
classifier = 'javadoc'
}
publishing {
publications {
myProjStarterArtifacts(MavenPublication) {
from components.java
artifact sourcesJar
artifact javadocJar
}
}
repositories {
// private repo info omitted
}
}
tasks.build.finalizedBy tasks.publishToMavenLocal
}
My starter sub-project's build file:
dependencies {
compile project(':myproj-spring-boot-autoconfig')
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security:spring-security-cas'
implementation 'org.springframework.security:spring-security-ldap'
}
If I change the above 'implementation' lines to be 'compile' lines, that's when the resulting pom file stops making these 4 dependencies 'runtime' scope and instead correctly scopes them as 'compile'. As a side note, the 'compile project' line works just fine, it's just the lines that are 'implementation' that don't seem to work the way I'd expect when the project has no classes of its own.
My new project's dependency for my starter:
dependencies {
implementation('com.myproj:myproj-spring-boot-starter:1.0.0')
// other dependencies
}
implementation dependencies defined in a Gradle project are only made transitive for the runtimeClasspath of the consumers of said project, that is by design.
If you have a project without code but only defining dependencies, consider using the java-platform plugin for it, which allows you to specify constraints and optionally dependencies.
Otherwise, if you want the project to expose its dependencies to consumers at compilation time, you should use the api configuration for declaring them instead of compile which is indeed on its way out.
For more details, see documentation.
I've a gradle project with subprojects. Every subproject produces a jar and a pom that it's published on a repository
1) In the main project gradle file there's a subprojects section that I used to define what and where to publish:
snippet from rootproject.gradle:
subprojects {
apply plugin: 'maven-publish'
publishing {
publications {
mavenJava(org.gradle.api.publish.maven.MavenPublication) {
from components.java
}
}
repositories {
maven {
url 'file://c:/temp/repo'
}
}
}
}
2) In the gradle file of one of my subprojects, I've added some dependencies:
snippet from subproject.gradle:
dependencies {
compile group: 'my-group', name: 'my-module', version:'1.1.0'
}
If I run "gradle publish" from the rootproject it will correctly publish every subproject. However, I noticed that the dependency defined in the subproject is missing from the pom publication related to the subproject.
If I cut and paste the content of the subprojects section in each subproject, the generated pom file contains the correct dependency.
It seems that “from components.java” is not a reference to something that should be used by the publish task to produce the pom, but the task will publish exactly what components.java contains when you call the “from” method.
As a workaround, I moved the subprojects code in a method defined in the root:
rootproject.gradle
def configurePublishing(project) {
project.apply plugin: 'maven-publish'
…
}
And I called it from each subproject:
subproject.gradle
dependencies {
compile group: 'my-group', name: 'my-module', version:'1.1.0'
}
configurePublishing(project)
Another solution could be adding a switch in the subprojects section and centralize everything in the gradle file of the root project:
subprojects { subProject ->
switch(subproject.name) {
case: ‘my-subproject-with-dependencies’ {
dependencies {
compile group: 'my-group', name: 'my-module', version:'1.1.0'
}
break;
}
}
apply plugin: 'maven-publish'
}
Is it an acceptable approach? Is there a best practice to follow? Is there an easier way to do it?
I have a Spring Boot project that is built using Gradle. It has compile dependencies on other projects. All the projects are mentioned via "includeFlat" in a settings.gradle file in a master project.
My problem: the boot ("fat") jar that is generated by the build omits the project dependencies.
Here's the project structure:
master
(no source)
build.gradle - applies 'Eclipse' plugin (but not Java)
settings.gradle -- has 'includeFlat' for projectA, projectB
projectA
src/main/java/...
build.gradle
projectB
src/main/java/...
build.gradle -- see below
The build.gradle for projectB looks roughly like this:
buildscript
{
repositories
{
mavenCentral()
}
dependencies
{
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.1.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
jar
{
baseName = 'xxx'
version = '0.1.0'
}
repositories
{
mavenCentral()
}
jar {
enabled = true
}
dependencies
{
compile project(":projectA")
}
"gradle build" on projectB generates both a regular and a boot jar. I expect the boot jar to include the classes from ":projectA" -- but they are missing.
Other than that, both projects build and run properly, whether built individually or via the master project.
The Question
How do I setup the dependencies of the modules in an Android Gradle project, where the modules have dependencies on each other, such that I can deploy a build of a new snapshot or release version of all modules in one go?
Project Setup
I have a project that consists of 2 modules: lib-core and lib-help, with lib-help depending on lib-core. I want to publish lib-core and lib-help seperately to Maven Central, such that the pom file of lib-help specifies the dependency on lib-core with the correct version.
The project is structured as follows:
/my-project-name
|
|--/lib-core
| |-- build.gradle
| |-- gradle.properties
|
|--/lib-help
| |-- build.gradle
| |-- gradle.properties
|
|-- build.gradle
|-- gradle.properties
|-- settings.gradle
|-- gradle-mvn-push.gradle
The project configuration files are as follows, showing only the relevant parts (by my judgement):
settings.gradle:
include ':lib-core', ':lib-help'
gradle.properties:
GROUP=com.company
VERSION=5.0.0-SNAPSHOT
gradle-mvn-push.gradle:
apply plugin: 'maven'
afterEvaluate { project ->
uploadArchives {
repositories {
mavenDeployer {
// GROUP is defined in <root>/gradle.properties
pom.groupId = GROUP
// ARTIFACT is defined in <root>/<module>/gradle.properties
pom.artifactId = ARTIFACT
// VERSION is defined in <root>/gradle.properties
pom.version = VERSION
}
}
}
}
// Contains a lot more to fill in other meta-data like project name,
// developer name, github url, javadoc, signing of the artifacts, etc, etc..
lib-core/gradle.properties:
ARTIFACT=my-lib-core
lib-core/build.gradle:
apply plugin: 'com.android.library'
// Contains more code to configure the android stuff, not relevant for this question
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
apply from: rootProject.file('gradle-mvn-push.gradle')
lib-help/gradle.properties:
ARTIFACT=my-lib-help
lib-help/build.gradle:
apply plugin: 'com.android.library'
// Contains more code to configure the android stuff, not relevant for this question
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':lib-core')
}
apply from: rootProject.file('gradle-mvn-push.gradle')
The Problem
When I run gradlew clean :lib-core:uploadArchives :lib-help:uploadArchives, everything compiles nicely and uploads are being pushed to the correct repository. In the Nexus Repository Manager I can see the artifacts for both modules with the correct name, version, signing, etc., but the dependency of lib-help on lib-core in the generated pom.xml is wrong. It uses the project name as groupId, the module name as the artifactId and an unspecified version name:
<dependency>
<groupId>my-project-name</groupId>
<artifactId>lib-core</artifactId>
<version>unspecified</version>
<scope>compile</scope>
</dependency>
Consequently, projects using lib-help as a dependency can't resolve the lib-core dependency. The correct values that should have been in lib-help's pom.xml are groupId=com.company, artifactId=my-lib-core and version=5.0.0-SNAPSHOT.
To fix this, I thought I'd use a maven dependency in lib-help, instead of a module dependency, like this:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.company:my-lib-core:' + VERSION
}
Naturally, this version does not exist before the uploadArchives command of lib-core has been executed, but gradle wants to resolve the dependencies of all modules before starting execution of any of the given commands. Thus, it can't resolve this dependency and quits with an error while configuring the gradle projects. Now, I can work around this by temporarily setting the dependency of lib-help to project(':lib-core'), then release lib-core, then adjust the dependency of lib-help to 'com.company:my-lib-core:5.0.0-SNAPSHOT', then release lib-help, but that just doesn't seem the right way. It invites human mistakes (what if I accidentally increase the version number between the to uploadArchives commands?) and it takes several manual, strictly ordered steps.
The Question (reprise)
How do I setup the dependencies of the modules in an Android Gradle project, where the modules have dependencies on each other, such that I can deploy a build of a new snapshot or release version of all modules in one go? (e.g., but not necessarily, with the command gradlew clean :lib-core:uploadArchives :lib-help:uploadArchives)
It's possible to keep the local project dependencies (compile project(':core')), if you rewrite the POM before uploading:
afterEvaluate { project ->
uploadArchives {
repositories {
mavenDeployer {
// ... other config here ...
pom.whenConfigured { pom ->
pom.dependencies.forEach { dep ->
if (dep.getVersion() == "unspecified") {
dep.setGroupId(GROUP)
dep.setVersion(VERSION_NAME)
}
}
}
}
}
}
}
Here's an example of a working project: https://github.com/hannesstruss/WindFish
I have a project setup like this
+ rootProject
+ module1
+ src
build.gradle
+ module2
+ src
build.gradle
+ src
build.gradle
settings.gradle
Content of rootProject/build.gradle
evaluationDependsOn(':module1')
apply plugin: 'java'
dependencies {
compile project(':module1')
compile project(':module2')
}
task myTask (type: JavaExec) {
main = 'org.gradle.example.Test.Main'
classpath runtimeClasspath
}
Content of module1/build.gradle
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile 'commons-codec:commons-codec:1.9'
}
Content of module2/build.gradle
apply plugin: 'java'
The dependencies graph
root --> module1 --> commons-codec
--> module2
gradle report this at build with command gradle build in root folder
Could not resolve all dependencies for configuration ':compile'.
> Could not find commons-codec:commons-codec:1.9. Required by:
:rootProject:unspecified > rootProject:codec:unspecified
if I add dependencies block in the rootProject's build file the it get build normally.
As you see, i already determine the dependencies in module1 build file. Why gradle keep saying that it couldnt resolve?
Do i need to put all dependencies of submodule in root build files?
First of all I see that You're missing settings.gradle file which is necessary in multimodule projects. You need to create it at the same level as rootProject/build.gradle is located.
The content should be
include 'module1', 'module2'
UPDATE
Ok, to root build.gradle added following piece of code:
allprojects {
repositories {
mavenCentral()
}
}
And removed from module1/build.gradle the repositories section. Now it works. Dependencies are fetched and displayed. Currently no idea what is the explanation.