Tworzenie uruchamialnego pliku JAR za pomocą Gradle

148

Do tej pory tworzyłem uruchamialne pliki JAR za pomocą funkcji Eclipse „Export ...”, ale teraz przełączyłem się na IntelliJ IDEA i Gradle w celu automatyzacji kompilacji.

Niektóre artykuły sugerują tutaj wtyczkę „aplikacja”, ale nie prowadzi to do końca, którego oczekiwałem (tylko JAR, brak skryptów startowych lub coś podobnego).

W jaki sposób mogę osiągnąć taki sam wynik, jaki daje Eclipse w oknie dialogowym „Eksportuj ...”?

Hannes
źródło

Odpowiedzi:

164

Wykonywalny plik jar to po prostu plik jar zawierający wpis klasy głównej w swoim manifeście. Wystarczy więc skonfigurować zadanie jar , aby dodać ten wpis do jego manifestu:

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

Może być konieczne dodanie wpisów ścieżki klas w manifeście, ale byłoby to zrobione w ten sam sposób.

Zobacz http://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html

JB Nizet
źródło
6
Wydaje się, że właśnie tego szukałem; ale: zadeklarowałem już zależności w build.gradle, czy naprawdę muszę ręcznie dodać ścieżkę klasy, czy mogę ponownie użyć mojej deklaracji zależności?
Hannes,
Powinieneś być w stanie przeglądać biblioteki wewnątrz konfiguracji „środowiska uruchomieniowego” i łączyć je w celu utworzenia wartości atrybutu Class-Path.
JB Nizet,
3
Co masz na myśli mówiąc „inseid the„ runtime ”configuration”? Przepraszam za głupie pytania, jestem całkiem nowy w Gradle ...
Hannes,
Wtyczka gradle java definiuje 4 "konfiguracje" odpowiadające 4 różnym ścieżkom klas: compile (używanym do kompilowania plików Java), testCompile (używanym do kompilowania testowych plików źródłowych Java), runtime (używanym do wykonywania aplikacji) i testRuntime (który jest używany do wykonywania testów). Zobacz gradle.org/docs/current/userguide/…
JB Nizet
Ach, dzięki - niż dobrze zrozumiałem i udało mi się zbudować JAR-a, teraz bawię się w tworzenie ścieżki klas ;-) Wielkie dzięki za pomoc!
Hannes,
98

Odpowiedzi JB Nizeta i Jorge_B są poprawne.

W najprostszej formie, utworzenie wykonywalnego pliku JAR za pomocą Gradle to tylko kwestia dodania odpowiednich wpisów do manifestu . Jednak znacznie częściej występują zależności, które muszą być uwzględnione w ścieżce klas, co utrudnia to podejście w praktyce.

Plugin aplikacja dostarcza alternatywnego podejścia; zamiast tworzyć wykonywalny plik JAR, zapewnia:

  • za runzadanie łatwe ułatwienia uruchamiania aplikacji bezpośrednio z budowy
  • na installDistzadanie, które generuje strukturę katalogów w tym wybudowanym JAR, wszystkie JAR, że zależy on i skryptu startowego, który ciągnie to wszystko razem do programu można uruchomić
  • distZipi distTarzadania, które tworzą archiwa zawierające pełną dystrybucję aplikacji (skrypty startowe i JAR)

Trzecim podejściem jest utworzenie tak zwanego „grubego pliku JAR”, który jest wykonywalnym plikiem JAR, który zawiera nie tylko kod komponentu, ale także wszystkie jego zależności. Istnieje kilka różnych wtyczek, które wykorzystują to podejście. Dołączyłem linki do kilku, o których wiem; Jestem pewien, że jest ich więcej.

