Good afternoon! I'm in a bit of a prickly situatio...
# community-support
c
Good afternoon! I'm in a bit of a prickly situation here (bloody amateur and all), and i was hoping to get some help here 🙂 I'm trying to set up publishing for a java gradle kts project with transitive dependencies. My goal would be that another project could include the artifact of the first one using the following reference from my gradle version catalogue:
Copy code
api(libs.pack.converter) {
    isTransitive = true
}
Ideally, this should result in all the transitive dependencies of this lib.pack.converter dependency to also be available. (For example, gson/a different third party lib/etc). To achieve this, i have this build script for the module that i wish to publish: (build.gradle.kts)
Copy code
plugins {
    `maven-publish`
}

dependencies {
    api(project(":pack-schema-api"))
    api("com.google.code.gson:gson:2.10.1")
    compileOnly("commons-io:commons-io:2.11.0")
    compileOnly("com.twelvemonkeys.imageio:imageio-tga:3.9.4")
    compileOnly("com.nukkitx.fastutil:fastutil-int-object-maps:8.5.3")
    api("net.kyori:adventure-api:4.14.0")
    api("net.kyori:adventure-text-serializer-gson:4.14.0")
    api("net.kyori:adventure-text-serializer-legacy:4.14.0")
    api("team.unnamed:creative-api:1.7.0")
    api("team.unnamed:creative-serializer-minecraft:1.7.0")

    compileOnly("com.google.auto.service:auto-service:1.0.1")
    annotationProcessor("com.google.auto.service:auto-service:1.0.1")
}

tasks.withType<Jar> {
    from("src/main/java/resources") {
        include("*")
    }
    archiveClassifier.set("")
}

tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
    from("src/main/java/resources") {
        include("*")
    }
    archiveClassifier.set("all")
}

// Generate a sources jar
val sourcesJar by tasks.registering(Jar::class) {
    archiveClassifier.set("sources")
    from(sourceSets.main.get().allSource)

    from("src/main/java/resources") {
        include("*")
    }
}

publishing {
    publications {
        register("sourcesPublish", MavenPublication::class) {
            from(components["java"])
            artifact(sourcesJar.get())
        }
    }
}
This seems to publish all the jars to my maven local repo - however, depending on them with the cope snippet above (isTransitive=true) doesn't result in the
api
dependencies being available for the consumer of my pack converter lib. Any idea what i'm missing here? Am i publishing the wrong jar, if so, what should i be publishing? Should i expect a pom.xml file in my
META-INF
folder with all repos + dependencies for the transitive ones, or is that handled differently? Sorry for asking a potentially dumb question - i'm a bloody beginner and just trying to learn. Thanks in advance!
v
You do not need
isTransitive = true
, except if you made the whole configuration non-transitive. Transitivity is the default, everthing else would make very little sense. Declaring everything as
api
is a bad idea, except if you really use all those dependencies in your public API (as superclasses, return types, parameter types, ...). Things you only need for your implementation like in private or package-private signatures or only in method bodies, should be
implementation
, otherwise they needlessly pollute the downstream compile classpath which makes compilation slower and modeling more unclean. What you declared as
api
will end up in the compile and runtime classpath of consumers, what you declare as
implementation
only in its runtime classpath. Why things are not working properly is hard to say, especially as you did not share your full build logic. For example I see you configure
ShadowJar
tasks, but you do nowhere apply the
shadow
plugin or even add it to the classpath. I would also strongly recommend to remove it if you do. Such fat jars are bad-practice and an especially bad idea when used for a library and especialllly when used without relocation. Depending on what other configuration you did, it might also be caused by the
shadow
plugin, that the downstream project is not getting the dependencies you expect. Also you should not configure
Jar
tasks to include arbitrary resources. Any other task needing resources will miss them. If you really need them in
src/main/java/resources
instead of putting them into the conventional
src/main/resources
, then at least configure that path in
sourceSets { main { resources { srcDir(...) } } }
instead, so that all resource consumers also get them. Besides that you configure each and every
Jar
typed task in your build to contain those files instead of only the one task where it should be done. Also, setting the
archiveClassifier
of each and ever task of type
Jar
sounds like an awfully bad idea. If you really need to set classifiers, do it targetedly. But that also seems to be some effect from the
shadow
configuration, which again hints at this being the problem and also changing the pom to not include the dependencies it re-packed. You should also not create a source jar, but just use
java { withSourcesJar() }
which will create the proper task and also add it to the
java
component for publishing. This will also remove the need to manually add the artifact to the publication which anway is discouraged and should not be done. Instead the published component should be changed so that the right things are published, for example as shown in the last paragraph.
And for the future, please don't post walls-of-text to the channel, but use a thread, for example for longer source snippets, otherwise it is for example pretty hard to stay up to date when using a mobile client.
c
Hey, first off - thanks a lot! That helped immensely, appreciate it. I'll try to clean up the current mess, remove shadow, and see if i can publications working 🙂 As for why there's that many api dependencies - those in fact are used as return types, and they should be available downstream. Regarding the full build script; here it is: That's the root build.gradle.kts of the parent project, it's applying plugins. I'll yeet shadow fully, and see if that works.
Copy code
plugins {
    id("java")
    id("java-library")
    id("maven-publish")
    id("com.github.johnrengelman.shadow") version "7.1.0"
    id("io.freefair.lombok") version "6.3.0" apply false
}

