Gradle을 사용하여 종속성이있는 jar 빌드


122

다중 프로젝트 빌드가 있고 하위 프로젝트 중 하나에 뚱뚱한 항아리를 빌드하는 작업을 넣었습니다. 요리 책에 설명 된 것과 유사한 작업을 만들었습니다 .

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

실행하면 다음 오류가 발생합니다.

원인 : 해결되지 않은 상태가 아닌 구성을 변경할 수 없습니다!

이 오류가 무엇을 의미하는지 잘 모르겠습니다. 버그 일 경우를 대비하여 Gradle JIRA에보고했습니다 .

답변:


195

업데이트 : 최신 Gradle 버전 (4+) compile에서는 새 apiimplementation구성 을 위해 한정자가 더 이상 사용되지 않습니다 . 이것을 사용하면 다음이 효과가 있습니다.

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

mainClassName전에 나타나야합니다 jar {.


4
런타임 종속성도 있으므로 프로젝트의 구성 .runtime.collect로 수정해야했습니다.
vextorspace

2
def mainClassName코드가 작동 하도록 추가 해야했습니다 ... 루트 프로젝트에 대해 알 수없는 속성 'mainClassName'을 설정할 수 없습니다.
hanskoff

1
파일 이름 충돌을 어떻게 처리합니까? 다른 JAR의 동일한 경로에있는 파일을 덮어 씁니다.
wst

3
불행히도 이것은 더 이상 작동하지 않습니다. implementation이제는 사용되지 않는 .NET 대신 gradle 4.10 및 새 구성을 사용 compile합니다. 위의 코드는 종속성이없는 작은 항아리를 만듭니다. 변경 ( from { configurations.implementation.collect {...} })하면 '구현'구성을 직접 해결할 수 없다는 오류가 발생합니다
Bastian Voigt

1
@BastianVoigt configurations.compileClasspath는 모든 implementations 를 수정 하지만 api종속성은 제외합니다. 다른 답변에서 해결책을 찾았습니다 runtimeClasspath. 여기에는 api종속성도 포함됩니다 .
rekire

64

@felix의 대답은 거의 나를 데려 왔습니다. 두 가지 문제가있었습니다.

  1. Gradle 1.5에서는 fatJar 작업 내에서 매니페스트 태그가 인식되지 않았으므로 Main-Class 속성을 직접 설정할 수 없습니다.
  2. 항아리에 충돌하는 외부 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에 저장되었지만 서명이 더 이상 내용과 일치하지 않습니다.
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/...
피터 N. Steinmetz의

6
작동하지 않습니다. 이 솔루션에서는 매니페스트 파일이 비어 있습니다.
Jonas

4
내 2 센트 : 이름을 변경하는 것보다 분류자를 설정하는 것이 좋습니다. baseName = project.name + '-all'대신 분류 자 ​​= 'all'을 넣으십시오. 이렇게하면 Maven / Nexus 정책에 따라 아티팩트 이름을 유지합니다.
taciosd

1
추가 group "build"하면이 작업이 build그룹에 포함됩니다 (다른 작업, 즉 jar작업 포함).
MAGx2

1
with jar키워드 에 대한 문서를 찾을 수 없습니다. 정확히 어떤 역할을합니까?
Philipp Hemmelmayr 19 년

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의 문제를 피하고 기본 실행 가능 클래스로 fat JAR을 생성하려면 gradle-one-jar plugin을 제안 합니다. 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

간단한 해결

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

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에서 작동합니다.

Jar는에 배치됩니다 ./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"의 코드는 " gradle build "작업을 실행할 때 "build / libs / xyz.jar"에 종속성을 추가합니다 .

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.