Создание работающего JAR с Gradle

148

До сих пор я создавал исполняемые файлы JAR с помощью функции Eclipse «Export ...», но теперь я переключился на IntelliJ IDEA и Gradle для автоматизации сборки.

Некоторые статьи здесь предлагают плагин «application», но это не совсем приводит к ожидаемому результату (просто JAR, никаких стартовых скриптов или что-то в этом роде).

Как я могу добиться того же результата, который Eclipse делает с диалоговым окном «Экспорт ...»?

Ханнес
источник

Ответы:

164

Исполняемый файл JAR - это просто файл JAR, содержащий запись Main-Class в своем манифесте. Так что вам просто нужно настроить задачу jar , чтобы добавить эту запись в манифест:

jar {
    manifest {
        attributes 'Main-Class': 'com.foo.bar.MainClass'
    }
}

Вам также может понадобиться добавить записи classpath в манифест, но это будет сделано так же.

См. Http://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html.

Дж. Б. Низет
источник
6
Кажется, это то, что я искал; но: я уже объявил зависимости в build.gradle, действительно ли мне нужно вручную добавлять путь к классу или я могу повторно использовать свое объявление зависимостей?
Ханнес
Вы должны иметь возможность циклически проходить по библиотекам внутри конфигурации 'runtime' и объединять их для создания значения атрибута Class-Path.
JB Nizet
3
Что вы имеете в виду под «внутренняя конфигурация»? Извините за глупые вопросы, я совсем новичок в Gradle ...
Ханнес
Плагин gradle java определяет 4 «конфигурации», соответствующие 4 различным путям классов: compile (используется для компиляции файлов Java), testCompile (который используется для компиляции тестовых исходных файлов Java), runtime (который используется для выполнения приложения) и testRuntime (который используется для выполнения тестов). См. Gradle.org/docs/current/userguide/…
JB Nizet
Ах, спасибо - тогда я понял, что это правильно, и мне удалось собрать JAR, теперь я облажался с созданием пути к классам ;-) Большое спасибо за вашу помощь!
Ханнес
98

Ответы JB Nizet и Jorge_B верны.

В простейшей форме создание исполняемого JAR-файла с Gradle - это просто добавление соответствующих записей в манифест . Однако гораздо чаще встречаются зависимости, которые необходимо включить в путь к классам, что делает этот подход сложным на практике.

Плагин приложение обеспечивает альтернативный подход; вместо создания исполняемого JAR он обеспечивает:

  • runзадача легко облегчить запуск приложения непосредственно из сборки
  • installDistзадача , которая формирует структуру каталогов , включая встроенный JAR, все баночки , что это зависит от, и скрипт для запуска , который тянет все это вместе в программу вы можете запустить
  • distZip и distTar задачи, которые создают архивы, содержащие полный дистрибутив приложения (сценарии запуска и JAR)

Третий подход заключается в создании так называемого «толстого JAR», который представляет собой исполняемый JAR, который включает в себя не только код вашего компонента, но и все его зависимости. Есть несколько различных плагинов, которые используют этот подход. Я включил ссылки на некоторые из них, которые я знаю; Я уверен, что есть еще.

davidmc24
источник
Только что попробовал тень и одну банку. Я буду придерживаться одной банки: это проще и проще в использовании. Прохладно! спасибо
Jako
К сожалению, одна банка не работает с последними версиями Gradle. См., Например, github.com/rholder/gradle-one-jar/issues/34
pharsicle
Вот решение с одним вкладышем для создания одного jar путем изменения задачи jar. Я нашел это наиболее удобным. Обратите внимание, что вам нужно добавить configurations.runtimeв пакет зависимости времени выполнения в одном банке.
Quazi Irfan
Я обнаружил, что «плагин приложения» подходит и достаточно гибок для моих нужд. Это позволяет также упаковать все в zip и включить / исключить дополнительные файлы в этом zip. Также можно добавить другой скрипт запуска с дополнительной точкой входа, используя пользовательское задание.
кинорнирвана
Как мне выполнить созданные файлы .tar/ .zip?
Тобик
35