davidmc24
źródło
Właśnie wypróbowałem cień i jeden słoik. Będę trzymał się jednego słoika: jest prostszy i łatwiejszy w użyciu. Chłodny! dzięki
Jako
Niestety jeden słoik nie działa z najnowszymi wersjami Gradle. Zobacz na przykład github.com/rholder/gradle-one-jar/issues/34
pharsicle
Oto jedno-liniowe rozwiązanie umożliwiające zbudowanie jednego słoika poprzez modyfikację zadania słoika. Uważam, że jest to najwygodniejsze. Zauważ, że musisz dodać configurations.runtimedo pakietu zależności wykonawcze w jednym pliku jar.
Quazi Irfan
Uważam, że „wtyczka aplikacji” jest odpowiednia i wystarczająco elastyczna dla moich potrzeb. Pozwala również spakować wszystko do spakowania i dołączyć / wykluczyć dodatkowe pliki w tym zipie. Możliwe jest również dodanie kolejnego skryptu uruchamiania z dodatkowym punktem wejścia przy użyciu niestandardowego zadania.
kinORnirvana
Jak wykonać wygenerowane pliki .tar/ .zip?
Tobiq
35

Jak zauważyli inni, aby plik jar był wykonywalny, punkt wejścia aplikacji musi być ustawiony w Main-Classatrybucie pliku manifestu. Jeśli pliki klas zależności nie są kolokowane, należy je ustawić we Class-Pathwpisie pliku manifestu.

Wypróbowałem różne kombinacje wtyczek, a nie tylko po to, aby utworzyć plik wykonywalny jar i jakoś dołączyć zależności. Wydaje się, że wszystkim wtyczkom brakuje w ten czy inny sposób, ale w końcu dostałem to tak, jak chciałem. Żadnych tajemniczych skryptów, ani miliona różnych mini-plików zanieczyszczających katalog kompilacji, całkiem czysty plik skryptu kompilacji, a przede wszystkim: ani milion obcych plików klas innych firm, które zostały włączone do mojego archiwum jar.

Poniżej znajduje się kopia-wklej stąd dla Twojej wygody.

[How-to] Utwórz dystrybucyjny plik ZIP z plikami JAR zależności w podkatalogu /libi dodaj wszystkie zależności do Class-Pathwpisu w pliku manifestu:

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
}

Hostowane jako sedno tutaj .

Wynik można znaleźć w, build/distributionsa rozpakowana zawartość wygląda następująco:

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

Zawartość MyJarFile.jar#META-INF/MANIFEST.mf:

Manifest-Version: 1.0
Main-Class: com.somepackage.MainClass
Class-Path: lib / commons-lang3-3.3.2.jar

Martin Andersson
źródło
Bieżący plik jar aplikacji będzie również znajdował się w katalogu „lib” dystrybucji. Musisz albo przenieść go do katalogu głównego, albo zmienić ten wiersz: „Class-Path”: configuration.runtime.files.collect {"lib / $ it.name"} .join ('')} na ten: 'Class-Path': configuration.runtime.files.collect {"$ it.name"} .join ('')}
Marc Nuri
1
@MarcNuri Czy na pewno? Próbowałem zastosować to podejście do mojej aplikacji, a aktualny plik jar aplikacji nie znajdował się w libkatalogu utworzonego pliku zip / tar, ale raczej w libkatalogu nadrzędnym, jak sugeruje ta odpowiedź. To rozwiązanie wydawało mi się działać idealnie.
thejonwithnoh
1
@thejonwithnoh Przepraszam, masz rację. Nie widziałem rozwiązania proponowanego przy użyciu wtyczki „java-library-distribution”. W moim przypadku po prostu używam wtyczki „aplikacja”, która wykonuje tę samą pracę, z tą główną różnicą, że wszystkie pliki jar (w tym plik jar aplikacji) znajdują się w katalogu „lib”. W ten sposób zmiana "lib/$it.name"na "$it.name"wystarczy.
Marc Nuri,
28

Najmniejszym wysiłkiem dla mnie było użycie wtyczki gradle-shadow-plugin

Oprócz zastosowania wtyczki wszystko, co należy zrobić, to:

Skonfiguruj zadanie jar, aby umieścić klasę główną w manifeście

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

Uruchom zadanie gradle

./gradlew shadowJar

Weź app-version-all.jar od build / libs /

I na koniec wykonaj to poprzez:

