Criando JAR executável com Gradle

148

Até agora, criei arquivos JAR executáveis ​​por meio da funcionalidade "Exportar ..." do Eclipse, mas agora mudei para o IntelliJ IDEA e Gradle para automação de compilação.

Alguns artigos aqui sugerem o plug-in "aplicativo", mas isso não leva totalmente ao resultado que eu esperava (apenas um JAR, nenhum script de inicialização ou algo assim).

Como posso obter o mesmo resultado que o Eclipse obtém com o diálogo "Exportar ..."?

Hannes
fonte

Respostas:

164

Um arquivo jar executável é apenas um arquivo jar que contém uma entrada de classe principal em seu manifesto. Então, você só precisa configurar a tarefa jar para adicionar esta entrada em seu manifesto:

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

Você também pode precisar adicionar entradas do caminho de classe no manifesto, mas isso seria feito da mesma maneira.

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

JB Nizet
fonte
6
Parece ser o que eu estava procurando; mas: Eu já declarei dependências no build.gradle, realmente preciso adicionar manualmente o caminho da classe ou posso reutilizar minha declaração de dependência?
Hannes
Você deve poder percorrer as bibliotecas dentro da configuração do 'runtime' e concatená-las para criar o valor do atributo Caminho da Classe.
JB Nizet
3
O que você quer dizer com 'inseid the' runtime 'configuration' '? Desculpe para perguntas estúpidas, eu sou muito novo para Gradle ...
Hannes
O plugin gradle java define 4 "configurações" correspondentes a 4 caminhos de classe diferentes: compilar (usado para compilar os arquivos Java), testCompile (usado para compilar os arquivos de origem Java de teste), tempo de execução (usado para executar o aplicativo) e testRuntime (usado para executar os testes). Veja gradle.org/docs/current/userguide/…
JB Nizet
Ah, obrigado - do que eu entendi correto e consigo construir o JAR, agora estou começando a criar o caminho de classe ;-) Muito obrigado pela ajuda!
Hannes
98

As respostas de JB Nizet e Jorge_B estão corretas.

Na sua forma mais simples, criar um JAR executável com Gradle é apenas uma questão de adicionar as entradas apropriadas ao manifesto . No entanto, é muito mais comum ter dependências que precisam ser incluídas no caminho de classe, tornando essa abordagem complicada na prática.

O plugin do aplicativo fornece uma abordagem alternativa; em vez de criar um JAR executável, ele fornece:

  • uma runtarefa para facilitar a execução fácil do aplicativo diretamente da construção
  • uma installDisttarefa que gera uma estrutura de diretórios incluindo o JAR construído, todos os JARs dos quais depende e um script de inicialização que reúne tudo isso em um programa que você pode executar
  • distZipe distTartarefas que criam arquivos que contêm uma distribuição completa de aplicativos (scripts de inicialização e JARs)

Uma terceira abordagem é criar o chamado "fat JAR", que é um JAR executável que inclui não apenas o código do seu componente, mas também todas as suas dependências. Existem alguns plugins diferentes que usam essa abordagem. Incluí links para alguns que eu conheço; Tenho certeza que existem mais.

davidmc24
fonte
Apenas tentei sombra e um frasco. Vou me ater ao frasco único: é mais simples e fácil de usar. Legal! graças
Jako
Infelizmente, o one-jar não funciona com versões recentes do Gradle. Veja, por exemplo, github.com/rholder/gradle-one-jar/issues/34
pharsicle
Aqui está uma solução de uma linha para criar um jar, modificando a tarefa jar. Eu achei isso o mais conveniente. Observe que você precisa adicionar configurations.runtimepara agrupar dependências de tempo de execução no jar único.
Quazi Irfan
Eu achei o "plugin de aplicativo" adequado e flexível o suficiente para minha necessidade. Ele também permite empacotar tudo para compactar e incluir / excluir arquivos extras nesse zip. Além disso, é possível adicionar outro script do iniciador com ponto de entrada adicional usando a tarefa personalizada.
kinORnirvana 5/01
Como eu executaria os arquivos .tar/ .zipgerados?
Tobiq
35

Como outros observaram, para que um arquivo jar seja executável, o ponto de entrada do aplicativo deve ser definido no Main-Classatributo do arquivo de manifesto. Se os arquivos de classe de dependência não forem colocados, eles deverão ser configurados na Class-Pathentrada do arquivo de manifesto.

Eu tentei todos os tipos de combinações de plugins e para a simples tarefa de criar um jar executável e de alguma forma incluir as dependências. Todos os plugins parecem estar faltando de um jeito ou de outro, mas finalmente consegui o que queria. Nenhum script misterioso, nem um milhão de mini-arquivos diferentes poluindo o diretório de compilação, um arquivo de script de compilação bastante limpo e, acima de tudo: nem um milhão de arquivos de classe de terceiros estrangeiros mesclados no meu arquivo jar.

A seguir, é copiada e colada a partir daqui para sua conveniência.

[Como fazer] crie um arquivo zip de distribuição com jars de dependência no subdiretório /libe adicione todas as dependências à Class-Pathentrada no arquivo de manifesto:

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
}

Hospedado como uma essência aqui .

O resultado pode ser encontrado em build/distributionse o conteúdo descompactado tem a seguinte aparência:

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