Как уже отмечали другие, для того, чтобы файл jar был исполняемым, точка входа приложения должна быть установлена ​​в Main-Classатрибуте файла манифеста. Если файлы классов зависимостей не размещены вместе, их необходимо установить вClass-Path записи файла манифеста.

Я перепробовал все виды комбинаций плагинов и что не для простой задачи создания исполняемого фляги и так или иначе, включая зависимости. Кажется, что всем плагинам так или иначе не хватает, но, наконец, я получил его так, как хотел. Никаких загадочных сценариев, не миллион разных мини-файлов, загрязняющих каталог сборки, довольно чистый файл сценариев сборки и, самое главное, ни один миллион файлов сторонних классов не слился в мой архив jar.

Ниже приводится копипаст из здесь для вашего удобства ..

[How-to] создайте дистрибутивный zip-файл с jar-файлами зависимостей в подкаталоге /libи добавьте все зависимости к Class-Pathзаписи в файле манифеста:

apply plugin: 'java'
apply plugin: 'java-library-distribution'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.apache.commons:commons-lang3:3.3.2'
}

// Task "distZip" added by plugin "java-library-distribution":
distZip.shouldRunAfter(build)

jar {
    // Keep jar clean:
    exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF'

    manifest {
        attributes 'Main-Class': 'com.somepackage.MainClass',
                   'Class-Path': configurations.runtime.files.collect { "lib/$it.name" }.join(' ')
    }
    // How-to add class path:
    //     /programming/22659463/add-classpath-in-manifest-using-gradle
    //     https://gist.github.com/simon04/6865179
}

Хостинг как суть здесь .

Результат можно найти в build/distributionsи разархивированное содержимое выглядит следующим образом:

lib / commons-lang3-3.3.2.jar
MyJarFile.jar

Содержание MyJarFile.jar#META-INF/MANIFEST.mf:

Версия манифеста: 1.0
Основной класс: com.somepackage.MainClass
Путь к классу: lib / commons-lang3-3.3.2.jar

Мартин Андерссон
источник
Текущий jar приложения также будет находиться в каталоге 'lib' дистрибутива. Вы должны либо переместить его по значению в верхний каталог, либо изменить эту строку: 'Class-Path': configurations.runtime.files.collect {"lib / $ it.name"} .join ('')} на это: 'Class-Path': configurations.runtime.files.collect {"$ it.name"} .join ('')}
Марк Нури,
1
@MarcNuri Ты уверен? Я попытался использовать этот подход для своего приложения, и текущий jar- файл приложения не находился в libкаталоге созданного файла zip / tar, а скорее находился в libродительском каталоге, как предполагает этот ответ. Это решение, похоже, отлично сработало для меня.
thejonwithnoh
1
@thejonwithnoh Извини, ты прав. Я не видел решения, предложенного для использования плагина "java-library-distribution". В моем случае я просто использую плагин «application», который выполняет ту же работу с тем отличием, что все файлы jar ( включая jar приложения) находятся в каталоге «lib». Таким образом , изменение "lib/$it.name"в "$it.name"будет делать эту работу.
Марк Нури
28

Наименьшим решением для меня было использовать плагин gradle-shadow-plugin

Помимо применения плагина все, что нужно сделать, это:

Сконфигурируйте задачу jar, чтобы поместить ваш главный класс в манифест

jar {
  manifest {
   attributes 'Main-Class': 'com.my.app.Main'
  }
}

Запустите задание Gradle

./gradlew shadowJar

Возьми app-version-all.jar из сборки / libs /

И, наконец, выполнить его через:

