Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Help shadowing a single dependency for library mantainers #1265

Open
slinkydeveloper opened this issue Feb 18, 2025 · 3 comments
Open

Help shadowing a single dependency for library mantainers #1265

slinkydeveloper opened this issue Feb 18, 2025 · 3 comments

Comments

@slinkydeveloper
Copy link

slinkydeveloper commented Feb 18, 2025

Describe your use-case which is not covered by existing documentation.

I've been struggling with setting up shading in my gradle build, I was wondering if someone has a ready made example for that, or perhaps a recipe can be added to the documentation for that.

I want to shadow just a single dependency (protobuf) when releasing my library using maven-publish, and then everything else should work business as usual. This single dependency i want to shade should not be neither in the MANIFEST file, nor in the runtime dependencies of the generated pom.

I've tried few setups, including copying the work done in Kafka here: https://github.com/apache/kafka/blob/9a2043041d38c6555ce0abf6d1438adb34f565b2/build.gradle, but none of them work for one reason or another: either I stumble on some issue with a wrong generated MANIFEST file (#886), or the generated pom file doesn't contain the other non-shaded dependencies that should be declared in the pom, and so on...

It would be amazing if the documentation could contain a little recipe of how to shadow just a single dependency, something that is often the case for library mantainers.

Reference any relevant documentation, other materials or issues/pull requests that can be used for inspiration.

No response

Are you interested in contributing to the documentation?

I'll be happy to add some documentation if people can help me craft this recipe :)

@slinkydeveloper
Copy link
Author

This is what i tried to do so far: restatedev/sdk-java@ffa41fb, this fixes the MANIFEST generation, but the pom won't contain all the other implementation dependencies.

@poom-kitti
Copy link

poom-kitti commented Feb 20, 2025

@slinkydeveloper Seems like we landed on the same problem. For your solution, please be careful regarding the generated Gradle module metadata (.module file) because if you modify the POM file, it may not be reflected to the Gradle module metadata file content, then those who use Gradle may not pull transitive dependencies correctly.

I have been successful to do something similar to what you wanted by using custom ShadowJar task as outlined in this issue #1252, maybe you can take a look if you are interested. But, it will have limitation that you cannot declare compile scope.

Another way I can think is as following (I developed on Gradle version 8.10 with Groovy syntax)

configurations {
    // For declaring dependencies that need to be shaded
    shaded {
        canBeConsumed = false
        canBeResolved = false
    }
    // For declaring classpath to be packaged in the shadow jar
    resolvable("shadedClasspath") {
        extendsFrom(shaded)
    }
    // For the shaded dependencies to be available in testing
    testImplementation {
        extendsFrom(shaded)
    }
    // For the shaded dependencies to be available in compile and runtime classpaths without interfering with
    // dependencies scope in POM file
    runtimeClasspath {
        extendsFrom(shadedClasspath)
    }
    compileClasspath {
        extendsFrom(shadedClasspath)
    }
}

dependencies {
    shaded 'org.slf4j:slf4j-api:2.0.16'
    implementation 'com.typesafe:config:1.4.3'
    api 'io.opentelemetry:opentelemetry-api:1.47.0'
}

tasks.named("shadowJar", ShadowJar) {
    mustRunAfter("jar")

    archiveClassifier = null
    configurations = [project.configurations.shadedClasspath]

    enableRelocation = true
    relocationPrefix = "org.example.shaded"
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java

            AdhocComponentWithVariants shadowComponent = (AdhocComponentWithVariants) components.java
            shadowComponent.addVariantsFromConfiguration(configurations.shadowRuntimeElements) {}
        }
    }
}

For above, seems like jar task is required and shadowJar must run after jar because we want to replace the default JAR created with the JAR generated by shadowJar.

I have not thoroughly tested the solution. But have confirm following:

  • From gradle dependencies seem like the dependencies declared as shaded are added to classpaths correctly
  • The generated POM file is correct
  • The generated Gradle module metadata should be correct
  • Publishing to MavenLocal, then using in another new project does pull the transitive dependencies correctly

The configurations setup are kind of long, but I am unsure whether there is a better way to configure them. Would love to hear your and the project maintainers thoughts on possible solutions for this use case 😄 .

@portlek
Copy link
Contributor

portlek commented Mar 6, 2025

I wouldn't recommend releasing a jar with shaded libraries in it. Let the user decide which version to use with your library. If the library requires a specific version of a library, specify that in your documentation. Releasing shaded libraries is not a good idea. The main reason is that the library won't contain the sources for the library you shaded into, and that's really annoying, an example of this would be grpc's shaded netty module. Another would be security risks you run into when using someone else's chosen version of the library, and when you want to update the shaded library version, it creates annoyance.

I would recommend you to publish without shadowJar and use compileOnlyApi(java-library) instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants