Usare Gradle per costruire un vaso con dipendenze


122

Ho una build multiprogetto e ho inserito un compito per costruire un fat jar in uno dei sottoprogetti. Ho creato l'attività simile a quella descritta nel ricettario .

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

Eseguendolo si ottiene il seguente errore:

Causa: non è possibile modificare una configurazione che non è in uno stato irrisolto!

Non sono sicuro di cosa significhi questo errore. Ho anche segnalato questo su Gradle JIRA nel caso fosse un bug .

Risposte:


195

Aggiornamento: nelle versioni Gradle più recenti (4+) il compilequalificatore è deprecato a favore del nuovo apie delle implementationconfigurazioni. Se li usi, dovrebbe funzionare quanto segue:

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

Per le versioni gradle precedenti, o se usi ancora il qualificatore "compile" per le tue dipendenze, dovrebbe funzionare:

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

Nota che mainClassNamedeve apparire PRIMA jar {.


4
Ho dovuto modificarlo in configurations.runtime.collect per il mio progetto poiché ho anche dipendenze di runtime.
— vextorspace

2
Ho dovuto aggiungere def mainClassNameper far funzionare il codice ... Stavo ricevendo Impossibile impostare la proprietà sconosciuta 'mainClassName' per il progetto root
— hanskoff

1
Come gestisci le collisioni di nomi di file? I file sullo stesso percorso in diversi JAR verranno sovrascritti.
— wst

3
Purtroppo questo non funziona più. Uso gradle 4.10 e la nuova implementationconfigurazione invece dell'ormai deprecato compile. Il codice sopra mi crea un piccolo vaso senza le dipendenze. Quando lo cambio ( from { configurations.implementation.collect {...} }), si verifica un errore dicendo che la risoluzione diretta dell '"implementazione" della configurazione non è consentita
— Bastian Voigt

1
@BastianVoigt configurations.compileClasspathrisolverà tutti i messaggi di posta elettronica implementation, ma apitralascerà le dipendenze afik. Trovata qui in un'altra risposta la soluzione runtimeClasspath. Ciò include anche le apidipendenze.
— rekire

64

La risposta di @felix mi ha quasi portato lì. Ho avuto due problemi:

  1. Con Gradle 1.5, il tag manifest non è stato riconosciuto all'interno dell'attività fatJar, quindi l'attributo Main-Class non può essere impostato direttamente
  2. il jar aveva file META-INF esterni in conflitto.

La seguente configurazione risolve questo problema

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
}

Per aggiungerlo all'attività di assemblaggio o creazione standard, aggiungi:

artifacts {
    archives fatJar
}

Modifica: grazie a @mjaggard: nelle versioni recenti di Gradle, configurations.runtimepassa aconfigurations.runtimeClasspath


3
Questo ha anche risolto un problema che avevo in cui era firmato uno dei miei vasi di dipendenza. I file della firma sono stati inseriti nel META-INF del mio vaso, ma la firma non corrisponde più al contenuto.
— Flavin

2
Un ringraziamento speciale per artifacts: esattamente quello che stavo cercando.
— AlexR

Quando esegui gradle fatJarle dipendenze di runtime non sembrano essere compilate, quindi non possono essere copiate.
— mjaggard

64

Se desideri che l' jarattività si comporti normalmente e abbia anche un'attività aggiuntiva fatJar, utilizza quanto segue:

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

La parte importante è with jar. Senza di esso, le classi di questo progetto non sono incluse.


1
Vedi anche il seguente problema se stai usando vasi firmati da includere e riscontri un problema con le firme: stackoverflow.com/questions/999489/…
— Peter N. Steinmetz

6
Questo non funziona. Il file manifest è vuoto con questa soluzione.
— Jonas

4
I miei 2 centesimi: è meglio impostare un classificatore che cambiare il nome. Inserisci classifier = 'all' invece di baseName = project.name + '-all'. In questo modo mantieni il nome del manufatto in conformità con le norme Maven / Nexus.
— taciosd

1
Aggiungi group "build"e questa attività sarà in buildgruppo (con altre attività, ad esempio jarattività.
— MAGx2

1
Non riesco a trovare alcun tipo di documentazione sulla with jarparola chiave, cosa fa esattamente?
— Philipp Hemmelmayr

9

Questo funziona bene per me.

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

}

Ed è il contenuto del mio file 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
}

E scrivo quanto segue nella mia console:

java -jar ProyectoEclipseTest-all.jar

E il risultato è ottimo:

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

6

Per generare un JAR grasso con una classe eseguibile principale, evitando problemi con i JAR firmati, suggerisco il plugin gradle-one-jar . Un semplice plugin che utilizza il progetto One-JAR .

Facile da usare:

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

5

Semplice soluzione

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

5

La risposta di @ben funziona quasi per me tranne per il fatto che le mie dipendenze sono troppo grandi e ho ricevuto il seguente errore

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

  To build this archive, please enable the zip64 extension.

Per risolvere questo problema, devo utilizzare il seguente codice

mainClassName = "com.company.application.Main"

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

1

Per chi ha bisogno di realizzare più di un vaso dal progetto.

Crea una funzione 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'
    }
}

quindi chiama:

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

Funziona su gradle 5.

Jar sarà posizionato in ./build/libs.


0

Uso task shadowJarby plugin. com.github.jengelman.gradle.plugins:shadow:5.2.0

L'utilizzo del ./gradlew app::shadowJar file dei risultati appena eseguito sarà inMyProject/app/build/libs/shadow.jar

build.gradlefile di primo livello :

 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.gradlefile a livello di modulo dell'app

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


0

Gradle 6.3, libreria Java. Il codice da "jar task" aggiunge le dipendenze a "build / libs / xyz.jar" durante l'esecuzione del task " gradle build ".

plugins {
    id 'java-library'
}

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

-1

Se sei abituato a formica, potresti provare lo stesso anche con Gradle:

task bundlemyjava{
    ant.jar(destfile: "build/cookmyjar.jar"){
        fileset(dir:"path to your source", includes:'**/*.class,*.class', excludes:'if any')
        } 
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.