Relocate dependencies

Hi!

I want to relocate some dependencies in the ‘fat-jar’.
For shadowing I am using the built-in shade from ForgeGradle. An example build.gradle. Some information here.

Now to relocating dependencies: The ForgeGradle wiki says I can use srgExtra. I have found a .srg file here. It seems to have a syntax for classes and methods:

CL: <original fully qualified class name> <new fully qualified class name>
MD: <original fully qualified method name> <return type> <new fully qualified method name> <return type>

Is this right? Is there some documentation? And can I relocate a whole package(like for my dependencies)?

RandomByte

The shade dependency configuration you added is not something built-in from ForgeGradle, but rather just an example how to include dependencies using the Gradle API. It is not related to ForgeGradle.

The way the ForgeGradle article suggests doing relocation is not part of including the dependencies in your JAR, instead you define extra SRG mappings that will be applied by ForgeGradle when it reobfuscates the JAR to the obfuscated Minecraft names. However, if you are building your plugin against SpongeAPI there is no reason to use ForgeGradle and its reobfuscation.

If you want to shade dependencies in a plugin, I’d suggest that you use the Gradle Shadow Plugin to include them in your JAR. You can also easily relocate your dependencies using it:

dependencies {
    compile 'com.example:mylib:1.0.0'
}

shadowJar {
    dependencies {
        include dependency('com.example:mylib')
    }

    relocate 'com.example.mylib', 'net.minecrell.test.mylib'
}
1 Like

Thanks for your response!

A project I am currently working on.
The jar contains everything I included, but there are 2 problems:

  1. The package de.randombyte.kosp isn’t relocated. kosp is a ‘commons’ plugin library for the use with Kotlin. Instead of shading it into every plugin I build with it, should I make a separate plugin? But then the user would have to also download Kosp to just run one plugin from me… And what about compatibility when 2 plugin of me use different versions of Kosp?
  2. Some StartServer class and java file is shaded into the final jar. It probably is from SpongeStart.

Is it even necessary to relocate every shaded lib? Like the Kotlin runtime and stdlib? It is shaded by every of my plugins and not relocated yet.

We want to allow multiple library versions to run independently, yet concurrently in the same JVM.

  1. Naming conflicts.
    It is indeed very useful to relocate libraries you wish to ship with your application. This helps to avoid dependency conflicts. If I use your plugin with Kotlin v1 (for example) under some arbitrary namespace (I don’t know kotlin well- at all.) such as kotlin.io and it works great for you. Although say my software instead is meant to be used with Kotlin v2. If namespaces haven’t changed then we will get naming conflicts as both my Kotlin v2 and your Kotlin v1 are not known to be different to the JVM.

  2. Implementation conflicts
    Say on the other hand, we somehow got no naming conflicts and got it all to compile and load up. At this point we’d then come into implementation conflict because my Kotlin v2 is supposed to say, parse strings for IPs by default for IPv4, but in Kotlin v2 it default parses IPv6, it won’t perform as intended.

The above isn’t the best examples, nor was it thought about for long. I just wanted to minorly point out how relocating packages of the libraries you wish to ship with your software can save you, and others a lot of headache down the line.

1 Like

My build.gradle

Shadowing and relocating the koltin runtime and stdlib works.
Shadowing the dependency com.github.randombyte-developer:kosp doesn’t work. Do you have any idea why it doesn’t work?

Edit: I finally got it to work

plugins {
    id "com.zoltu.kotlin" version "1.0.4"
    id "com.qixalite.spongestart" version "1.5.2"
    id "com.github.johnrengelman.shadow" version "1.2.4"
}

group "de.randombyte"
version "v0.1"

repositories {
    jcenter()
    maven { url "https://repo.spongepowered.org/maven/" }
    maven { url "https://jitpack.io" }
}

spongestart {
    eula true
}

ext.spongeApiVersion = "5.0.0"
ext.kotlinVersion = "1.0.4"

dependencies {
    compile "org.jetbrains.kotlin:kotlin-runtime:$kotlinVersion"
    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
    compile "org.spongepowered:spongeapi:$spongeApiVersion"
    kapt "org.spongepowered:spongeapi:$spongeApiVersion"
    compile "com.github.randombyte-developer:kosp:v0.2"
}

shadowJar {
    dependencies {
        include dependency(":kotlin-runtime")
        include dependency(":kotlin-stdlib")
        include dependency(":kosp")
    }

    relocate "kotlin", "de.randombyte.chatmentions.internal.kotlin"
    relocate "de.randombyte.kosp", "de.randombyte.chatmentions.internal.kosp"
}

The problem was that I said com.github.randombyte-developer:kosp as dependency, it should only be :kosp. I changed kotlin’s dependencies to that format, too.
The previous notation worked before with kotlin but not with kosp. I don’t know why. And I also didn’t find anything about that on the internet. I just tried many different things until it worked.

@Minecrell When shading, what is the best practice to name the shaded dependency? As you can see, I put it into <plugin package>.internal.<shaded dependency name>.

You should preferably always use the group_id:artifact_id syntax for the shadow configuration to prevent including other dependencies which have the same artifact ID but a different group ID.

In your case, the problem seems to be related to the way JitPack publishes the artifacts on the Maven repository. According to your dependency, the correct way to include it in the shadow JAR would be com.github.randombyte-developer:kosp, however that does not seem to match any classes.

We can see the reason for that if we take a look at the dependency tree:

$ ./gradlew :dependencies --configuration=compile
:dependencies

------------------------------------------------------------
Root project
------------------------------------------------------------

compile - Dependencies for source set 'main'.
...
\--- com.github.randombyte-developer:kosp:v0.2
     +--- com.github.randombyte-developer.kosp:kosp:v0.2
     |    +--- org.jetbrains.kotlin:kotlin-runtime:1.0.4
     |    +--- org.jetbrains.kotlin:kotlin-stdlib:1.0.4 (*)
     |    \--- org.spongepowered:spongeapi:5.0.0 (*)
     \--- com.github.randombyte-developer.kosp:kosp-test-plugin:v0.2
          \--- org.spongepowered:spongeapi:5.0.0 (*)

(*) - dependencies omitted (listed previously)

BUILD SUCCESSFUL

Total time: 2.492 secs

So why does it not include any classes? Your kosp repository includes 2 projects, the main kosp project and the kosp-test-plugin. JitPack publishes them separately, and if you depend on the repository, then you depend on a kind of “meta package” that has both projects as dependencies.

If you specify dependencies to include in the shadow JAR it will only add the dependency itself, but not their transitive dependencies (the dependencies of that dependency, and so on).
The classes you want to include are in the com.github.randombyte-developer.kosp:kosp dependency (note the additional .kosp in the group ID). Therefore you can specify the correct set of dependencies like this:

shadowJar {
    dependencies {
        include dependency("org.jetbrains.kotlin:kotlin-runtime")
        include dependency("org.jetbrains.kotlin:kotlin-stdlib")
        include dependency("com.github.randombyte-developer.kosp:kosp")
    }
}

I don’t think there is any naming convention, just use whatever looks best for you. :wink:

2 Likes

Your are the best! :ok_hand: