Sử dụng Gradle để xây dựng một jar với các phụ thuộc


122

Tôi có một bản xây dựng nhiều dự án và tôi đặt một nhiệm vụ là xây dựng một cái lọ béo trong một trong các dự án con. Tôi đã tạo tác vụ tương tự như tác vụ được mô tả trong sách dạy nấu ăn .

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

Chạy nó dẫn đến lỗi sau:

Nguyên nhân: Bạn không thể thay đổi cấu hình không ở trạng thái chưa được giải quyết!

Tôi không chắc lỗi này có nghĩa là gì. Tôi cũng đã báo cáo điều này trên Gradle JIRA trong trường hợp nó là một lỗi .

Câu trả lời:


195

Cập nhật: Trong các phiên bản Gradle mới hơn (4+), bộ định lượng compilekhông được dùng nữa để thay thế cho các cấu hình apiimplementationcấu hình mới. Nếu bạn sử dụng những thứ này, những thứ sau sẽ phù hợp với bạn:

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

Đối với các phiên bản gradle cũ hơn hoặc nếu bạn vẫn sử dụng bộ định lượng "biên dịch" cho các phần phụ thuộc của mình, điều này sẽ hoạt động:

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

Lưu ý là mainClassNamephải xuất hiện TRƯỚC jar {.


4
Tôi đã phải sửa đổi điều này thành configurations.runtime.collect cho dự án của mình vì tôi cũng có phụ thuộc thời gian chạy.
vextorspace

2
Tôi phải thêm def mainClassNameđể làm cho mã hoạt động ... Tôi đã nhận được Không thể đặt thuộc tính không xác định 'mainClassName' cho dự án gốc
hanskoff

1
Làm thế nào để bạn xử lý xung đột tên tệp? Các tệp trên cùng một đường dẫn trong các JAR khác nhau sẽ bị ghi đè.
WST

3
Thật không may, điều này không hoạt động nữa. Tôi sử dụng gradle 4.10 và implementationcấu hình mới thay vì cấu hình hiện không dùng nữa compile. Đoạn mã trên xây dựng cho tôi một cái lọ nhỏ mà không có phụ thuộc. Khi tôi thay đổi nó ( from { configurations.implementation.collect {...} }), một lỗi xảy ra nói rằng việc giải quyết các cấu hình 'thực hiện' trực tiếp không được phép
Bastian Voigt

1
@BastianVoigt configurations.compileClasspathsẽ sửa tất cả các implementations, nhưng sẽ loại bỏ các phần apiphụ thuộc. Tìm thấy ở đây trong một câu trả lời khác giải pháp runtimeClasspath. Điều đó cũng bao gồm các apiphụ thuộc.
rekire

64

Câu trả lời của @felix gần như đã đưa tôi đến đó. Tôi gặp hai vấn đề:

  1. Với Gradle 1.5, thẻ kê khai không được nhận dạng bên trong tác vụ fatJar, vì vậy không thể trực tiếp đặt thuộc tính Lớp chính
  2. jar có các tệp META-INF bên ngoài xung đột.

Thiết lập sau giải quyết vấn đề này

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
}

Để thêm điều này vào nhiệm vụ lắp ráp hoặc xây dựng tiêu chuẩn, hãy thêm:

artifacts {
    archives fatJar
}

Chỉnh sửa: cảm ơn @mjaggard: trong các phiên bản gần đây của Gradle, hãy thay đổi configurations.runtimethànhconfigurations.runtimeClasspath


3
Điều này cũng đã khắc phục sự cố mà tôi gặp phải khi một trong các lọ phụ thuộc của tôi đã được ký. Các tệp chữ ký đã được đưa vào META-INF của tôi, nhưng chữ ký không còn khớp với nội dung.
Flavin

2
Cảm ơn đặc biệt vì artifacts: chính xác những gì tôi đang tìm kiếm.
AlexR

Khi bạn chạy gradle fatJarcác phần phụ thuộc thời gian chạy dường như không được biên dịch, vì vậy chúng không thể được sao chép.
mjaggard

64

Nếu bạn muốn jartác vụ hoạt động bình thường và cũng có fatJartác vụ bổ sung , hãy sử dụng như sau:

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

Phần quan trọng là with jar. Không có nó, các lớp của dự án này không được bao gồm.


1
Xem thêm các vấn đề sau đây nếu bạn đang sử dụng lọ ký để bao gồm và chạy vào một vấn đề với chữ ký: stackoverflow.com/questions/999489/...
Peter N. Steinmetz

6
Điều này không hoạt động. Tệp kê khai trống với giải pháp này.
Jonas

4
2 xu của tôi: Tốt hơn là đặt một bộ phân loại hơn là thay đổi tên. Đặt classifier = 'all' thay vì baseName = project.name + '-all'. Bằng cách đó, bạn giữ tên cấu phần phần mềm tuân thủ các chính sách của Maven / Nexus.
taciosd

1
Thêm group "build"và nhiệm vụ này sẽ nằm trong buildnhóm (với các nhiệm vụ khác, ví dụ: jarnhiệm vụ.
MAGx2

1
Tôi không thể tìm thấy bất kỳ loại tài liệu nào về with jartừ khóa, chính xác thì nó làm gì?
Philipp Hemmelmayr

9

Cái này làm việc tốt cho tôi.

Lớp học chính của tôi:

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

}

Và nó là nội dung của tệp build.gradle của tôi:

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
}

Và tôi viết những điều sau trong bảng điều khiển của mình:

java -jar ProyectoEclipseTest-all.jar

Và kết quả là tuyệt vời:

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

Để tạo một JAR béo với một lớp thực thi chính, tránh các vấn đề với các JAR đã ký, tôi đề xuất plugin gradle-một-jar . Một plugin đơn giản sử dụng dự án One-JAR .

Dễ sử dụng:

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

Đơn giản

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

5

Câu trả lời từ @ben gần như phù hợp với tôi ngoại trừ việc phụ thuộc của tôi quá lớn và tôi gặp lỗi sau

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

  To build this archive, please enable the zip64 extension.

Để khắc phục sự cố này, tôi phải sử dụng mã sau

mainClassName = "com.company.application.Main"

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

1

Đối với những người cần xây dựng nhiều hơn một lọ từ dự án.

Tạo một hàm trong 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'
    }
}

sau đó gọi:

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

Hoạt động trên lớp 5.

Bình sẽ được đặt tại ./build/libs.


0

Tôi sử dụng task shadowJarby plugin. com.github.jengelman.gradle.plugins:shadow:5.2.0

Sử dụng chỉ chạy ./gradlew app::shadowJar tệp kết quả sẽ ởMyProject/app/build/libs/shadow.jar

build.gradletệp cấp cao nhất :

 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.gradletệp cấp mô-đun ứng dụng

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, thư viện Java. Mã từ "tác vụ jar" thêm các phần phụ thuộc vào "build / libs / xyz.jar" khi chạy tác vụ " gradle build ".

plugins {
    id 'java-library'
}

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

-1

Nếu bạn đã quen với kiến ​​thì bạn cũng có thể thử tương tự với Gradle:

task bundlemyjava{
    ant.jar(destfile: "build/cookmyjar.jar"){
        fileset(dir:"path to your source", includes:'**/*.class,*.class', excludes:'if any')
        } 
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.