allprojects {
    apply(plugin = "java")
    apply(plugin = "java-library")
    apply(plugin = "maven-publish")
    apply(plugin = "com.github.johnrengelman.shadow")
    apply(plugin = "io.freefair.lombok")

    repositories {
        mavenLocal()
        mavenCentral()

        gradlePluginPortal()

        // Geyser, Floodgate, Cumulus etc.
        maven("<https://repo.opencollab.dev/main>")
        maven("<https://repo.opencollab.dev/maven-snapshots>")

        // Java pack library
        maven("<https://repo.unnamed.team/repository/unnamed-public/>")
    }

    group = "org.geysermc.pack"
    version = "3.0-SNAPSHOT"

    java.sourceCompatibility = JavaVersion.VERSION_17
    java.targetCompatibility = JavaVersion.VERSION_17

    tasks.jar {
        archiveClassifier.set("unshaded")
    }

    tasks.named("build") {
        dependsOn(tasks.shadowJar)
    }

    publishing {
        repositories {
            // publishing setup here
        }
    }
}
finally, i'm sorry for dumping such a wall-of-text there - i wont do so again, and keep it to a thread. Thanks a lot for helping me out here!
v
Some more as you shared more: •
java-library
is a very small addition over
java
and applies
java
automatically, you can just keep the second, while it does not hurt • do not use the legacy style to apply plugins, but always use the
plugins { ... }
block • related, do not use
allprojects { ... }
or any other means of doing cross-project configuration, that is discouraged bad practice that works against some of the optimizations and more sophisticated features of Gradle. To DRY and centralize the build logic, better consider using convention plugins, for example in
buildSrc
or an included build like
gradle/build-logic
, for example implemented as precompiled script plugin. • Do not use
mavenLocal
, especially not as first repository and especially not without any content filter. It is broken by design in Maven already, and makes your builds slow and at best flaky. Read more about it at https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:case-for-maven-local • Just in case you are not developing a Gradle plugin there, is usually makes very little sense to have the
gradlePluginPortal
as repository, especially as each dependency is then searched twice in Maven Central before searched in the other three repositories, as Gradle Plugin Portal redirects to JCenter for things it does not have which forwards to Maven Central for things it does not have. • You also might consider using the JVM Toolchains feature instead of setting
*Compatibility
to decouple the Java version used to run Gradle from the Java version needed for your code.
c
Thanks!!