Java Gradle

O Gradle faz o mesmo trabalho que o Maven: baixa as bibliotecas, compila, roda os testes e empacota. A diferença está na receita: em vez de um XML, você escreve um pequeno programa em Kotlin, o build.gradle.kts. É a ferramenta padrão dos aplicativos Android e muito usada em projetos Java novos.

Você vai aprender
  • Criar um projeto com gradle init, sem perguntas.
  • O que é cada arquivo gerado, e o build.gradle.kts linha por linha.
  • O que é o wrapper (./gradlew) e por que usá-lo.
  • ./gradlew build, run e test, com as saídas reais (e uma falha).
  • Adicionar uma dependência e o que acontece quando ela não existe.
  • O que é o daemon do Gradle.
  • Quando escolher Maven ou Gradle.

Esta aula supõe que você já leu a de Maven: as ideias de dependência, repositório e estrutura src/main/java são as mesmas. Os exemplos rodam na máquina fictícia da Ana, com Gradle 9.8.1 e JDK 25.


A ideia: a mesma cozinha, outra receita

Se o Maven é uma cozinha padronizada com uma receita em formulário (você preenche os campos do pom.xml), o Gradle é a mesma cozinha, mas a receita é escrita como um roteiro: dá para pôr "se", "repita", criar tarefas novas. Para projetos simples, as duas receitas têm quase o mesmo tamanho. Para projetos enormes e cheios de particularidades, o roteiro do Gradle é mais flexível.

O Gradle trabalha com tarefas (tasks): compileJava, test, jar, build… Cada tarefa depende de outras, e o Gradle roda só as que precisam (se nada mudou, ele pula).


1. Conferir a instalação

No laboratório, o Gradle já está em /opt/gradle (para instalar no seu computador, baixe o zip de gradle.org e ponha a pasta bin no PATH; ou use o SDKMAN!). Na primeira vez, ele dá boas-vindas:

$ gradle --version
Welcome to Gradle 9.8.1!

Here are the highlights of this release:
 - Java 27 support
 - Maven mirror settings reuse
 - Linked problem locations in build output

For more details see https://docs.gradle.org/9.8.1/release-notes.html

------------------------------------------------------------
Gradle 9.8.1
------------------------------------------------------------

Build time:    2026-10-07 16:57:46 UTC
Revision:      39daf39feb60a18b1b6ddccde53366522fef516f

Kotlin:        2.4.10
Groovy:        4.0.33
Ant:           Apache Ant(TM) version 1.10.17 compiled on April 6 2026
Launcher JVM:  25.0.4.1 (Eclipse Adoptium 25.0.4.1+1-LTS)
Daemon JVM:    /opt/jdk-25 (no Daemon JVM specified, using current Java home)
OS:            Linux 7.0.0-38-generic amd64

O Gradle é escrito em Java e roda numa JVM (Launcher JVM: 25.0.4.1). As linhas Kotlin e Groovy são as duas linguagens em que se escreve a receita: Kotlin (arquivos .gradle.kts, o padrão atual) ou Groovy (arquivos .gradle, comuns em projetos mais antigos).


2. Criar o projeto: gradle init

Sem opções, o gradle init faz uma série de perguntas (tipo de projeto, linguagem, pacote…). Dando todas as respostas na linha de comando, ele não pergunta nada:

$ mkdir mercado && cd mercado
$ gradle init --type java-application --dsl kotlin --test-framework junit-jupiter --package br.com.ana.mercado --project-name mercado --java-version 25 --no-split-project --no-incubating
Starting a Gradle Daemon, 1 stopped Daemon could not be reused, use --status for details

> Task :init
Learn more about Gradle by exploring our Samples at https://docs.gradle.org/9.8.1/samples/sample_building_java_applications.html

BUILD SUCCESSFUL in 6s
1 actionable task: 1 executed
--type java-application
Um programa Java com main (a outra opção comum é java-library, uma biblioteca).
--dsl kotlin
A receita em Kotlin (build.gradle.kts). DSL = domain-specific language, "linguagem para um assunto específico".
--test-framework junit-jupiter
Testes com o JUnit (veja Testes com JUnit).
--package br.com.ana.mercado
O pacote das classes.
--project-name mercado
O nome do projeto.
--java-version 25
A versão do Java.
--no-split-project
Um projeto só (e não vários subprojetos).
--no-incubating
Não usar recursos ainda experimentais do Gradle.

