Membuat JAR runnable dengan Gradle

148

Sampai sekarang saya membuat file JAR yang dapat dijalankan melalui fungsi "Ekspor ..." Eclipse tetapi sekarang saya beralih ke IntelliJ IDEA dan Gradle untuk membangun otomatisasi.

Beberapa artikel di sini menyarankan plugin "aplikasi", tetapi ini tidak sepenuhnya mengarah pada hasil yang saya harapkan (hanya JAR, tidak ada skrip awal atau semacamnya).

Bagaimana saya bisa mencapai hasil yang sama dengan Eclipse dengan dialog "Ekspor ..."?

Hannes
sumber

Jawaban:

164

File jar yang dapat dieksekusi hanyalah file jar yang berisi entri Main-Class dalam manifesnya. Jadi Anda hanya perlu mengkonfigurasi tugas jar untuk menambahkan entri ini dalam manifesnya:

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

Anda mungkin juga perlu menambahkan entri classpath di manifes, tetapi itu akan dilakukan dengan cara yang sama.

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

JB Nizet
sumber
6
Ini sepertinya adalah yang saya cari; tetapi: Saya sudah mendeklarasikan dependensi di build.gradle, apakah saya benar-benar harus menambahkan jalur kelas secara manual atau dapatkah saya menggunakan kembali deklarasi dependensi?
Hannes
Anda harus dapat mengulang-ulang pustaka di dalam konfigurasi 'runtime' dan menyatukannya untuk membuat nilai atribut Class-Path.
JB Nizet
3
Apa yang Anda maksud dengan 'inseid the "runtime" konfigurasi'? Maaf untuk pertanyaan bodoh, saya cukup baru di Gradle ...
Hannes
Plugin java gradle mendefinisikan 4 "konfigurasi" yang sesuai dengan 4 kelas yang berbeda: kompilasi (digunakan untuk mengkompilasi file Java), testCompile (yang digunakan untuk mengkompilasi file sumber uji Java), runtime (yang digunakan untuk menjalankan aplikasi) dan testRuntime (yang digunakan untuk menjalankan tes). Lihat gradle.org/docs/current/userguide/…
JB Nizet
Ah, terima kasih - daripada yang saya mengerti benar dan berhasil membangun JAR, sekarang saya bermain-main dengan menciptakan classpath ;-) Terima kasih banyak atas bantuan Anda!
Hannes
98

Jawaban JB Nizet dan Jorge_B benar.

Dalam bentuknya yang paling sederhana, membuat JAR yang dapat dieksekusi dengan Gradle hanyalah masalah menambahkan entri yang sesuai ke manifes . Namun, jauh lebih umum untuk memiliki dependensi yang perlu dimasukkan pada classpath, membuat pendekatan ini sulit dalam praktik.

Itu Aplikasi Plugin memberikan pendekatan alternatif; alih-alih membuat JAR yang dapat dieksekusi, ia menyediakan:

  • sebuah runtugas untuk memfasilitasi dengan mudah menjalankan aplikasi langsung dari membangun
  • sebuah installDist tugas yang menghasilkan struktur direktori termasuk JAR dibangun, semua guci bahwa itu tergantung pada, dan skrip startup yang tarikan itu semua bersama-sama ke program Anda dapat menjalankan
  • distZip dan distTar tugas yang membuat arsip yang berisi distribusi aplikasi lengkap (skrip startup dan JAR)

Pendekatan ketiga adalah membuat apa yang disebut "JAR gemuk" yang merupakan JAR yang dapat dieksekusi yang tidak hanya mencakup kode komponen Anda, tetapi juga semua dependensinya. Ada beberapa plugin berbeda yang menggunakan pendekatan ini. Saya telah menyertakan tautan ke beberapa yang saya ketahui; Saya yakin masih ada lagi.

