Використання Gradle для створення баночки із залежностями


122

У мене є багатопроектна збірка, і я ставлю завдання зібрати жирову банку в одному з підпроектів. Я створив завдання, подібні до описаного в кулінарній книзі .

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

Запуск його призводить до наступної помилки:

Причина: Ви не можете змінити конфігурацію, яка не знаходиться у невирішеному стані!

Я не впевнений, що означає ця помилка. Я також повідомив про це на JIRA Gradle у випадку, якщо це помилка .

Відповіді:


195

Оновлення: у новіших версіях Gradle (4+) compileкласифікатор застарілий на користь нових 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) }
  }
}

Зверніть увагу, що mainClassNameповинно з’явитися ПЕРЕД jar {.


4
Мені довелося змінити це до configigurations.runtime.collect для мого проекту, оскільки у мене є також і умови виконання.
vextorspace

2
Мені довелося додати, def mainClassNameщоб код працював ... Мені не вдалося встановити невідому властивість 'mainClassName' для root проекту
hanskoff

1
Як ви обробляєте зіткнення імен файлів? Файли на одному шляху в різних JAR будуть перезаписані.
wst

3
На жаль, це вже не працює. Я використовую gradle 4.10 та нову implementationконфігурацію замість застарілої compile. Наведений вище код створює мені невелику банку без залежностей. Коли я змінюю його ( 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. у банку були конфліктні зовнішні файли 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 мого банку, але підпис більше не відповідав змісту.
Флавін

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/…
Пітер Н. Штейнмет

6
Це не працює. Файл Manifest порожній із цим рішенням.
Йонас

4
Мої 2 копійки: Краще встановити класифікатор, ніж міняти ім’я. Помістіть classifier = 'all' замість baseName = project.name + '-all'. Таким чином ви зберігаєте назву артефакту відповідно до політики Maven / Nexus.
taciosd

1
Додайте group "build"і це завдання буде в buildгрупі (з іншими завданнями, тобто jarзавданнями.
MAGx2

1
Я не можу знайти будь-яку документацію на with jarключове слово, що саме це робить?
Філіп Хеммельмайр

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 з основним виконуваним класом, уникаючи проблем із підписаними JAR, я пропоную плагін gradle-one-jar . Простий плагін, який використовує проект 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')
}

Працює над 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')
        } 
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.