ใช้ Gradle เพื่อสร้าง jar ที่มีการอ้างอิง


122

ฉันมีโครงการสร้างหลายโครงการและฉันได้มอบหมายงานเพื่อสร้างโถไขมันในโครงการย่อยอย่างใดอย่างหนึ่ง ฉันสร้างงานที่คล้ายกับที่อธิบายไว้ในตำรา

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

การเรียกใช้จะทำให้เกิดข้อผิดพลาดต่อไปนี้:

สาเหตุ: คุณไม่สามารถเปลี่ยนการกำหนดค่าที่ไม่ได้อยู่ในสถานะที่ไม่ได้รับการแก้ไข!

ฉันไม่แน่ใจว่าข้อผิดพลาดนี้หมายถึงอะไร ฉันยังรายงานเรื่องนี้ใน Gradle จิระในกรณีที่เป็นข้อผิดพลาด

คำตอบ:


196

อัปเดต: ใน Gradle เวอร์ชันที่ใหม่กว่า (4+) compileQualifier จะถูกเลิกใช้เพื่อสนับสนุนการกำหนดค่าใหม่apiและ implementationหากคุณใช้สิ่งเหล่านี้สิ่งต่อไปนี้จะเหมาะกับคุณ:

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

สำหรับรุ่น gradle ที่เก่ากว่าหรือหากคุณยังคงใช้คุณสมบัติ "คอมไพล์" สำหรับการอ้างอิงของคุณสิ่งนี้ควรได้ผล:

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

โปรดทราบว่าจะต้องปรากฏก่อนmainClassNamejar {


4
ฉันต้องแก้ไขสิ่งนี้เป็น configurations.runtime.collect สำหรับโครงการของฉันเนื่องจากฉันมีการอ้างอิงรันไทม์ด้วย
vextorspace

2
ฉันต้องเพิ่มdef mainClassNameเพื่อให้โค้ดทำงาน ... ฉันได้รับไม่สามารถตั้งค่าคุณสมบัติที่ไม่รู้จัก 'mainClassName' สำหรับโปรเจ็กต์รูท
hanskoff

1
คุณจัดการกับการชนชื่อไฟล์ได้อย่างไร? ไฟล์บนพา ธ เดียวกันใน JAR ต่างกันจะถูกเขียนทับ
wst

3
น่าเสียดายที่ไม่สามารถใช้งานได้อีกต่อไป ผมใช้ gradle 4.10 ใหม่และการตั้งค่าแทนการเลิกใช้ในขณะนี้implementation compileโค้ดด้านบนสร้าง jar ขนาดเล็กโดยไม่ต้องพึ่งพา เมื่อฉันเปลี่ยน ( from { configurations.implementation.collect {...} }) ข้อผิดพลาดเกิดขึ้นโดยบอกว่าไม่อนุญาตให้แก้ไขการกำหนดค่า 'การใช้งาน' โดยตรง
Bastian Voigt

1
@BastianVoigt configurations.compileClasspathจะแก้ไขimplementations ทั้งหมดแต่จะละทิ้งการapiอ้างอิง afik runtimeClasspathพบได้ที่นี่ในคำตอบของการแก้ปัญหาอื่น ซึ่งรวมถึงการapiอ้างอิงด้วย
rekire

64

คำตอบโดย @felix เกือบจะนำฉันไปที่นั่น ฉันมีสองประเด็น:

  1. ด้วย Gradle 1.5 ไม่รู้จักแท็กรายการภายในงาน fatJar ดังนั้นจึงไม่สามารถตั้งค่าแอตทริบิวต์ Main-Class ได้โดยตรง
  2. jar มีไฟล์ META-INF ภายนอกที่ขัดแย้งกัน

การตั้งค่าต่อไปนี้ช่วยแก้ปัญหานี้ได้

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
}

หากต้องการเพิ่มสิ่งนี้ในงานการประกอบหรือสร้างมาตรฐานให้เพิ่ม:

artifacts {
    archives fatJar
}

แก้ไข: ขอบคุณ @mjaggard: ใน Gradle เวอร์ชันล่าสุดเปลี่ยนconfigurations.runtimeเป็นconfigurations.runtimeClasspath


3
นอกจากนี้ยังแก้ไขปัญหาที่ฉันพบเมื่อมีการลงนามในไหการพึ่งพาของฉัน ไฟล์ลายเซ็นถูกใส่ไว้ใน META-INF ของ jar ของฉัน แต่ลายเซ็นไม่ตรงกับเนื้อหาอีกต่อไป
Flavin

2
ขอขอบคุณเป็นพิเศษสำหรับartifacts: สิ่งที่ฉันกำลังมองหา
AlexR

เมื่อคุณเรียกใช้gradle fatJarการอ้างอิงรันไทม์ดูเหมือนจะไม่ถูกคอมไพล์ดังนั้นจึงไม่สามารถคัดลอกได้
mjaggard

64

หากคุณต้องการให้jarงานทำงานตามปกติและมีfatJarงานเพิ่มเติมให้ใช้ดังต่อไปนี้:

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

ส่วนที่สำคัญคือwith jar. หากไม่มีคลาสของโครงการนี้จะไม่รวมอยู่ด้วย


1
นอกจากนี้โปรดดูปัญหาต่อไปนี้หากคุณใช้ขวดโหลที่มีลายเซ็นเพื่อรวมและพบปัญหาเกี่ยวกับลายเซ็น: stackoverflow.com/questions/999489/…
Peter N. Steinmetz

6
สิ่งนี้ใช้ไม่ได้ ไฟล์ Manifest ว่างเปล่าเมื่อใช้โซลูชันนี้
Jonas

4
2 เซ็นต์ของฉัน: การตั้งค่าลักษณนามจะดีกว่าการเปลี่ยนชื่อ ใส่ลักษณนาม = 'ทั้งหมด' แทน baseName = project.name + '-all' ด้วยวิธีนี้จะทำให้ชื่ออาร์ติแฟกต์เป็นไปตามนโยบายของ Maven / Nexus
taciosd

1
เพิ่มgroup "build"และงานนี้จะอยู่ในbuildกลุ่ม (กับงานอื่น ๆ เช่นjarงาน
MAGx2

1
ฉันไม่พบเอกสารเกี่ยวกับwith jarคีย์เวิร์ดใด ๆ มันทำหน้าที่อะไรกันแน่?
Philipp Hemmelmayr

9

วิธีนี้ใช้ได้ดีสำหรับฉัน

ชั้นเรียนหลักของฉัน:

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

}

และเป็นเนื้อหาของไฟล์ 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
}

และฉันเขียนสิ่งต่อไปนี้ในคอนโซลของฉัน:

java -jar ProyectoEclipseTest-all.jar

และผลผลิตดีมาก:

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

เพื่อสร้าง JAR ไขมันกับระดับปฏิบัติการหลักหลีกเลี่ยงปัญหาเกี่ยวกับการลงนามไหผมขอแนะนำปลั๊กอิน gradle-หนึ่งขวด ปลั๊กอินง่ายๆที่ใช้ในโครงการ One-JAR

ง่ายต่อการใช้:

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

คำตอบจาก @ben เกือบจะใช้ได้กับฉันยกเว้นว่าการอ้างอิงของฉันใหญ่เกินไปและฉันได้รับข้อผิดพลาดต่อไปนี้

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

  To build this archive, please enable the zip64 extension.

ในการแก้ไขปัญหานี้ฉันต้องใช้รหัสต่อไปนี้

mainClassName = "com.company.application.Main"

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

1

สำหรับผู้ที่ต้องการสร้างมากกว่าหนึ่งขวดจากโครงการ

สร้างฟังก์ชันใน 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'
    }
}

จากนั้นโทร:

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

ทำงานบน gradle 5.

./build/libsขวดจะถูกวางไว้ที่


0

ฉันใช้งานshadowJarโดยปลั๊กอิน. com.github.jengelman.gradle.plugins:shadow:5.2.0

การใช้งานเพียงแค่เรียกใช้./gradlew app::shadowJar ไฟล์ผลลัพธ์จะอยู่ที่MyProject/app/build/libs/shadow.jar

build.gradleไฟล์ระดับบนสุด:

 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.gradleไฟล์ระดับโมดูลแอป

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 ไลบรารี Java โค้ดจาก "jar task" จะเพิ่มการอ้างอิงให้กับ "build / libs / xyz.jar" เมื่อรันงาน " gradle build "

plugins {
    id 'java-library'
}

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

-1

หากคุณคุ้นเคยกับมดคุณสามารถลองใช้ Gradle ได้เช่นกัน:

task bundlemyjava{
    ant.jar(destfile: "build/cookmyjar.jar"){
        fileset(dir:"path to your source", includes:'**/*.class,*.class', excludes:'if any')
        } 
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.