Se faltar alguma opção, você pode pôr --use-defaults para ele escolher o padrão em vez de perguntar.

O que foi criado

$ find . -type f | sort
./app/build.gradle.kts
./app/src/main/java/br/com/ana/mercado/App.java
./app/src/test/java/br/com/ana/mercado/AppTest.java
./.gitattributes
./.gitignore
./gradle/libs.versions.toml
./gradle.properties
./gradlew
./gradlew.bat
./gradle/wrapper/gradle-wrapper.jar
./gradle/wrapper/gradle-wrapper.properties
./settings.gradle.kts
ArquivoPara que serve
settings.gradle.ktsO "índice" do projeto: nome e quais subprojetos existem (aqui, um: app)
app/build.gradle.ktsA receita do subprojeto app: plugins, dependências, versão do Java, classe principal
app/src/main/java/…, app/src/test/java/…Código e testes, na mesma estrutura do Maven
gradle/libs.versions.tomlO catálogo de versões: um lugar só para as versões das bibliotecas
gradle.propertiesOpções do Gradle (aqui, liga o configuration cache, que deixa os builds seguintes mais rápidos)
gradlew, gradlew.bat, gradle/wrapper/*O wrapper (seção 4)
.gitignore, .gitattributesConfiguração para o Git (ignorar a pasta build, por exemplo)

Repare na diferença do Maven: o código fica dentro de app/. O Gradle já cria o projeto preparado para ter várias partes (um app, uma biblioteca…), cada uma com o seu build.gradle.kts.


3. A receita: build.gradle.kts

O arquivo gerado tem comentários em inglês explicando cada bloco. Sem os comentários, e depois de duas mudanças da Ana (ela tirou a biblioteca de exemplo Guava, que o modelo inclui, e acrescentou a Gson, que você vai ver adiante), ficou assim:

plugins {
    application
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation(libs.junit.jupiter)
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
    implementation("com.google.code.gson:gson:2.13.1")
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(25)
    }
}

application {
    mainClass = "br.com.ana.mercado.App"
}

tasks.named<Test>("test") {
    useJUnitPlatform()
}
TrechoO que faz
plugins { application }Liga o plugin application: compilar Java, criar o JAR e, além disso, a tarefa run e scripts para executar o programa.
repositories { mavenCentral() }Onde buscar bibliotecas: no mesmo Maven Central que o Maven usa.
dependencies { … }As bibliotecas. A palavra antes dos parênteses diz para que serve cada uma:
  implementation("grupo:nome:versão")Usada pelo programa (como a dependência sem scope do Maven). As coordenadas vão numa linha só, separadas por :.
  testImplementation(libs.junit.jupiter)Usada só nos testes (como <scope>test</scope>). libs.junit.jupiter aponta para o catálogo libs.versions.toml, que diz org.junit.jupiter:junit-jupiter, versão 6.0.1.
  testRuntimeOnly(…)Necessária só para rodar os testes (o "lançador" do JUnit).
java { toolchain { languageVersion = … 25 } }Compilar e rodar com um JDK 25. O Gradle procura um JDK 25 instalado (e, se não achar, pode baixar um, graças ao plugin foojay-resolver declarado no settings.gradle.kts).
application { mainClass = "…" }A classe principal, para o ./gradlew run.
tasks.named<Test>("test") { useJUnitPlatform() }Configura a tarefa test para usar o JUnit 5/6 (a "plataforma JUnit").

Compare com o pom.xml: a dependência da Gson, que no Maven ocupa 5 linhas de XML, aqui é uma linha. Como o arquivo é código Kotlin, o editor (como o IntelliJ) completa e confere enquanto você digita.

A classe gerada, App.java, tem um método que devolve a saudação, e um teste para ele. A Ana trocou o texto para português:

package br.com.ana.mercado;

public class App {
    public String getGreeting() {
        return "Bem-vinda ao mercado da Ana!";
    }

    public static void main(String[] args) {
        System.out.println(new App().getGreeting());
    }
}

Missão: o teste do projeto confere se getGreeting() devolve exatamente Bem-vinda ao mercado da Ana!. O main abaixo imita esse teste e mostra PASSED ou FAILED. O método está com um erro de digitação: conserte-o (sem mexer no main) até aparecer PASSED.

public class Main {
    public String getGreeting() {
        return "Bem-vindo ao mercado da Ana!";
    }

    public static void main(String[] args) {
        String esperado = "Bem-vinda ao mercado da Ana!";
        String obtido = new Main().getGreeting();
        if (esperado.equals(obtido)) {
            System.out.println("PASSED");
        } else {
            System.out.println("FAILED: expected: <" + esperado + "> but was: <" + obtido + ">");
        }
    }
}
public class Main {
    public String getGreeting() {
        return "Bem-vinda ao mercado da Ana!";
    }

    public static void main(String[] args) {
        String esperado = "Bem-vinda ao mercado da Ana!";
        String obtido = new Main().getGreeting();
        if (esperado.equals(obtido)) {
            System.out.println("PASSED");
        } else {
            System.out.println("FAILED: expected: <" + esperado + "> but was: <" + obtido + ">");
        }
    }
}

4. O wrapper: ./gradlew

O gradle init criou um script chamado gradlew (Gradle wrapper, "embrulho do Gradle"). Ele é um pequeno programa que baixa a versão certa do Gradle para este projeto e a usa. A versão está anotada em gradle/wrapper/gradle-wrapper.properties:

$ cat gradle/wrapper/gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

A linha que importa é distributionUrl: "este projeto usa o Gradle 9.8.1, baixado deste endereço". Por que isso é bom? Porque o Gradle muda bastante entre versões. Com o wrapper, quem baixar o seu projeto (um colega, o servidor que gera as versões) roda ./gradlew build e usa exatamente o mesmo Gradle, mesmo sem ter nenhum Gradle instalado. Por isso a regra é: use sempre ./gradlew num projeto que tem wrapper, e guarde os arquivos do wrapper junto com o código (no Git).

Uma observação sobre as saídas desta aula

Num terminal de verdade, o Gradle mostra uma barra de progresso animada enquanto trabalha e, no fim, deixa na tela só o resultado (BUILD SUCCESSFUL in 8s e mais duas linhas). Para ver a lista das tarefas que rodaram, como nesta aula, acrescente --console=plain ("console simples") ao comando.

O primeiro ./gradlew build

$ ./gradlew build --console=plain
Fetching distribution.
Downloading https://services.gradle.org/distributions/gradle-9.8.1-bin.zip
..............10%..............20%...............30%..............40%...............50%..............60%...............70%..............80%...............90%..............100%
Starting a Gradle Daemon, 2 stopped Daemons could not be reused, use --status for details
Calculating task graph as no cached configuration is available for tasks: build
> Task :app:processTestResources NO-SOURCE
> Task :app:processResources NO-SOURCE
> Task :app:compileJava
> Task :app:classes
> Task :app:jar
> Task :app:compileTestJava
> Task :app:testClasses
> Task :app:startScripts
> Task :app:distZip
> Task :app:distTar
> Task :app:assemble
> Task :app:test
> Task :app:check
> Task :app:build

BUILD SUCCESSFUL in 1m 54s
7 actionable tasks: 7 executed
Configuration cache entry stored.
Downloading … gradle-9.8.1-bin.zip
Só na primeira vez: o wrapper baixou o Gradle inteiro (ele fica em ~/.gradle/wrapper, 165 MB).
Starting a Gradle Daemon
O Gradle começou um daemon (veja abaixo).
> Task :app:compileJava
Cada linha é uma tarefa do subprojeto app. NO-SOURCE = não havia arquivos para ela (não existe src/main/resources).
:app:jar
Criou o JAR.
:app:distZip, :app:distTar
Criaram um .zip e um .tar prontos para distribuir o programa (com scripts e bibliotecas).
:app:test, :app:check
Rodaram os testes.
BUILD SUCCESSFUL in 1m 54s
Deu certo. Lento por causa dos downloads.
7 actionable tasks: 7 executed
Sete tarefas tinham trabalho a fazer, e todas foram executadas.

Rodando de novo, sem mudar nada:

$ ./gradlew build --console=plain
Reusing configuration cache.
> Task :app:processResources NO-SOURCE
> Task :app:processTestResources NO-SOURCE
> Task :app:compileJava UP-TO-DATE
> Task :app:classes UP-TO-DATE
> Task :app:jar UP-TO-DATE
> Task :app:compileTestJava UP-TO-DATE
> Task :app:testClasses UP-TO-DATE
> Task :app:startScripts UP-TO-DATE
> Task :app:distTar UP-TO-DATE
> Task :app:distZip UP-TO-DATE
> Task :app:assemble UP-TO-DATE
> Task :app:test UP-TO-DATE
> Task :app:check UP-TO-DATE
> Task :app:build UP-TO-DATE

BUILD SUCCESSFUL in 1s
7 actionable tasks: 7 up-to-date
Configuration cache entry reused.

UP-TO-DATE = "já está em dia": nada mudou desde a última vez, então o Gradle não refez nada. Um segundo, em vez de dois minutos. Essa é uma das forças do Gradle: ele lembra o que já fez, até os testes.

A pasta build/

No Gradle, o que é gerado vai para app/build (no Maven era target):

$ ls app/build/libs app/build/distributions
app/build/distributions:
app.tar
app.zip

app/build/libs:
app.jar

Para apagar tudo o que foi gerado: ./gradlew clean.

O daemon

Iniciar uma JVM e carregar o Gradle leva vários segundos. Para não pagar isso a cada comando, o Gradle deixa um processo rodando em segundo plano, o daemon (palavra antiga da informática para "processo que trabalha nos bastidores"). Os comandos seguintes conversam com ele e ficam bem mais rápidos. Ele se desliga sozinho depois de um tempo parado, ou com:

$ ./gradlew --stop
Stopping Daemon(s)
1 Daemon stopped

A mensagem Starting a Gradle Daemon, 2 stopped Daemons could not be reused só quer dizer que havia daemons parados (a Ana parou o daemon depois de cada exemplo) e um novo foi iniciado. O daemon ocupa memória; num computador modesto, vale parar quando terminar de trabalhar.


5. Rodar: ./gradlew run

$ ./gradlew run --console=plain
Calculating task graph as no cached configuration is available for tasks: run
> Task :app:processResources NO-SOURCE
> Task :app:compileJava UP-TO-DATE
> Task :app:classes UP-TO-DATE

> Task :app:run
Hello World!

BUILD SUCCESSFUL in 18s
2 actionable tasks: 1 executed, 1 up-to-date
Configuration cache entry stored.

A saída do seu programa aparece logo depois de > Task :app:run (este foi o run antes de a Ana traduzir a saudação). Para passar argumentos ao main: ./gradlew run --args="Ana 29".


6. Testar: ./gradlew test

O teste gerado foi adaptado pela Ana para conferir a saudação:

package br.com.ana.mercado;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class AppTest {
    @Test void saudacaoDoMercado() {
        App app = new App();
        assertEquals("Bem-vinda ao mercado da Ana!", app.getGreeting());
    }
}

O que cada parte faz (@Test, assertEquals) é o assunto de Testes com JUnit; aqui interessa como o Gradle mostra o resultado. A Ana tinha digitado Bem-vindo (com o) no App.java:

Este teste falha de propósito
$ ./gradlew test --console=plain
Reusing configuration cache.
> Task :app:processTestResources NO-SOURCE
> Task :app:processResources NO-SOURCE
> Task :app:compileJava
> Task :app:classes
> Task :app:compileTestJava
> Task :app:testClasses

> Task :app:test FAILED

AppTest > saudacaoDoMercado() FAILED
    org.opentest4j.AssertionFailedError at AppTest.java:9

1 test completed, 1 failed

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:test'.
> There were failing tests. See the report at: file:///home/ana/mercado/app/build/reports/tests/test/index.html

* Try:
> Run with --scan to get full insights from a Build Scan (powered by Develocity).

BUILD FAILED in 8s
3 actionable tasks: 3 executed
Configuration cache entry reused.

O Gradle diz qual teste falhou (saudacaoDoMercado()) e em que linha (AppTest.java:9), mas não por quê. O detalhe está no relatório em HTML (abra o index.html no navegador). Para ver o motivo direto no terminal, a Ana acrescentou um bloco testLogging à tarefa test:

tasks.named<Test>("test") {
    useJUnitPlatform()
    testLogging {
        events("passed", "failed")
        exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
    }
}
events("passed", "failed")
Mostrar uma linha para cada teste que passou ou falhou.
exceptionFormat = … FULL
Mostrar a mensagem completa do erro, com o stack trace.
$ ./gradlew test --console=plain
…
> Task :app:test FAILED

AppTest > saudacaoDoMercado() FAILED
    org.opentest4j.AssertionFailedError: expected: <Bem-vinda ao mercado da Ana!> but was: <Bem-vindo ao mercado da Ana!>
        at app//org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:158)
        at app//org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:139)
        at app//org.junit.jupiter.api.AssertEquals.failNotEqual(AssertEquals.java:201)
        at app//org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:184)
        at app//org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:179)
        at app//org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1188)
        at app//br.com.ana.mercado.AppTest.saudacaoDoMercado(AppTest.java:9)

1 test completed, 1 failed
…
BUILD FAILED in 19s

Agora está claro: esperava (expected) Bem-vinda, recebeu (but was) Bem-vindo. Depois de corrigir o App.java:

$ ./gradlew test --console=plain
Reusing configuration cache.
> Task :app:processResources NO-SOURCE
> Task :app:processTestResources NO-SOURCE
> Task :app:compileJava
> Task :app:classes
> Task :app:compileTestJava UP-TO-DATE
> Task :app:testClasses UP-TO-DATE
> Task :app:test

AppTest > saudacaoDoMercado() PASSED

BUILD SUCCESSFUL in 2s
3 actionable tasks: 2 executed, 1 up-to-date
Configuration cache entry reused.

7. Uma dependência: a Gson

Como na aula de Maven, a Ana quer mostrar a lista de compras em JSON com a Gson. Ela mudou o main:

package br.com.ana.mercado;

import com.google.gson.Gson;
import java.util.List;

public class App {
    record Item(String nome, double preco) {}

    public String getGreeting() {
        return "Bem-vinda ao mercado da Ana!";
    }

    public static void main(String[] args) {
        System.out.println(new App().getGreeting());
        List<Item> itens = List.of(new Item("arroz", 25.9), new Item("feijão", 8.75));
        System.out.println(new Gson().toJson(itens));
    }
}

Sem a linha implementation("com.google.code.gson:gson:2.13.1") no build.gradle.kts, a compilação falha com a mensagem do javac:

Este build falha de propósito
$ ./gradlew run --console=plain
…
> Task :app:compileJava FAILED
/home/ana/mercado/app/src/main/java/br/com/ana/mercado/App.java:3: error: package com.google.gson does not exist
import com.google.gson.Gson;
                      ^
/home/ana/mercado/app/src/main/java/br/com/ana/mercado/App.java:16: error: cannot find symbol
        System.out.println(new Gson().toJson(itens));
                               ^
  symbol:   class Gson
  location: class App
2 errors

[Incubating] Problems report is available at: file:///home/ana/mercado/build/reports/problems/problems-report.html

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:compileJava' (registered by plugin class 'org.gradle.api.plugins.JavaBasePlugin').
> Compilation failed; see the compiler output below.
…                                                    « os mesmos 2 erros, repetidos
* Try:
> Check your code and dependencies to fix the compilation error(s)
> Run with --scan to get full insights from a Build Scan (powered by Develocity).

BUILD FAILED in 9s
1 actionable task: 1 executed

Com a dependência no lugar:

$ ./gradlew run --console=plain
Calculating task graph as configuration cache cannot be reused because file 'app/build.gradle.kts' has changed.
> Task :app:processResources NO-SOURCE
> Task :app:compileJava
> Task :app:classes

> Task :app:run
Bem-vinda ao mercado da Ana!
[{"nome":"arroz","preco":25.9},{"nome":"feijão","preco":8.75}]

BUILD SUCCESSFUL in 4s
2 actionable tasks: 2 executed
Configuration cache entry stored.

Repare: o run do Gradle já põe a Gson (e a dependência dela) no classpath. No Maven, a Ana teve de montar o -cp à mão. As bibliotecas baixadas pelo Gradle ficam em ~/.gradle/caches (o Gradle não usa o ~/.m2 do Maven).

Entregar o programa com as bibliotecas

O app.jar sozinho não tem Main-Class nem a Gson. A tarefa installDist monta uma pasta com tudo, e um script para rodar:

$ ./gradlew installDist --console=plain
…
> Task :app:jar
> Task :app:startScripts
> Task :app:installDist

BUILD SUCCESSFUL in 9s
4 actionable tasks: 3 executed, 1 up-to-date
Configuration cache entry stored.
$ find app/build/install -type f
app/build/install/app/lib/gson-2.13.1.jar
app/build/install/app/lib/app.jar
app/build/install/app/lib/error_prone_annotations-2.38.0.jar
app/build/install/app/bin/app                « script para Linux e macOS
app/build/install/app/bin/app.bat            « script para Windows
$ app/build/install/app/bin/app
Bem-vinda ao mercado da Ana!
[{"nome":"arroz","preco":25.9},{"nome":"feijão","preco":8.75}]
$ java -jar app/build/libs/app.jar
nenhum atributo de manifesto principal em app/build/libs/app.jar

O script bin/app monta o classpath com os três JARs da pasta lib e chama a classe principal. O app.zip que o build gerou é essa mesma pasta, compactada. (Se você quiser mesmo um java -jar, precisa configurar o manifesto da tarefa jar; veja a aula de JAR para entender o porquê.)

Missão: o mercado da Ana precisa do total da compra. Use o record Item e a lista abaixo para somar os preços num laço e mostrar Total: R$ 34,65 com printf e %.2f.

import java.util.List;

public class Main {
    record Item(String nome, double preco) {}

    public static void main(String[] args) {
        List<Item> itens = List.of(new Item("arroz", 25.9), new Item("feijão", 8.75));
        double total = 0;
        // some os preços aqui

        System.out.printf("Total: R$ %.2f%n", total);
    }
}
import java.util.List;

public class Main {
    record Item(String nome, double preco) {}

    public static void main(String[] args) {
        List<Item> itens = List.of(new Item("arroz", 25.9), new Item("feijão", 8.75));
        double total = 0;
        for (Item i : itens) {
            total += i.preco();
        }
        System.out.printf("Total: R$ %.2f%n", total);
    }
}

8. Maven × Gradle

MavenGradle
Receitapom.xml (XML, declarativo)build.gradle.kts (código Kotlin) ou build.gradle (Groovy)
Criar projetomvn archetype:generategradle init
Compilarmvn compile./gradlew compileJava
Testarmvn test./gradlew test
Gerar o JARmvn package./gradlew build (ou jar)
Rodar o programaplugin extra (exec:java) ou java -cp …./gradlew run (plugin application)
Apagar o geradomvn clean (pasta target/)./gradlew clean (pasta build/)
Dependência5 linhas de <dependency>implementation("g:a:v")
Bibliotecas baixadas~/.m2/repository~/.gradle/caches
Versão da ferramentaA instalada (existe o mvnw, opcional)O wrapper ./gradlew, gerado sempre
VelocidadeRefaz fases inteirasPula tarefas em dia, daemon, cache
SaídaLonga e detalhadaCurta (barra de progresso)
Onde aparece maisEmpresas, sistemas Java tradicionais, SpringAndroid, projetos Kotlin, projetos grandes com muitas partes
Curva de aprendizadoMais fácil: há um jeito de fazer cada coisaMais flexível, e por isso há mais jeitos de errar

Qual usar? Num projeto que já existe, a que ele usa. Num projeto novo seu, qualquer uma serve; se a equipe ou o tutorial que você segue usa uma, fique com ela. As duas usam o mesmo Maven Central, a mesma estrutura src/main/java e o mesmo JUnit, então mudar de uma para outra é tranquilo.


Quando dá errado

Could not find com.google.code.gson:gson:2.99.0
$ ./gradlew run --console=plain
…
FAILURE: Build failed with an exception.

* What went wrong:
Configuration cache state could not be cached: field `classpath` of task `:app:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: error writing value of type 'org.gradle.api.internal.artifacts.configurations.DefaultResolvableConfiguration'
> Could not resolve all files for configuration ':app:compileClasspath'.
   > Could not find com.google.code.gson:gson:2.99.0.
     Searched in the following locations:
       - https://repo.maven.apache.org/maven2/com/google/code/gson/gson/2.99.0/gson-2.99.0.pom
     Required by:
         project ':app'
…
BUILD FAILED in 16s

O que aconteceu: a versão 2.99.0 não existe. A primeira frase (sobre configuration cache) assusta, mas é consequência; a causa está mais abaixo: Could not find …, com o endereço onde o Gradle procurou. Como resolver: corrija as coordenadas (procure em central.sonatype.com).

bash: ./gradlew: Permissão negada
$ ./gradlew build
bash: ./gradlew: Permissão negada

O que aconteceu: o script perdeu a permissão de execução (acontece ao copiar o projeto de um zip ou do Windows). Como resolver: chmod +x gradlew. Para rodar uma vez sem mudar a permissão: sh gradlew build.

Directory '/home/ana' does not contain a Gradle build.
$ gradle build
…
* What went wrong:
Directory '/home/ana' does not contain a Gradle build.

A Gradle build's root directory should contain one of the possible settings files: settings.gradle, settings.gradle.kts, settings.gradle.dcl. It may also contain one of the possible build files: build.gradle, build.gradle.kts, build.gradle.dcl.

To create a new Gradle build in this directory run 'gradle init'
…
BUILD FAILED in 2s

O que aconteceu: você está fora da pasta do projeto. Como resolver: cd para a pasta que tem o settings.gradle.kts (a raiz, não a app).

There were failing tests

O que aconteceu: pelo menos um teste falhou, e o Gradle para o build (não gera o JAR). Como resolver: veja o motivo no relatório app/build/reports/tests/test/index.html ou ligue o testLogging com exceptionFormat = FULL, como na seção 6. Depois corrija o código (ou o teste, se o teste estiver errado).


Referência

ComandoO que faz
gradle init --type java-application --dsl kotlin … Cria um projeto (com --use-defaults, não pergunta nada)
./gradlew buildCompila, testa e empacota (JAR, zip, tar)
./gradlew run / run --args="a b"Roda a mainClass com as dependências no classpath
./gradlew testRoda os testes
./gradlew cleanApaga a pasta build
./gradlew installDistMonta build/install/… com bin/ e lib/
./gradlew tasksLista as tarefas disponíveis
./gradlew --stopPara o daemon
./gradlew --versionVersão do Gradle do wrapper
… --console=plainSaída em texto simples, uma linha por tarefa

Exercícios

1. Rodar o programa

Complete o comando que roda o programa usando o wrapper:

2. Uma dependência

Complete a linha do build.gradle.kts que adiciona a biblioteca org.apache.commons:commons-lang3:3.17.0 para o programa:

("org.apache.commons:commons-lang3:3.17.0")

3. Só para os testes

E se a biblioteca fosse usada só nos testes?

("org.assertj:assertj-core:3.27.3")

4. Por que usar ./gradlew em vez de gradle?

5. O que quer dizer > Task :app:compileJava UP-TO-DATE?

6. Onde ficam o JAR e os relatórios gerados pelo Gradle neste projeto?


Resumo

  • O Gradle faz o mesmo que o Maven (dependências e build), com a receita em código: build.gradle.kts.
  • gradle init --type java-application --dsl kotlin … cria o projeto; o código fica em app/src/main/java.
  • Dependências: implementation("grupo:nome:versão"); só para testes: testImplementation(…).
  • Use sempre o wrapper ./gradlew: ele baixa e usa a versão certa do Gradle.
  • ./gradlew build, run, test, clean; o gerado vai para app/build/.
  • UP-TO-DATE = tarefa pulada porque nada mudou. O daemon deixa os comandos seguintes rápidos; --stop o desliga.
  • Num BUILD FAILED, procure a causa em What went wrong e nas linhas do javac ou do teste.