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 ..."?

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


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.


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


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


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


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.


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.


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

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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.