Utilisation de Gradle pour créer un fichier jar avec des dépendances

122

J'ai une construction multiprojet et j'ai mis une tâche pour construire un gros pot dans l'un des sous-projets. J'ai créé la tâche similaire à celle décrite dans le livre de cuisine .

jar {
  from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
  manifest { attributes 'Main-Class': 'com.benmccann.gradle.test.WebServer' }
}

L'exécuter entraîne l'erreur suivante:

Cause: vous ne pouvez pas modifier une configuration qui n'est pas dans un état non résolu!

Je ne sais pas ce que signifie cette erreur. J'ai également signalé cela sur le Gradle JIRA au cas où il s'agirait d'un bug .

Ben McCann
la source

Réponses:

195

Mise à jour: dans les versions plus récentes de Gradle (4+), le compilequalificatif est obsolète au profit des nouvelles configurations apiet implementation. Si vous les utilisez, les éléments suivants devraient fonctionner pour vous:

// Include dependent libraries in archive.
mainClassName = "com.company.application.Main"

jar {
  manifest { 
    attributes "Main-Class": "$mainClassName"
  }  

  from {
    configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
  }
}

Pour les anciennes versions de Gradle, ou si vous utilisez toujours le qualificatif "compile" pour vos dépendances, cela devrait fonctionner:

// Include dependent libraries in archive.
mainClassName = "com.company.application.Main"

jar {
  manifest { 
    attributes "Main-Class": "$mainClassName"
  }  

  from {
    configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
  }
}

Notez que mainClassNamedoit apparaître AVANT jar {.

Ben McCann
la source
4
J'ai dû modifier cela en configurations.runtime.collect pour mon projet car j'ai également des dépendances d'exécution.
vextorspace
2
J'ai dû ajouter def mainClassNamepour que le code fonctionne ... Je recevais Impossible de définir la propriété inconnue 'mainClassName' pour le projet racine
hanskoff
1
Comment gérez-vous les collisions de noms de fichiers? Les fichiers sur le même chemin dans différents JAR seront écrasés.
wst
3
Malheureusement, cela ne fonctionne plus. J'utilise gradle 4.10 et la nouvelle implementationconfiguration au lieu du maintenant obsolète compile. Le code ci-dessus me construit un petit pot sans les dépendances. Quand je le change ( from { configurations.implementation.collect {...} }), une erreur se produit disant que la résolution directe de la `` mise en œuvre '' de la configuration n'est pas autorisée
Bastian Voigt
1
@BastianVoigt configurations.compileClasspathcorrigera tous les implementations, mais laissera de côté les apidépendances afik. Trouvé ici dans une autre réponse la solution runtimeClasspath. Cela inclut également les apidépendances.
rekire le
64

La réponse de @felix m'a presque amené là-bas. J'ai eu deux problèmes:

  1. Avec Gradle 1.5, la balise manifest n'était pas reconnue dans la tâche fatJar, donc l'attribut Main-Class ne pouvait pas être directement défini
  2. le jar contenait des fichiers META-INF externes en conflit.

La configuration suivante résout ce problème

jar {
  manifest {
    attributes(
      'Main-Class': 'my.project.main',
    )
  }
}

task fatJar(type: Jar) {
  manifest.from jar.manifest
  classifier = 'all'
  from {
    configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
  } {
    exclude "META-INF/*.SF"
    exclude "META-INF/*.DSA"
    exclude "META-INF/*.RSA"
  }
  with jar
}

Pour l'ajouter à la tâche d'assemblage ou de construction standard, ajoutez:

artifacts {
    archives fatJar
}

Edit: grâce à @mjaggard: dans les versions récentes de Gradle, passez configurations.runtimeàconfigurations.runtimeClasspath

blootsvoets
la source
3
Cela a également résolu un problème que j'avais où l'un de mes jar de dépendances était signé. Les fichiers de signature ont été placés dans le META-INF de mon pot, mais la signature ne correspondait plus au contenu.
Flavin
2
Un merci spécial pour artifacts: exactement ce que je cherchais.
AlexR
Lorsque vous exécutez gradle fatJarles dépendances d'exécution ne semblent pas être compilées, elles ne peuvent donc pas être copiées.
mjaggard le
64

Si vous souhaitez que la jartâche se comporte normalement et ait également une fatJartâche supplémentaire , utilisez ce qui suit:

task fatJar(type: Jar) {
    classifier = 'all'
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
    with jar
}

La partie importante est with jar. Sans lui, les classes de ce projet ne sont pas incluses.

Félix
la source
1
Consultez également le problème suivant si vous utilisez des fichiers jar signés pour inclure et rencontrez un problème avec les signatures: stackoverflow.com/questions/999489/…
Peter N. Steinmetz
6
Cela ne fonctionne pas. Le fichier Manifest est vide avec cette solution.
Jonas
4
Mes 2 centimes: il vaut mieux définir un classificateur que de changer le nom. Mettez classifier = 'all' au lieu de baseName = project.name + '-all'. De cette façon, vous gardez le nom de l'artefact en conformité avec les politiques Maven / Nexus.
taciosd
1
Ajouter group "build"et cette tâche sera en buildgroupe (avec d'autres tâches, par exemple jartâche.
MAGx2
1
Je ne trouve aucun type de documentation sur le with jarmot - clé, que fait-il exactement?
Philipp Hemmelmayr
9

Cela fonctionne bien pour moi.

Ma classe principale:

package com.curso.online.gradle;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

public class Main {

    public static void main(String[] args) {
        Logger logger = Logger.getLogger(Main.class);
        logger.debug("Starting demo");

        String s = "Some Value";

        if (!StringUtils.isEmpty(s)) {
            System.out.println("Welcome ");
        }

        logger.debug("End of demo");
    }

}

Et c'est le contenu de mon fichier build.gradle:

apply plugin: 'java'

apply plugin: 'eclipse'

repositories {
    mavenCentral()
}

dependencies {
    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
    testCompile group: 'junit', name: 'junit', version: '4.+'
    compile  'org.apache.commons:commons-lang3:3.0'
    compile  'log4j:log4j:1.2.16'
}

task fatJar(type: Jar) {
    manifest {
        attributes 'Main-Class': 'com.curso.online.gradle.Main'
    }
    baseName = project.name + '-all'
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
    with jar
}

Et j'écris ce qui suit dans ma console:

java -jar ProyectoEclipseTest-all.jar

Et la sortie est excellente:

log4j:WARN No appenders could be found for logger (com.curso.online.gradle.Main)
.
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more in
fo.
Welcome
Aron
la source
6

Pour générer un gros JAR avec une classe exécutable principale, en évitant les problèmes avec les JAR signés, je suggère le plugin gradle-one-jar . Un plugin simple qui utilise le projet One-JAR .

Facile à utiliser:

apply plugin: 'gradle-one-jar'

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.github.rholder:gradle-one-jar:1.0.4'
    }
}