davidmc24
sumber
Baru saja mencoba shadow dan one-jar. Saya akan tetap menggunakan satu stoples: lebih sederhana dan lebih mudah digunakan. Keren! terima kasih
Jako
Sayangnya one-jar tidak bekerja dengan versi terbaru Gradle. Lihat, misalnya, github.com/rholder/gradle-one-jar/issues/34
pharsicle
Berikut ini adalah solusi satu-liner untuk membangun satu-stoples dengan memodifikasi tugas jar. Saya telah menemukan ini sebagai yang paling nyaman. Perhatikan bahwa Anda perlu menambahkan configurations.runtimeke dependensi bundel bundel dalam tabung tunggal.
Quazi Irfan
Saya menemukan "plugin aplikasi" cocok dan cukup fleksibel untuk kebutuhan saya. Hal ini memungkinkan juga untuk mengemas semuanya ke zip dan memasukkan / mengecualikan file tambahan di zip itu. Juga, dimungkinkan untuk menambahkan skrip peluncur lain dengan titik masuk tambahan menggunakan tugas kustom.
kinORnirvana
Bagaimana saya menjalankan .tar/ .zipfile yang dihasilkan?
Tobiq
35

Seperti yang telah dicatat orang lain, agar file jar dapat dieksekusi, titik masuk aplikasi harus diatur dalam Main-Classatribut file manifes. Jika file kelas dependensi tidak dilokasikan, maka mereka perlu diatur dalam Class-Pathentri file manifes.

Saya telah mencoba semua jenis kombinasi plugin dan apa yang tidak untuk tugas sederhana membuat jar yang dapat dieksekusi dan entah bagaimana, termasuk dependensi. Semua plugin tampaknya kurang satu atau lain cara, tetapi akhirnya saya mendapatkannya seperti yang saya inginkan. Tidak ada skrip misterius, tidak satu juta file mini berbeda mencemari direktori build, file skrip build yang cukup bersih, dan yang terpenting: tidak satu juta file kelas pihak ketiga asing bergabung ke arsip jar saya.

Berikut ini adalah copy-paste dari sini untuk kenyamanan Anda ..

[Cara-cara] membuat file zip distribusi dengan stoples ketergantungan di subdirektori /libdan menambahkan semua dependensi ke Class-Pathentri dalam file manifes:

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
}

Diselenggarakan sebagai intisari di sini .

Hasilnya dapat ditemukan di build/distributionsdan isi ritsleting terlihat seperti ini:

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

Isi dari MyJarFile.jar#META-INF/MANIFEST.mf:

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

Martin Andersson
sumber
Jar aplikasi saat ini juga akan ada di direktori 'lib' dari distribusi. Anda harus memindahkannya dengan harus ke direktori teratas atau mengubah baris ini: 'Jalur-Kelas': configureations.runtime.files.collect {"lib / $ it.name"} .join ('')} ke ini: 'Class-Path': configurationations.runtime.files.collect {"$ it.name"} .join ('')}
Marc Nuri
1
@MarcNuri Anda yakin? Saya mencoba menggunakan pendekatan ini untuk aplikasi saya, dan toples aplikasi saat ini tidak ada di libdirektori file zip / tar yang dihasilkan, melainkan di libdirektori induk, seperti yang disarankan jawaban ini. Solusi ini sepertinya bekerja dengan baik untuk saya.
thejonwithnoh
1
@ thejonwithnoh Maaf, kamu benar. Saya tidak melihat solusi yang diusulkan untuk menggunakan plugin "java-library-distribution". Dalam kasus saya, saya hanya menggunakan plugin "aplikasi" yang melakukan pekerjaan yang sama dengan perbedaan utama bahwa semua file jar ( termasuk jar aplikasi) terletak di direktori "lib". Dengan demikian berubah "lib/$it.name"menjadi "$it.name"akan melakukan pekerjaan.
Marc Nuri
28

Solusi upaya paling tidak bagi saya adalah menggunakan plugin gradle-shadow-plugin

Selain menerapkan plugin, semua yang perlu dilakukan adalah:

Konfigurasikan tugas jar untuk membuat kelas Utama Anda menjadi manifes

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

Jalankan tugas gradle

./gradlew shadowJar

Ambil app-versi-all.jar dari build / libs /

Dan akhirnya jalankan melalui:

