6 - Tareas (Tasks) en Gradle

Objetivo del tema

Comprenderás cómo Gradle modela el trabajo mediante tareas, cómo se ejecutan desde la línea de comandos y qué mecanismos ofrece para automatizar builds eficientes.

Al finalizar, podrás crear tareas personalizadas, reconocer el ciclo de vida de un build y aprovechar el soporte incremental y la caché para reducir tiempos de ejecución.

6.1 Creación y ejecución de tareas

Una tarea representa una unidad de trabajo. Gradle incluye tareas predefinidas como build, clean o test, y permite ejecutar cualquier tarea desde la terminal con gradle tarea o gradlew tarea.

  • Utiliza gradle tasks para listar las tareas disponibles en el proyecto.
  • Las tareas se pueden encadenar: gradle clean build ejecuta ambas respetando sus dependencias.
  • Los argumentos --info o --scan proporcionan diagnósticos detallados al ejecutar un build.
tasks.register("generarInforme") {
  group = "reportes"
  description = "Genera un informe con la fecha y versión del proyecto"
  doLast {
    println("Informe generado el ${new Date()} para la versión ${project.version}")
  }
}

// Ejecutar desde la terminal:
// gradle generarInforme

Registra tareas con tasks.register para aprovechar la configuración diferida, lo que evita costos innecesarios mientras no se ejecutan.

6.2 Tipos de tareas: predeterminadas y personalizadas

Gradle provee tareas predeterminadas a través de sus plugins. Por ejemplo, el plugin java aporta compileJava, processResources o jar. Las tareas personalizadas permiten extender la automatización con lógica a medida.

  • Tareas predeterminadas: se crean al aplicar plugins y siguen convenciones de directorios.
  • Tareas personalizadas simples: definen acciones doFirst/doLast para ejecutar código Groovy o Kotlin.
  • Tareas basadas en tipos: puedes heredar de DefaultTask o reutilizar tipos existentes como Copy para manipular archivos.
abstract class GenerarVersionTask extends DefaultTask {
  @TaskAction
  void imprimir() {
    println("Versión actual: ${project.version}")
  }
}

tasks.register("mostrarVersion", GenerarVersionTask)

tasks.named("build") {
  dependsOn("mostrarVersion")
}

Asociar tareas personalizadas a tareas predeterminadas amplía el pipeline sin duplicar lógica.

6.3 Ciclo de vida de un build

El proceso de build se divide en tres fases: inicialización, configuración y ejecución. En la primera, Gradle identifica los proyectos; en la segunda, configura tareas y dependencias; en la tercera, resuelve y ejecuta solo las tareas solicitadas.

  • Las tareas se configuran en la fase de configuración, por eso conviene registrar en lugar de crear directamente para evitar trabajo innecesario.
  • El DAG de tareas garantiza que cada acción se ejecute una sola vez cuando sus dependencias están listas.
  • Analizar el ciclo ayuda a detectar por qué se ejecutan tareas aun cuando no se solicitaron de forma explícita.

Consulta la guía oficial de Gradle sobre ciclo de vida del build para profundizar en cada fase y en cómo influyen los gradle scripts.

6.4 Incremental build y caching

Gradle detecta cambios para evitar tareas redundantes. Un build incremental compara entradas y salidas de cada tarea, marcándolas como up-to-date cuando nada cambió. Además, la caché de build reutiliza resultados entre ejecuciones locales o remotas.

  • Define entradas con @Input, @OutputFile y anotaciones similares para habilitar la optimización incremental.
  • Activa la caché en gradle.properties con org.gradle.caching=true.
  • El comando gradle build --build-cache aprovecha la caché incluso si no está habilitada por defecto.
tasks.register("procesarDatos") {
  inputs.file("data/origen.csv")
  outputs.file("build/cache/destino.csv")
  doLast {
    println("Procesando datos...")
    // Lógica de transformación
  }
}

// Activar caché en gradle.properties:
// org.gradle.caching=true

La documentación de Gradle Build Cache explica cómo compartir resultados entre equipos y cómo medir el beneficio con build scans.

Resumen didáctico

Dominar las tareas en Gradle implica conocer cómo se definen, cómo interactúan con el ciclo de vida y cómo aprovechar la ejecución incremental y la caché. Estas prácticas permiten builds más rápidos, repetibles y fáciles de mantener.