java -jar app-version-all.jar
Ostkontentitan
źródło
2
Oto pełny build.gradleprzykład .
Brad Turek
Kiedy wykonuję tę kompilację, kompilacja zakończyła się pomyślnie, ale kiedy próbuję uruchomić plik jar za pomocą java -jar build / libs / core-all-1.0.jar, pojawia się następujący błąd: Błąd: Nie można znaleźć lub załadować głównej klasy scanners.exchange. Główna przyczyna: java.lang.ClassNotFoundException: scanners.exchange.Main Czy wiesz, jak mogę rozwiązać ten problem?
Luka Lopusina
@LukaLopusina Podana klasa nie znajduje się w Twoim pliku JAR. Jeśli używasz kotlin, musisz powiedzieć 'com.my.app.MainKt'. Bez dodatkowych informacji nie mogę Ci dalej pomóc.
byxor
Obecna wtyczka Shadow v5. + Jest kompatybilna tylko z Gradle 5.0+ i Java 7+.
Zon
5

Czy próbowałeś wykonać zadanie „installApp”? Czy nie tworzy pełnego katalogu z zestawem skryptów startowych?

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

Jorge_B
źródło
Z tego co rozumiem, installAppnie tworzy META-INF/MANIFEST.MFpliku. czy robię coś źle?
Advait
1
Nie widzę installAppzadania na liście zadań wtyczki aplikacji . Czy installDistzamiast tego miałeś na myśli ?
Quazi Irfan
2
Tak, w Gradle 3.0 installAppzmieniono nazwę na installDist. Oto informacja o wydaniu .
Quazi Irfan
4

Dziękuję Konstantin, działał jak urok z kilkoma niuansami. Z jakiegoś powodu określenie klasy main jako części manifestu jar nie zadziałało i zamiast tego potrzebny był atrybut mainClassName. Oto fragment z build.gradle, który zawiera wszystko, aby działał:

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'
}

Po uruchomieniu gradle shadowJar dostajesz myapp- {wersja} -all.jar w folderze kompilacji, który można uruchomić jako java -jar myapp- {wersja} -all.jar.

Alex Yavorskiy
źródło
3

Artefakt jar można zdefiniować w ustawieniach modułu (lub strukturze projektu).

  • Kliknij prawym przyciskiem myszy moduł> Otwórz ustawienia modułu> Artefakty> +> JAR> z modułów z zależnościami.
  • Ustaw główną klasę.

Zrobienie słoika jest wtedy tak proste, jak kliknięcie „Zbuduj artefakt ...” w menu Budowa. Jako bonus możesz spakować wszystkie zależności w jeden słoik.

Przetestowano na IntelliJ IDEA 14 Ultimate.

Mondain
źródło
2

Sprawdziłem kilka linków do rozwiązania, w końcu wykonałem poniższe kroki, aby to działało. Używam Gradle 2.9.

Wprowadź następujące zmiany w kompilacji, pliku gradle:

  1. Wspomnij o wtyczce:

    apply plugin: 'eu.appsatori.fatjar'
  2. Podaj skrypt kompilacji:

    buildscript {
    repositories {
        jcenter()
    }
    
    dependencies {
        classpath "eu.appsatori:gradle-fatjar-plugin:0.3"
    }
    }
  3. Podaj klasę główną:

    fatJar {
      classifier 'fat'
      manifest {
        attributes 'Main-Class': 'my.project.core.MyMainClass'
      }
      exclude 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.SF'
    }
  4. Utwórz fatjar:

    ./gradlew clean fatjar
  5. Uruchom fatjar z / build / libs /:

    java -jar MyFatJar.jar
Sandeep Sarkar
źródło
1
Od 2019: Ta sugestia nie działa tutaj. Żadne zależności nie są zawarte w fatjar
carl
1

Możesz użyć wtyczki SpringBoot:

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

Utwórz słoik

gradle assemble

A następnie uruchom go

java -jar build/libs/*.jar

Uwaga: Twój projekt NIE musi być projektem SpringBoot, aby korzystać z tej wtyczki.

Topera
źródło