Sharing and Publishing Dependencies with Gradle
Reusing or accessing code from other projects with gradle is very easy. This blog posts dives into publishing dependencies to your local maven repository (or remote) to use it in another project. We also take a short look at online repositories and the small extras you have to account for when your projects are minecraft plugins.
If you are not familiar with gradle yet, have a look at my previous post about gradle basics.
Project Setup
We have two projects which are named Project A
and Project B
for simplicity.
In the end Project A
will implement code and functions that are used by Project B
.
Since this is the more complex setup we're going to start with this.
Project A - The Provider
Project A
will provide code accessed by Project B
.
That means we need to do several things:
- Create a new gradle project named
Project A
- Add some logic/code to it, that we want to use in
Project B
- Publish our project into our local maven repository
Let's start with the setup itself. The configuration for our project and our class we want to use in our other project look like this:
For now we take it simple. We defined our group and version. No dependencies and no additional repositories.
Now that we have our basic project setup we can work on publishing our changes.
This is fairly simple.
All we need to do is to import the maven-publish
plugin and configure it.
Note
Some parts that were already there were left out now. Only parts that were changed are shown.
plugins {
java
`maven-publish`
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
withSourcesJar()
withJavadocJar()
}
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
}
}
}
A lot happened here, so lets go through it step by step.
- We added
maven-publish
to ourplugins
. This allows us to publish artifacts in the first place - We configured the
java
plugin:- We defined the language version our library will use
- We defined that we want to create a jar containing our source
- We defined that we want to create a jar containing our javadocs
- We configured the
maven-publish
plugin.- We created a new publications section
- In that section we created a new
MavenPublication
calledmaven
- We defined that this publication should return all components returned by our java plugin. This will be:
- One jar containing our compiled code
- One jar containing our source code
- One jar containing our java docs
Now that this is done we can publish into our maven local by executing the publishToMavenLocal
task of gradle.
Transitive dependencies
If your api depends on another api you can tell this to others.
Instead of using implementation
in your dependencies
section you use api
.
For that you also need to import java-library
as a plugin.
plugins {
java
`maven-publish`
`java-library`
}
dependencies {
api("de.chojo.sadu", "sadu", "2.3.1")
}
This will make them import the library your depend on as a transitive dependency as well.
Project B - The Consumer
Now that we published our artifacts into our local maven repository we can access it from other projects on our system.
Warning
If you send your Project B
to someone else they need to execute the publishToMavenLocal
task themself on Project A
.
To properly share dependencies you should use remote repositories.
The local maven repository should only be used for debugging and general testing of your project.
First we create our basic project setup again like we did on Project A
.
We defined our group and version. No dependencies and no additional repositories yet.
Now we get to importing our Project A
.
For that we need to do three things:
- Add
mavenLocal()
as a repository. - Configure our java task to use the same or a newer toolchain version than
Project A
. - Add our project as a dependency
Note
Some parts that were already there were left out now. Only parts that were changed are shown.
repositories {
mavenCentral()
mavenLocal()
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
dependencies {
implementation("dev.chojo", "project-a", "1.0.0")
}
Now we can go into our Project B
class, create a new instance of our Project A
class and call the meow method.
package dev.chojo.projectb;
import dev.chojo.projecta.ProjectA;
public class ProjectB {
public static void main(String[] args) {
new ProjectA().meow();
}
}
If we run our main method now we can see that "meow" get printed out.
While this works in our IDE, it will not work when we actually build Project B
and execute our Project B
jar.
This has two reasons:
- We did not define our main class anywhere.
- Our IDE does imports
Project A
into our class path, since we imported it asimplementation
. When we build our IDE can no longer import it and gradle assumes that we will addProject A
ourselves to the classpath. To fix this we can either use the shadow plugin or the application plugin. Configure either of them as described in the documentation
Note on minecraft plugins
When you are building a minecraft plugin there are several more things you should consider:
CompileOnly or Implementation
Whether your dependency is a compileOnly
or implementation
depends on multiple factos.
Implementation
- The dependency is not a plugin
- Is not hosted in maven central.
- You are on an older version than 1.16.5
Make sure to use shadow and relocation
CompileOnly
- The dependency is hosted on MavenCentral. Use the library loader
- The dependency is another plugin. See next section
Depend or Softdepend
If your dependency is a plugin and imported as compileOnly
make sure to add its name as a depend
or soft-depend
in your plugin.yml.
Soft-Depend
- Your plugin can work without any class of your dependency
Depend
- Your plugin will not work without the dependency.
Remote Repositories
To allow everyone to build your project regardless of their local maven repository content you should deploy your code into a remove repository.
The most famous one might be Maven Central. However, publishing there is quite complex and nothing for beginners. The repository is also intended for projects with general usability for the public.
There are several different pieces of software for self-hosting repositories like sonatype nexus or reposilite. If you don't want to self-host, there are a some repositories out there that are open to the public. However, have in mind that all those repositories require your project to be open source
However, in the end publishing to a remote repository works similar.
All you need to do usually is to configure some simple auth:
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
}
}
repositories {
maven {
name = "Example"
authentication {
credentials(PasswordCredentials::class) {
username = "username"
password = "password"
}
}
url = uri("https://repo.example.com")
}
}
}
Warning
Don't put your credentials in your code. Read them from some environment variable instead.
Once you configured your repository you have a new tasks called publishMavenPublicationToExampleRepository
which allows you to publish to your remote repository.
Note
The task name changes based on the name you assign to your repository.
And that's it. Of course there is more to consider like snapshot and stable repositories. Plugins like publishData or indra can take care of that for example.
Thank you
That's it. Now you can easily share code between your projects or allow others to use code from your projects.
Thanks for reading! If you liked this post, or have some questions, feel free to come by my discord and have a chat!
Or write me a mail at mail [at] chojo [dot] dev
.
If you want to support me, feel free to sponsor me via GitHub.