neoforged / NeoGradle

Gradle plugin for NeoForge development
GNU Lesser General Public License v2.1
43 stars 25 forks source link

Including transitive dependency tree doesn't work without including each dependency #175

Open dannypas00 opened 3 months ago

dannypas00 commented 3 months ago

I'm attempting to include the google drive api client library, when doing so the code compiles just fine, but it crashes in runtime on a transtive dependency. I've tried using jarjar, but I've been told in discord that I'd need to include each dependency individually for this to work.

I'd like some way to "automatically" add all transitive dependencies, because the client library relies on way too many dependencies to manually manage.

build.gradle:

plugins {
    id 'java-library'
    id 'eclipse'
    id 'idea'
    id 'maven-publish'
    id 'net.neoforged.gradle.userdev' version '7.0.124'
}

version = mod_version
group = mod_group_id

repositories {
    mavenLocal()
    mavenCentral()
    google()
}

base {
    archivesName = mod_id
}

jarJar.enable()

// Mojang ships Java 21 to end users starting in 1.20.5, so mods should target Java 21.
java.toolchain.languageVersion = JavaLanguageVersion.of(21)

//minecraft.accessTransformers.file rootProject.file('src/main/resources/META-INF/accesstransformer.cfg')
//minecraft.accessTransformers.entry public net.minecraft.client.Minecraft textureManager # textureManager

// Default run configurations.
// These can be tweaked, removed, or duplicated as needed.
runs {
    // applies to all the run configs below
    configureEach {
        // Recommended logging data for a userdev environment
        // The markers can be added/remove as needed separated by commas.
        // "SCAN": For mods scan.
        // "REGISTRIES": For firing of registry events.
        // "REGISTRYDUMP": For getting the contents of all registries.
        systemProperty 'forge.logging.markers', 'REGISTRIES'

        // Recommended logging level for the console
        // You can set various levels here.
        // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
        systemProperty 'forge.logging.console.level', 'debug'

        modSource project.sourceSets.main
    }

    client {
        // Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
        systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
    }

    server {
        systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
        programArgument '--nogui'
    }

    // This run config launches GameTestServer and runs all registered gametests, then exits.
    // By default, the server will crash when no gametests are provided.
    // The gametest system is also enabled by default for other run configs under the /test command.
    gameTestServer {
        systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
    }

    data {
        // example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it
        // workingDirectory project.file('run-data')

        // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
        programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath()
    }
}

// Include resources generated by data generators.
sourceSets.main.resources { srcDir 'src/generated/resources' }

// Sets up a dependency configuration called 'localRuntime'.
// This configuration should be used instead of 'runtimeOnly' to declare
// a dependency that will be present for runtime testing but that is
// "optional", meaning it will not be pulled by dependents of this mod.
configurations {
    runtimeClasspath.extendsFrom localRuntime
}

dependencies {
    implementation "net.neoforged:neoforge:${neo_version}"

    // Google drive API
    implementation 'com.google.api-client:google-api-client:+'
    implementation 'com.google.oauth-client:google-oauth-client-jetty:+'
    implementation 'com.google.apis:google-api-services-drive:+'
    jarJar 'com.google.api-client:google-api-client:+'
    jarJar 'com.google.oauth-client:google-oauth-client-jetty:+'
    jarJar 'com.google.apis:google-api-services-drive:+'
}

tasks.named('jarJar') {
    archiveClassifier = ''
}

tasks.named('jar') {
    archiveClassifier = 'slim'
}

// This block of code expands all declared replace properties in the specified resource targets.
// A missing property will result in an error. Properties are expanded using ${} Groovy notation.
// When "copyIdeResources" is enabled, this will also run before the game launches in IDE environments.
// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html
tasks.withType(ProcessResources).configureEach {
    var replaceProperties = [
            minecraft_version      : minecraft_version,
            minecraft_version_range: minecraft_version_range,
            neo_version            : neo_version,
            neo_version_range      : neo_version_range,
            loader_version_range   : loader_version_range,
            mod_id                 : mod_id,
            mod_name               : mod_name,
            mod_license            : mod_license,
            mod_version            : mod_version,
            mod_authors            : mod_authors,
            mod_description        : mod_description
    ]
    inputs.properties replaceProperties

    filesMatching(['META-INF/neoforge.mods.toml']) {
        expand replaceProperties
    }
}

// Example configuration to allow publishing using the maven-publish plugin
publishing {
    publications {
        register('mavenJava', MavenPublication) {
            from components.java
        }
    }
    repositories {
        maven {
            url "file://${project.projectDir}/repo"
        }
    }
}

tasks.withType(JavaCompile).configureEach {
    options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
}

// IDEA no longer automatically downloads sources/javadoc jars for dependencies, so we need to explicitly enable the behavior.
idea {
    module {
        downloadSources = true
        downloadJavadoc = true
    }
}

error message:

java.lang.NoClassDefFoundError: com/google/api/client/json/JsonFactory
lukebemish commented 3 months ago

This is already fully possible. You can include the dependency and all its transitive deps simply by making the jarJar configuration -- or whatever other configuration you choose to use for jarJar -- transitive. Note that you will have to manually filter out any deps that MC already comes with, and that as this is a non-mod dependency you will have to add it to the runs via the convention or the runs DSL -- and that bit may not support transitive deps as easily though it should still be possible with a file collection buffer.