Handling duplicate entries (ZipException: duplicate entry)


(German Vekhorev) #1

Hello,

I use some libraries in my Gradle project which have also got other libraries inside them and so on. As a result, I get many duplicate files in my final JAR, e.g. several LICENSE files or several javax packages.

I have already found a way to exclude certain files:

jar {
    from {
        configurations.embed.collect {
            exclude '.project'
            exclude '.classpath'
            exclude 'about.html'
            exclude 'LICENSE'
            exclude 'NOTICE'
            exclude 'LICENSE.txt'
            exclude 'NOTICE.txt'
            exclude 'META-INF/*'
            exclude 'META-INF/NOTICE'
            exclude 'META-INF/LICENSE.txt'
            exclude 'META-INF/NOTICE.txt'
            exclude 'META-INF/DEPENDENCIES'

            it.isDirectory() ? it : zipTree(it)
        }
    }
}

This works fine, however, there are also tons of classes duplicated, e.g.:

:reobfJar FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':reobfJar'.
> java.util.zip.ZipException: duplicate entry: javax/annotation/CheckForNull.class

I’d liked to ask how to exclude a whole directory like javax/, but I guess classes that directory contains may be essential for some libraries I use. So my question is:

how do I properly handle the “duplicate entry” issue in Gradle?

Is there a way to, for example, just skip this error and simply ignore files that are already present in the JAR or something like that?

My whole build.gradle file:

buildscript {
    repositories {
        jcenter()
        maven {
            name = "forge"
            url = "http://files.minecraftforge.net/maven"
        }
    }

    dependencies {
        classpath 'net.minecraftforge.gradle:ForgeGradle:2.1-SNAPSHOT'
    }
}

repositories {
    mavenLocal()
    mavenCentral()
}

apply plugin: 'net.minecraftforge.gradle.forge'

version = "1.0.0"
group = "club.pvpnation.forgemod"
archivesBaseName = "pvpn-mod"

sourceCompatibility = 1.8
targetCompatibility = 1.8

minecraft {
    version = "1.8.9-11.15.1.2318-1.8.9"
    runDir = "run"
    
    // the mappings can be changed at any time, and must be in the following format.
    // snapshot_YYYYMMDD   snapshot are built nightly.
    // stable_#            stables are built at the discretion of the MCP team.
    // Use non-default mappings at your own risk. they may not allways work.
    // simply re-run your setup task after changing the mappings to update your workspace.
    mappings = "stable_20"
    // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
}

configurations {
    embed
    compile.extendsFrom embed
}

dependencies {
    // Embed in JAR
    embed group: 'org.reflections', name: 'reflections', version: '0.9.10'
    embed group: 'me.darksidecode.kantanj', name: 'kantanj', version: '1.0.0'

    // Annotation Processors
    /* annotationProcessor - Gradle 5+ */ compile group: 'org.projectlombok', name: 'lombok', version: '1.18.6'
}

processResources {
    // this will ensure that this task is redone when the versions change.
    inputs.property "version", project.version
    inputs.property "mcversion", project.minecraft.version

    // replace stuff in mcmod.info, nothing else
    from (sourceSets.main.resources.srcDirs) {
        include 'mcmod.info'
                
        // replace version and mcversion
        expand 'version':project.version, 'mcversion':project.minecraft.version
    }
        
    // copy everything else, thats not the mcmod.info
    from (sourceSets.main.resources.srcDirs) {
        exclude 'mcmod.info'
    }
}

// https://gist.github.com/matthewprenger/9b2da059b89433a01c1c
task signJar(type: SignJar, dependsOn: reobfJar) {
    // gradle.properties
    keyStore = project.keyStore // This needs to be a path to the keystore file
    alias = project.keyStoreAlias
    storePass = project.keyStorePass
    keyPass = project.keyStoreKeyPass
    inputFile = jar.archivePath
    outputFile = jar.archivePath
}

//noinspection GrUnresolvedAccess
build.dependsOn signJar

// Include dependencies in JAR
jar {
    from {
        configurations.embed.collect {
            exclude 'javax'
            exclude '.project'
            exclude '.classpath'
            exclude 'about.html'
            exclude 'LICENSE'
            exclude 'NOTICE'
            exclude 'LICENSE.txt'
            exclude 'NOTICE.txt'
            exclude 'META-INF/*'
            exclude 'META-INF/NOTICE'
            exclude 'META-INF/LICENSE.txt'
            exclude 'META-INF/NOTICE.txt'
            exclude 'META-INF/DEPENDENCIES'

            it.isDirectory() ? it : zipTree(it)
        }
    }
}