Conteúdo de MyJarFile.jar#META-INF/MANIFEST.mf:

Versão do manifesto: 1.0
Classe principal: caminho da
classe com.somepackage.MainClass : lib / commons-lang3-3.3.2.jar

Martin Andersson
fonte
O jar do aplicativo atual também estará no diretório 'lib' da distribuição. Você precisa movê-lo para o diretório superior ou alterar esta linha: 'Caminho da classe': configurations.runtime.files.collect {"lib / $ it.name"} .join ('')} para isso: 'Caminho da classe': configurations.runtime.files.collect {"$ it.name"} .join ('')}
Marc Nuri
1
@MarcNuri Você tem certeza? Tentei usar essa abordagem para o meu aplicativo, e o jar do aplicativo atual não estava no libdiretório do arquivo zip / tar produzido, mas no libdiretório-pai, como sugere esta resposta. Essa solução parecia funcionar perfeitamente para mim.
thejonwithnoh
1
@thejonwithnoh Me desculpe, você está certo. Não vi a solução proposta para usar o plug-in "java-library-distribution". No meu caso, estou simplesmente usando o plug-in "application", que faz o mesmo trabalho com a principal diferença de que todos os arquivos jar ( incluindo o jar do aplicativo) estão localizados no diretório "lib". Assim, mudar "lib/$it.name"para "$it.name"fará o trabalho.
Marc Nuri
28

A menor solução para mim foi usar o plugin gradle-shadow-plugin

Além de aplicar o plugin, tudo o que precisa ser feito é:

Configure a tarefa jar para colocar sua classe Main em manifesto

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

Execute a tarefa gradle

./gradlew shadowJar

Pegue o app-version-all.jar em build / libs /

E, finalmente, execute-o via:

java -jar app-version-all.jar
Ostkontentitan
fonte
2
Aqui está um exemplo completobuild.gradle .
precisa saber é o seguinte
Quando faço essa compilação, fico BUILD SUCCESSFUL, mas quando tento executar o arquivo jar com o java -jar build / libs / core-all-1.0.jar, recebo o seguinte erro: Erro: Não foi possível localizar ou carregar o scanners.exchange da classe principal. Principal causado por: java.lang.ClassNotFoundException: scanners.exchange.Main Você sabe como posso resolver isso?
Luka Lopusina
@LukaLopusina A classe que você especificou não está no seu arquivo JAR. Se você estiver usando o kotlin, precisará dizer 'com.my.app.MainKt'. Sem mais informações, não posso ajudá-lo mais.
byxor
O plug-in atual Shadow v5. + É compatível apenas com Gradle 5.0+ e Java 7+.
Zon
5

Você já tentou a tarefa 'installApp'? Ele não cria um diretório completo com um conjunto de scripts de início?

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

Jorge_B
fonte
Pelo que entendi, installAppnão cria um META-INF/MANIFEST.MFarquivo. Estou fazendo algo errado?
Advait 14/10
1
Não vejo installApptarefa na lista de tarefas do Plug-in de Aplicativo . Você quis dizer em installDistvez disso?
Quazi Irfan
2
Sim, installAppfoi renomeado para installDistno Gradle 3.0. Aqui está a nota de lançamento .
Quazi Irfan
4

Obrigado Konstantin, funcionou como um encanto com poucas nuances. Por alguma razão, especificar a classe principal como parte do manifest do jar não funcionou muito e, em vez disso, ele queria o atributo mainClassName. Aqui está um trecho de build.gradle que inclui tudo para fazê-lo funcionar:

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

Depois de executar o gradle shadowJar, você obtém myapp- {version} -all.jar na pasta de construção que pode ser executada como java -jar myapp- {version} -all.jar.

Alex Yavorskiy
fonte
3

Você pode definir um artefato jar nas configurações do módulo (ou estrutura do projeto).

  • Clique com o botão direito do mouse no módulo> Abrir configurações do módulo> Artefatos> +> JAR> dos módulos com dependências.
  • Defina a classe principal.

Fazer um jarro é tão fácil quanto clicar em "Criar artefato ..." no menu Build. Como bônus, você pode empacotar todas as dependências em um único jar.

Testado no IntelliJ IDEA 14 Ultimate.

Mondain
fonte
2

Eu verifiquei alguns links para a solução, finalmente fiz as etapas abaixo mencionadas para fazê-la funcionar. Estou usando o Gradle 2.9.

Faça as seguintes alterações em seu arquivo build, gradle:

  1. Mencione o plug-in:

    apply plugin: 'eu.appsatori.fatjar'
  2. Forneça o Buildscript:

    buildscript {
    repositories {
        jcenter()
    }
    
    dependencies {
        classpath "eu.appsatori:gradle-fatjar-plugin:0.3"
    }
    }
  3. Forneça a classe principal:

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

    ./gradlew clean fatjar
  5. Execute o fatjar em / build / libs /:

    java -jar MyFatJar.jar
Sandeep Sarkar
fonte
1
A partir de 2019: esta sugestão não funciona aqui. Nenhuma dependência está incluída no fatjar
carl
1

Você pode usar o plugin SpringBoot:

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

Crie o jar

gradle assemble

E então execute

java -jar build/libs/*.jar

Nota: seu projeto NÃO precisa ser um projeto SpringBoot para usar este plugin.

Topera
fonte