Menggunakan Gradle untuk membuat jar dengan dependensi

122

Saya memiliki build multiproyek dan saya memberi tugas untuk membangun tabung lemak di salah satu subproyek. Saya membuat tugas yang mirip dengan yang dijelaskan di buku masak .

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

Menjalankannya menghasilkan kesalahan berikut:

Penyebab: Anda tidak dapat mengubah konfigurasi yang bukan dalam keadaan tidak terselesaikan!

Saya tidak yakin apa arti kesalahan ini. Saya juga melaporkan ini di Gradle JIRA seandainya itu adalah bug .

Ben McCann
sumber

Jawaban:

195

Update: Dalam versi Gradle yang lebih baru (4+), compilequalifier tidak digunakan lagi untuk mendukung konfigurasi apidan yang baru implementation. Jika Anda menggunakan ini, berikut ini akan bekerja untuk Anda:

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

Untuk versi gradle yang lebih lama, atau jika Anda masih menggunakan qualifier "compile" untuk dependensi Anda, ini akan berfungsi:

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

Catatan yang mainClassNameharus muncul SEBELUM jar {.

Ben McCann
sumber
4
Saya harus memodifikasi ini menjadi configurations.runtime.collect untuk proyek saya karena saya juga memiliki dependensi runtime.
vextorspace
2
Saya harus menambahkan def mainClassNameagar kode berfungsi ... Saya menerima Tidak dapat mengatur properti yang tidak diketahui 'mainClassName' untuk proyek root
hanskoff
1
Bagaimana Anda menangani tabrakan nama file? File di jalur yang sama di JAR berbeda akan ditimpa.
wst
3
Sayangnya ini tidak berfungsi lagi. Saya menggunakan gradle 4.10 dan implementationkonfigurasi baru, bukan yang sekarang sudah usang compile. Kode di atas membuatkan saya toples kecil tanpa ketergantungan. Ketika saya mengubahnya ( from { configurations.implementation.collect {...} }), terjadi kesalahan yang mengatakan bahwa menyelesaikan 'implementasi' konfigurasi secara langsung tidak diizinkan
Bastian Voigt
1
@BastianVoigt configurations.compileClasspathakan memperbaiki semua implementation, tetapi akan meninggalkan apidependensi afik. Ditemukan di sini di jawaban lain solusinya runtimeClasspath. Itu termasuk apidependensi juga.
rekire
64

Jawaban dari @felix hampir membawaku kesana. Saya punya dua masalah:

  1. Dengan Gradle 1.5, tag manifes tidak dikenali di dalam tugas fatJar, sehingga atribut Main-Class tidak bisa langsung disetel
  2. tabung memiliki file META-INF eksternal yang bentrok.

Setup berikut menyelesaikan ini

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
}

Untuk menambahkan ini ke tugas assemble atau build standar, tambahkan:

artifacts {
    archives fatJar
}

Edit: terima kasih kepada @mjaggard: di Gradle versi terbaru, ubah configurations.runtimemenjadiconfigurations.runtimeClasspath

blootsvoets
sumber
3
Ini juga memperbaiki masalah yang saya alami di mana salah satu stoples ketergantungan saya ditandatangani. File tanda tangan dimasukkan ke META-INF jar saya, tetapi tanda tangan tidak lagi cocok dengan isinya.
Flavin
2
Terima kasih khusus untuk artifacts: persis seperti yang saya cari.
AlexR
Ketika Anda menjalankan gradle fatJarruntime, dependensi tampaknya tidak dikompilasi, sehingga tidak dapat disalin.
mjaggard
64

Jika Anda ingin jartugas berperilaku normal dan juga memiliki fatJartugas tambahan , gunakan yang berikut ini:

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

Bagian yang penting adalah with jar. Tanpanya, kelas proyek ini tidak termasuk.

Felix
sumber
1
Juga lihat masalah berikut jika Anda menggunakan toples bertanda tangan untuk disertakan dan mengalami masalah dengan tanda tangan: stackoverflow.com/questions/999489/…
Peter N. Steinmetz
6
Ini tidak bekerja. File Manifest kosong dengan solusi ini.
Jonas
4
2 sen saya: Lebih baik menetapkan pengklasifikasi daripada mengubah nama. Letakkan classifier = 'all' sebagai ganti baseName = project.name + '-all'. Dengan cara itu Anda menjaga nama artefak sesuai dengan kebijakan Maven / Nexus.
taciosd
1
Tambah group "build"dan tugas ini akan builddikelompokkan (dengan tugas lain, yaitu jartugas.
MAGx2
1
Saya tidak dapat menemukan jenis dokumentasi apa pun tentang with jarkata kunci, apa sebenarnya fungsinya?
Philipp Hemmelmayr
9

Ini bekerja dengan baik untuk saya.

Kelas Utama saya:

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

}

Dan itu adalah konten file saya 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
}

Dan saya menulis yang berikut ini di konsol saya:

java -jar ProyectoEclipseTest-all.jar

Dan hasilnya bagus:

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

Untuk menghasilkan JAR gemuk dengan kelas utama yang dapat dieksekusi, untuk menghindari masalah dengan JAR yang ditandatangani, saya sarankan plugin gradle-one-jar . Plugin sederhana yang menggunakan proyek One-JAR .

Mudah digunakan:

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

Sulusi sederhana

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

Jawaban dari @ben hampir berhasil untuk saya kecuali dependensi saya terlalu besar dan saya mendapat error berikut

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

  To build this archive, please enable the zip64 extension.

Untuk memperbaiki masalah ini, saya harus menggunakan kode berikut

mainClassName = "com.company.application.Main"

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

Bagi mereka yang perlu membangun lebih dari satu toples dari proyek tersebut.

Buat fungsi dalam 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'
    }
}

lalu hubungi:

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

Bekerja pada gradle 5.

Stoples akan ditempatkan di ./build/libs.

MiguelSlv
sumber
0

Saya menggunakan tugas shadowJardengan plugin. com.github.jengelman.gradle.plugins:shadow:5.2.0

Penggunaan hanya menjalankan ./gradlew app::shadowJar file hasil akan diMyProject/app/build/libs/shadow.jar

build.gradlefile tingkat atas :

 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 tingkat modul aplikasi

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

Gradle 6.3, perpustakaan Java. Kode dari "tugas jar" menambahkan dependensi ke "build / libs / xyz.jar" saat menjalankan tugas " gradle build ".

plugins {
    id 'java-library'
}

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

Jika Anda terbiasa dengan semut, Anda dapat mencoba hal yang sama dengan Gradle juga:

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