Publishing of a modular library to Maven using Gradle - gradle

Suppose I'm using Gradle for a modular library development. In my root project I have subprojects geometry, algorithms, visualizer, and I'd like to publish a jar artifact of each.
As for now in my root build.gradle I have the following part:
apply plugin: 'maven-publish'
publishing {
publications {
publishDemos(MavenPublication) {
groupId 'ru.ifmo.ctddev.igushkin.cg'
artifactId 'geometry'
version globalVersion
artifact project(':geometry').tasks.getByName('jar')
}
publishAlgorithms(MavenPublication) {
groupId 'ru.ifmo.ctddev.igushkin.cg'
artifactId 'algorithms'
version globalVersion
artifact project(':algorithms').tasks.getByName('jar')
}
publishVisualizer(MavenPublication) {
groupId 'ru.ifmo.ctddev.igushkin.cg'
artifactId 'visualizer'
version globalVersion
artifact project(':visualizer').tasks.getByName('jar')
}
}
}
My first question: is there a shorter way of describing the publications? For example, I'd like to state that for each subproject I need a publication with the artifactId set from its name.
Next, my subprojects depend on each other, both algorithms and visualizer depend on classes from geometry, but at this point the jars do not contain the dependencies, and, for example, the user will have to add dependencies to both geometry and algorithms if they want to use algorithms.
So, is there a way for to provide some sort of auto-dependency, so that adding algorithms would also add geometry? If yes, how do I do it? If no, what is the idiomatic way of providing modular libraries? Should I assemble jars with dependencies instead?
UPD: Am I right that instead of artifact ... I should just use from project(':...').components.java, because it will pick up both artifacts and dependencies? How do I pick dependencies separately if I use artifact ...?

You can do less verbose publication declaration by injecting the same publication config into each subproject. For example for a multi-build project with the structure:
ROOT
│ build.gradle
│ settings.gradle
├───subA
│ build.gradle
│
├───subB
│ build.gradle
│
└───subC
build.gradle
In your root build.gradle, you can do:
apply plugin:'maven-publish'
subprojects{
publishing {
publications {
"$project.name"(MavenPublication) {
groupId project.group
artifactId project.name
version project.version
from components.java
}
}
}
}
Each subproject defines its own groupid and version like so:
group = 'org.test.sample.A'
version = '1.0'
The artifactId is picked up from the subproject name. Running gradle publish results in a repo of this structure:
org
└───test
└───sample
├───A
│ └───subA
│ └───1.0
│ subA-1.0.jar
│ subA-1.0.pom
├───B
│ └───subB
│ └───1.0
│ subB-1.0.jar
│ subB-1.0.pom
└───C
└───subC
└───1.0
subC-1.0.jar
subC-1.0.pom
Dependencies
This config also automatically takes care of dependencies. For instance if in subproject subA you had:
dependencies{
compile project(':subB')
}
Since I am using from components.java instead of artifact, the plugin knows to look for dependencies and generates a pom for subA that includes:
<dependencies>
<dependency>
<groupId>org.gradle.sample.B</groupId>
<artifactId>subB</artifactId>
<version>1.0</version>
<scope>runtime</scope>
</dependency>
</dependencies>

Related

creating springboot gradle multi-module library project using plugins DSL and a convention plugin that reside in buildSrc