task myjar(type: OneJar) {
    mainClass = 'com.benmccann.gradle.test.WebServer'
}
Italo Borssatto
la source
5

Sulution simple

jar {
    manifest {
        attributes 'Main-Class': 'cova2.Main'
    } 
    doFirst {
        from { configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) } }
    }
}
Jonas Mayer
la source
5

La réponse de @ben fonctionne presque pour moi sauf que mes dépendances sont trop grandes et que j'ai l'erreur suivante

Execution failed for task ':jar'.
> archive contains more than 65535 entries.

  To build this archive, please enable the zip64 extension.

Pour résoudre ce problème, je dois utiliser le code suivant

mainClassName = "com.company.application.Main"

jar {
  manifest { 
    attributes "Main-Class": "$mainClassName"
  }  
  zip64 = true
  from {
    configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
  }
}
Algorithme
la source
1

Pour ceux qui ont besoin de construire plus d'un pot à partir du projet.

Créez une fonction dans gradle:

void jarFactory(Jar jarTask, jarName, mainClass) {
    jarTask.doFirst {
        println 'Build jar ' + jarTask.name + + ' started'
    }

    jarTask.manifest {
        attributes(
                'Main-Class':  mainClass
        )
    }
    jarTask.classifier = 'all'
    jarTask.baseName = jarName
    jarTask.from {
        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
    {
        exclude "META-INF/*.SF"
        exclude "META-INF/*.DSA"
        exclude "META-INF/*.RSA"
    }
    jarTask.with jar 
    jarTask.doFirst {
        println 'Build jar ' + jarTask.name + ' ended'
    }
}

puis appelez:

task makeMyJar(type: Jar) {
    jarFactory(it, 'MyJar', 'org.company.MainClass')
}

Fonctionne sur gradle 5.

Jar sera placé à ./build/libs.

MiguelSlv
la source
0

J'utilise tâche shadowJarpar plugin. com.github.jengelman.gradle.plugins:shadow:5.2.0

L'utilisation juste exécuter le ./gradlew app::shadowJar fichier de résultat sera àMyProject/app/build/libs/shadow.jar

build.gradlefichier de niveau supérieur :

 apply plugin: 'kotlin'

buildscript {
    ext.kotlin_version = '1.3.61'

    repositories {
        mavenLocal()
        mavenCentral()
        jcenter()
    }

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0'
    }
}

build.gradlefichier de niveau de module d'application

apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'

sourceCompatibility = 1.8

kapt {
    generateStubs = true
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation "org.seleniumhq.selenium:selenium-java:4.0.0-alpha-4"
    shadow "org.seleniumhq.selenium:selenium-java:4.0.0-alpha-4"

    implementation project(":module_remote")
    shadow project(":module_remote")
}

jar {
    exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF'
    manifest {
        attributes(
                'Main-Class': 'com.github.kolyall.TheApplication',
                'Class-Path': configurations.compile.files.collect { "lib/$it.name" }.join(' ')
        )
    }
}

shadowJar {
    baseName = 'shadow'
    classifier = ''
    archiveVersion = ''
    mainClassName = 'com.github.kolyall.TheApplication'

    mergeServiceFiles()
}

NickUnuchek
la source
0

Gradle 6.3, bibliothèque Java. Le code de la "tâche jar" ajoute les dépendances à la tâche "build / libs / xyz.jar" lors de l'exécution de la tâche " gradle build ".

plugins {
    id 'java-library'
}

jar {
    from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    }
}
Alex Ureche
la source
-1

Si vous êtes habitué à la fourmi, vous pouvez également essayer la même chose avec Gradle:

task bundlemyjava{
    ant.jar(destfile: "build/cookmyjar.jar"){
        fileset(dir:"path to your source", includes:'**/*.class,*.class', excludes:'if any')
        } 
}
mig
la source