java -jar app-version-all.jar
Ostkontentitan
источник
2
Вот полный build.gradleпример .
Брэд Турек
Когда я делаю эту сборку, я получаю BUILD SUCCESSFUL, но когда я пытаюсь запустить файл jar с java -jar build / libs / core-all-1.0.jar, я получаю следующую ошибку: Ошибка: не удалось найти или загрузить основной класс scanners.exchange. Главная причина: java.lang.ClassNotFoundException: scanners.exchange.Main Знаете ли вы, как я могу решить эту проблему?
Лука Лопусина
@LukaLopusina Указанный вами класс отсутствует в вашем JAR-файле. Если вы используете kotlin, вам нужно сказать 'com.my.app.MainKt'. Без дополнительной информации я не смогу вам помочь.
19
Текущий плагин Shadow v5. + Совместим только с Gradle 5.0+ и Java 7+.
Зон
5

Вы пробовали задачу 'installApp'? Разве это не создает полный каталог с набором стартовых скриптов?

http://www.gradle.org/docs/current/userguide/application_plugin.html

Jorge_B
источник
Из того, что я понимаю, installAppне создает META-INF/MANIFEST.MFфайл. Я делаю что-то неправильно?
Advait
1
Я не вижу installAppзадачу в списке задач Плагина приложения . Вы имели в виду installDistвместо этого?
Quazi Irfan
2
Да, installAppбыл переименован installDistв Gradle 3.0. Вот примечание к выпуску .
Quazi Irfan
4

Спасибо Константин, это сработало как шарм с несколькими нюансами. По какой-то причине указание основного класса как части манифеста jar не совсем работает, и вместо этого ему нужен атрибут mainClassName. Вот фрагмент из build.gradle, который включает в себя все, чтобы он работал:

plugins {
  id 'java' 
  id 'com.github.johnrengelman.shadow' version '1.2.2'
}
...
...
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'
...
...
mainClassName = 'com.acme.myapp.MyClassMain'
...
...
...
shadowJar {
    baseName = 'myapp'
}

После запуска gradle shadowJar вы получите myapp- {version} -all.jar в папке сборки, которую можно запустить как java -jar myapp- {version} -all.jar.

Алекс Яворский
источник
3

Вы можете определить артефакт jar в настройках модуля (или структуре проекта).

  • Щелкните правой кнопкой мыши модуль> Открыть настройки модуля> Артефакты> +> JAR> из модулей с зависимостями.
  • Установите основной класс.

Сделать банку так же просто, как нажать «Построить артефакт ...» в меню «Построить». В качестве бонуса вы можете упаковать все зависимости в одну банку.

Проверено на IntelliJ IDEA 14 Ultimate.

Mondain
источник
2

Я проверил довольно много ссылок для решения, наконец-то сделал следующие шаги, чтобы оно заработало. Я использую Gradle 2.9.

Внесите следующие изменения в свой файл сборки, Gradle:

  1. Упомянуть плагин:

    apply plugin: 'eu.appsatori.fatjar'
  2. Предоставьте Buildscript:

    buildscript {
    repositories {
        jcenter()
    }
    
    dependencies {
        classpath "eu.appsatori:gradle-fatjar-plugin:0.3"
    }
    }
  3. Предоставить основной класс:

    fatJar {
      classifier 'fat'
      manifest {
        attributes 'Main-Class': 'my.project.core.MyMainClass'
      }
      exclude 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.SF'
    }
  4. Создать фиджар:

    ./gradlew clean fatjar
  5. Запустите фаджар из / build / libs /:

    java -jar MyFatJar.jar
Сандип Саркар
источник
1
С 2019 года: это предложение не работает здесь. Никакие зависимости не включены в фаджар
Карл
1

Вы можете использовать плагин SpringBoot:

plugins {
  id "org.springframework.boot" version "2.2.2.RELEASE"
}

Создать банку

gradle assemble

А потом запусти

java -jar build/libs/*.jar

Примечание: ваш проект НЕ должен быть проектом SpringBoot, чтобы использовать этот плагин.

Topera
источник