I am trying to build multi module gradle library. I am trying to follow what I read in offical documentation.
I am working in company which has its own nexus repository for spring and other libraries
My project structure is as follows
multi-project
├── libA
│ └── build.gradle
├── libB
│ └── build.gradle
├── buildSrc
│ └── build.gradle
└── main/src/groovy
└────────multiproject.common-conventions.gradle
│
└── settings.gradle
The project involves using spring boot for different library, and the dependencies are almost the same. Here was my attempt that is not working
buildSrc/build.gradle
plugins {
id 'groovy-gradle-plugin'
}
repositories {
gradlePluginPortal()
}
buildSrc/multiproject.common-conventions.gradle
plugins {
id 'java-library'
id 'maven-publish'
id 'org.springframework.boot'
}
repositories {
mavenCentral()
}
dependencies {
implementation group: 'org.springframework.boot:spring-boot-gradle-plugin'
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-aop'
implementation 'org.springframework.ws:spring-ws-core'
implementation 'org.apache.httpcomponents:httpclient:4.5.13'
implementation 'org.springframework.retry:spring-retry:1.3.1'
implementation 'javax.cache:cache-api:1.1.1'
implementation 'org.ehcache:ehcache:3.10.0'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.3'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
now I try to use above plugin in one sub modules, let say libA
libA/build.gradle
plugins {
id("multiproject.common-conventions")
id 'org.springframework.boot' version
}
I get the following exception
org.gradle.api.plugins.UnknownPluginException: Plugin with id 'org.springframework.boot' not found.
when I add the version to 2.6.4 I get
Invalid plugin request [id: 'org.springframework.boot', version: '2.7.1']. Plugin requests from precompiled scripts must not include a version number. Please remove the version from the offending request and make sure the module containing the requested plugin 'org.springframework.boot' is an implementation dependency.
How can I setup multi module gradle library to use common-conventions plugin using the buildSrc folder, and the plugins DSL? I am using gradle 7.3 version.
Have a look at this section of Gradle's doc https://docs.gradle.org/current/userguide/custom_plugins.html#applying_external_plugins_in_precompiled_script_plugins.
As stated there, in order to apply an external plugin (spring-boot-gradle-plugin in your case) in a precompiled script plugin (your multiproject.common-conventions.gradle), it has to be added to the plugin project’s implementation classpath in the plugin’s build file (you're buildSrc/build.gradle file)
buildSrc/build.gradle
plugins {
id 'groovy-gradle-plugin'
}
repositories {
mavenCentral()
}
dependencies {
implementation('org.springframework.boot:spring-boot-gradle-plugin:3.0.0')
}

Failure when applying custom external gradle plugin to multi-module gradle project

I'm having a hard time applying my custom external gradle plugins to an existing multi-module project. My project's structure looks like this:
my-app/
├─ buildSrc/
│ ├─ build.gradle.kts
├─ module1/
│ ├─ build.gradle.kts
├─ module2/
│ ├─ build.gradle.kts
├─ settings.gradle.kts
I've now implemented a couple of custom binary plugins which live in another repository and publishing them to mavenLocal() like so:
Plugin Repo Code
gradlePlugin {
plugins {
create("plugin") {
id = "organization.gradle.my-conventions"
implementationClass = "organization.gradle.MyConventionsPlugin"
version = "0.0.1"
}
create("plugin2") {
id = "organization.gradle.my-other-conventions"
implementationClass = "organization.gradle.MyOtherConventionsPlugin"
version = "0.0.1"
}
}
}
Main Project Code
In my main project, I've configured settings.gradle.kts to resolve plugins from mavenLocal and apply one of the plugins (let's say plugin1) in the build.gradle.kts of module1. This looks like this:
plugins {
id("organzation.gradle.MyConventionsPlugin") version "0.0.1"
}
This doesn't work though. I end up getting a stacktrace saying:
> Failed to apply plugin class 'org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubplugin'.
> Cannot run Project.afterEvaluate(Action) when the project is already evaluated.
I tried the same way of applying the plugins in a non multi-module project (i.e. a single build.gradle.kts) and the plugins are applied as expected. So this most likely has to do with the multi-module aspect of it and some additional config?

grade subprojects & dependencies

This is definitely a beginner gradle question, but I just can't make it work.
I have a library and an application which depends on the library.
.
/ library
/ application
I can install the library through gradle install in ./library, then have the application depend on the library through the group/name/version, and pick up the library through the mavenLocal() repository in the application build.gradle, then build the app in the application folder. And that works.
But I'd like to have a project, and that I could have a single command to build the application and, if needed, the library too (similar to the maven -am flag).
Here's what I have right now, in the parent folder, build.gradle:
subprojects {
apply plugin: "java"
repositories {
mavenCentral()
}
}
project(':application') {
dependencies {
compile project(':library')
}
}
and in the same folder I have settings.gradle:
include ':library', ':application'
Again, if I go in the library folder and run gradle install, it works. I get the artifact in my ~/.m2.
But now with the configuration I described, if I go in the root folder and run gradle shadowJar.. and the shadowJar task is present only in application, then gradle tries to compile the library but fails, apparently because it doesn't pick up the library dependencies.
* What went wrong:
Execution failed for task ':library:compileJava'.
> Compilation failed; see the compiler error output for details.
the errors are like, for instance:
Task :library:compileJava FAILED
.../DateTimeAdapter.java:5: error: package javax.xml.bind.annotation.adapters does not exist
import javax.xml.bind.annotation.adapters.XmlAdapter;
and sure enough these are dependencies in the library/build.gradle =>
implementation 'com.fasterxml.jackson.core:jackson-core:2.8.8'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.8.8'
So my question is how do I specify the application dependency on library, so that I can run a single command and have maven build the library if needed, then the application.. I thought what I did would have gotten the first part through, but it fails, apparently due to library's transitive dependencies. I actually expect I need to handle a second part after that, which is how to specify the dependency in the application build.json. I may have to switch from specifying the maven group/name/version coordinates to the project name, but I didn't get that far yet.
I would definitely not want to list the library dependencies in the root gradle file: I'd like library to handle its dependencies, and application its dependencies, I'd like to keep the root gradle file small.
application is unable to see the dependencies of library because you have declared the Jackson dependencies as an implementation detail of library.
What you intended to do was expose your library and it's dependencies as an api for consumers.
Dependencies declared in the implementation configuration can be thought of as "private" meaning consumers of your library should not access methods/classes that use those dependencies otherwise they will face errors like the one you are. api is basically the opposite of implementation.
The api configuration is available via the java-library plugin.
Full working example for what you're trying to achieve (Kotlin DSL):
├── application
│   └── build.gradle.kts
├── build.gradle.kts
├── gradle
│   └── wrapper
│   ├── gradle-wrapper.jar
│   └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── library
│   └── build.gradle.kts
└── settings.gradle.kts
Root project build.gradle.kts:
subprojects {
apply {
plugin("java-library")
}
repositories {
jcenter()
}
}
settings.gradle.kts:
rootProject.name = "example-proj"
include("application")
include("library")
Library build.gradle.kts:
dependencies {
api("com.fasterxml.jackson.core:jackson-core:2.10.0")
api("com.fasterxml.jackson.core:jackson-databind:2.10.0")
}
Application build.gradle.kts:
dependencies {
implementation(project(":library"))
}

How to build a maven meta project from sibling projects in gradle

I have a multi project that has a structure like this:
.
├── all
├── left
└── right
I am trying to make :all a "composite meta project" of it's siblings (:left and :right). That is :all project should publish just a pom that declares dependency on siblings (it itself contains no code and doesn't produce a jar).
Is this possible? And if so how should I configure :all to achieve it?
I am using gradle 1.12
I've prepared the following example. Maybe I it will help You.
root/build.gradle is empty
root/settings.gradle
include ":l", ":r", ":all"
root/l/build.gradle is empty
root/p/build.gradle is empty
root/all/build.gradle
apply plugin: 'java'
dependencies {
compile project(':l')
compile project(':r')
}
publishing {
publications {
maven(MavenPublication) {
from components.java
}
}
}
Hope it helps. Mind that this isn't a full example, rather proof of concept.

