Verwenden von Gradle zum Erstellen eines JARs mit Abhängigkeiten

122

Ich habe einen Multiprojekt-Build und habe die Aufgabe gestellt, in einem der Teilprojekte ein Fettglas zu bauen. Ich habe die Aufgabe ähnlich der im Kochbuch beschriebenen erstellt .

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

Das Ausführen führt zu folgendem Fehler:

Ursache: Sie können keine Konfiguration ändern, die sich nicht im ungelösten Zustand befindet!

Ich bin mir nicht sicher, was dieser Fehler bedeutet. Ich habe dies auch auf dem Gradle JIRA gemeldet, falls es sich um einen Fehler handelt .

Ben McCann
quelle

Antworten:

195

Update: In neueren Gradle-Versionen (4+) ist das compileQualifikationsmerkmal zugunsten der neuen apiund implementationKonfigurationen veraltet . Wenn Sie diese verwenden, sollte Folgendes für Sie funktionieren:

// 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) }
  }
}

Für ältere Gradle-Versionen oder wenn Sie immer noch das Qualifikationsmerkmal "Kompilieren" für Ihre Abhängigkeiten verwenden, sollte dies funktionieren:

// 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) }
  }
}

Beachten Sie, dass mainClassNameVOR dem Erscheinen angezeigt werden muss jar {.

Ben McCann
quelle
4
Ich musste dies für mein Projekt in configuration.runtime.collect ändern, da ich auch Laufzeitabhängigkeiten habe.
vextorspace
2
Ich musste hinzufügen def mainClassName, damit der Code funktioniert ... Ich erhielt Die unbekannte Eigenschaft 'mainClassName' für das Root-Projekt konnte nicht festgelegt werden
hanskoff
1
Wie gehen Sie mit Dateinamenkollisionen um? Dateien auf demselben Pfad in verschiedenen JARs werden überschrieben.
wst
3
Das funktioniert leider nicht mehr. Ich benutze Gradle 4.10 und die neue implementationKonfiguration anstelle der jetzt veralteten compile. Der obige Code baut mir ein kleines Glas ohne die Abhängigkeiten auf. Wenn ich es ändere ( from { configurations.implementation.collect {...} }), tritt ein Fehler auf, der besagt, dass das direkte Auflösen der Konfigurationsimplementierung nicht zulässig ist
Bastian Voigt
1
@BastianVoigt configurations.compileClasspathwird alle implementations reparieren , aber die apiAbhängigkeiten afik weglassen. Hier in einer anderen Antwort die Lösung gefunden runtimeClasspath. Das schließt auch die apiAbhängigkeiten ein.
rekire
64

Die Antwort von @felix hätte mich fast dorthin gebracht. Ich hatte zwei Probleme:

  1. Mit Gradle 1.5 wurde das Manifest-Tag innerhalb der fatJar-Task nicht erkannt, sodass das Hauptklassenattribut nicht direkt festgelegt werden konnte
  2. Das Glas hatte widersprüchliche externe META-INF-Dateien.

Das folgende Setup behebt dieses Problem

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
}

Fügen Sie Folgendes hinzu, um dies zur Standard-Assembler- oder Build-Aufgabe hinzuzufügen:

artifacts {
    archives fatJar
}

Bearbeiten: dank @mjaggard: in neueren Versionen von Gradle wechseln Sie configurations.runtimezuconfigurations.runtimeClasspath

blootsvoets
quelle
3
Dies behebt auch ein Problem, bei dem eines meiner Abhängigkeitsgläser signiert war. Die Signaturdateien wurden in das META-INF meines Glases gestellt, aber die Signatur stimmte nicht mehr mit dem Inhalt überein.
Flavin
2
Besonderer Dank für artifacts: genau das, wonach ich gesucht habe.
AlexR
Wenn Sie gradle fatJardie Laufzeitabhängigkeiten ausführen, scheinen sie nicht kompiliert zu sein, sodass sie nicht kopiert werden können.
mjaggard
64

Wenn Sie möchten, dass sich die jarAufgabe normal verhält und eine zusätzliche fatJarAufgabe hat, verwenden Sie Folgendes:

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

Der wichtige Teil ist with jar. Ohne sie sind die Klassen dieses Projekts nicht enthalten.

Felix
quelle
1
Siehe auch das folgende Problem, wenn Sie signierte Gläser verwenden, um Signaturen einzuschließen,
Peter N. Steinmetz
6
Das funktioniert nicht. Die Manifest-Datei ist bei dieser Lösung leer.
Jonas
4
Meine 2 Cent: Es ist besser, einen Klassifikator zu setzen, als den Namen zu ändern. Setzen Sie classifier = 'all' anstelle von baseName = project.name + '-all'. Auf diese Weise behalten Sie den Artefaktnamen in Übereinstimmung mit den Maven / Nexus-Richtlinien.
Taciosd
1
Hinzufügen group "build"und diese Aufgabe wird in buildGruppe sein (mit anderen Aufgaben, dh jarAufgabe.
MAGx2
1
Ich kann keine Dokumentation zum with jarKeyword finden. Was genau macht es?
Philipp Hemmelmayr
9

Das funktioniert gut für mich.

Meine Hauptklasse:

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");
    }

}

Und es ist der Inhalt meiner Datei 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
}

Und ich schreibe folgendes in meine Konsole:

java -jar ProyectoEclipseTest-all.jar

Und die Ausgabe ist großartig:

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
quelle
6

Um eine fette JAR mit einer ausführbaren Hauptklasse zu generieren und Probleme mit signierten JARs zu vermeiden, empfehle ich das Gradle-One-Jar-Plugin . Ein einfaches Plugin, das das One-JAR-Projekt verwendet .

Einfach zu verwenden:

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
quelle
5

Einfache Lösung

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

Die Antwort von @ben funktioniert fast für mich, außer dass meine Abhängigkeiten zu groß sind und ich den folgenden Fehler erhalten habe

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

  To build this archive, please enable the zip64 extension.

Um dieses Problem zu beheben, muss ich den folgenden Code verwenden

mainClassName = "com.company.application.Main"

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

Für diejenigen, die mehr als ein Glas aus dem Projekt bauen müssen.

Erstellen Sie eine Funktion in 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'
    }
}

Dann ruf an:

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

Funktioniert auf Gradle 5.

Jar wird bei platziert ./build/libs.

MiguelSlv
quelle
0

Ich benutze Aufgabe shadowJarper Plugin. com.github.jengelman.gradle.plugins:shadow:5.2.0

Die gerade ausgeführte ./gradlew app::shadowJar Ergebnisdatei wird bei verwendetMyProject/app/build/libs/shadow.jar

build.gradleDatei der obersten Ebene :

 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'
    }
}

App-Modul-Level- build.gradleDatei

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
quelle
0

Gradle 6.3, Java-Bibliothek. Der Code aus "jar task" fügt die Abhängigkeiten zur "build / libs / xyz.jar" hinzu, wenn die Aufgabe " gradle build " ausgeführt wird.

plugins {
    id 'java-library'
}

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

Wenn Sie an Ameisen gewöhnt sind, können Sie dies auch mit Gradle versuchen:

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