java -jar app-version-all.jar
Ostkontentitan
sumber
2
Ini contoh lengkapnyabuild.gradle .
Brad Turek
Ketika saya melakukan build ini, saya mendapatkan BUILD SUCCESSFUL tetapi ketika saya mencoba menjalankan file jar dengan java -jar build / libs / core-all-1.0.jar saya mendapatkan error berikut: Kesalahan: Tidak dapat menemukan atau memuat scanners.exchange kelas utama. Utama Disebabkan oleh: java.lang.ClassNotFoundException: scanners.exchange.Main Apakah Anda tahu bagaimana saya bisa menyelesaikan ini?
Luka Lopusina
@LukaLopusina Kelas yang Anda tentukan tidak ada dalam file JAR Anda. Jika Anda menggunakan kotlin, Anda harus mengatakannya 'com.my.app.MainKt'. Tanpa info lebih lanjut, saya tidak dapat membantu Anda lebih jauh.
byxor
Plugin saat ini Shadow v5. + Hanya kompatibel dengan Gradle 5.0+ dan Java 7+ saja.
Zon 6-19
5

Sudahkah Anda mencoba tugas 'installApp'? Apakah itu tidak membuat direktori lengkap dengan satu set skrip mulai?

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

Jorge_B
sumber
Dari apa yang saya mengerti, installApptidak membuat META-INF/MANIFEST.MFfile. Apakah saya melakukan sesuatu yang salah?
advait
1
Saya tidak melihat installApptugas di daftar tugas Plugin Aplikasi . Apakah maksud Anda installDistsebagai gantinya?
Quazi Irfan
2
Ya, installAppdiubah namanya menjadi installDistdi Gradle 3.0. Ini catatan rilisnya .
Quazi Irfan
4

Terima kasih Konstantin, itu berfungsi seperti pesona dengan sedikit nuansa. Untuk beberapa alasan, menentukan kelas utama sebagai bagian dari manifes jar tidak cukup berhasil dan ia menginginkan atribut mainClassName sebagai gantinya. Berikut ini cuplikan dari build.gradle yang mencakup segalanya untuk membuatnya berfungsi:

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

Setelah menjalankan gradle shadowJar, Anda mendapatkan myapp- {versi} -all.jar di folder build Anda yang dapat dijalankan sebagai java -jar myapp- {version} -all.jar.

Alex Yavorskiy
sumber
3

Anda dapat menentukan artefak tabung dalam pengaturan modul (atau struktur proyek).

  • Klik kanan modul> Buka pengaturan modul> Artefak> +> JAR> dari modul dengan dependensi.
  • Atur kelas utama.

Membuat toples semudah mengklik "Bangun artefak ..." dari menu Bangun. Sebagai bonus, Anda dapat mengemas semua dependensi ke dalam satu toples.

Diuji pada IntelliJ IDEA 14 Ultimate.

Mondain
sumber
2

Saya memeriksa beberapa tautan untuk solusinya, akhirnya melakukan langkah-langkah yang disebutkan di bawah untuk membuatnya berfungsi. Saya menggunakan Gradle 2.9.

Buat perubahan berikut di file build, gradle Anda:

  1. Sebutkan plugin:

    apply plugin: 'eu.appsatori.fatjar'
  2. Berikan Buildscript:

    buildscript {
    repositories {
        jcenter()
    }
    
    dependencies {
        classpath "eu.appsatori:gradle-fatjar-plugin:0.3"
    }
    }
  3. Berikan Kelas Utama:

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

    ./gradlew clean fatjar
  5. Jalankan fatjar dari / build / libs /:

    java -jar MyFatJar.jar
Sandeep Sarkar
sumber
1
Dari 2019: Saran ini tidak berfungsi di sini. Tidak ada ketergantungan yang termasuk dalam fatjar
carl
1

Anda dapat menggunakan plugin SpringBoot:

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

Buat toples

gradle assemble

Dan kemudian jalankan

java -jar build/libs/*.jar

Catatan: proyek Anda TIDAK perlu menjadi proyek SpringBoot untuk menggunakan plugin ini.

Topera
sumber