How to add other projects in current project in Gradle?

I have two projects ProjA and ProjB, here ProjB is depends on ProjA. So, how to include ProjA in ProjB?
And please let me know in case change in the ProjA build.gradle file?
Okay, so you have two projects, whereas ProjA depends on ProjB.
If you have already set up a gradle build for those, both of them will have a file settings.gradle, which at least defines the project name and build.gradle, where you can specify the dependencies.
Example:
settings.gradle
rootProject.name = 'ProjA'
build.gradle
group = 'mygroup'
version = '1.0'
By further adding one of Gradle's publishing plugins (see Maven publishing or Ivy publishing), you can configure a publication. A publication in turn contains artifacts, that is the files you want to upload (and usually some repositories).
Example for a minimal Maven publication:
apply plugin: 'maven-publish'
publishing {
publications {
core(MavenPublication) {
from components.java
}
}
}
The maven-publish plugin will add the task publishToMavenLocal (among others) to your project which installs the jar somewhere to ~/.gradle/caches/.
In build.gradle of ProjB you can then define a compile time dependency on ProjA like so:
dependencies {
compile(group: 'mygroup', name: 'ProjA', version: '1.0')
}
If your projects are co-located, you could have a multi build project.
Say you have a root folder with these contents:
some root directory
project-a
project-a.gradle
src
project-b
project-b.gradle
src
build.gradle
settings.gradle
settings.gradle contains:
include 'project-a', 'project-b'
project-b.gradle then depends on project A:
dependencies {
compile project(':project-a')
}
After this, project a will always compile and assemble before project-b and the jar file for project-a jar will be on the classpath when compiling project-b.

Resources