Refinando la Tarea de Copiado
⏱ Dedicación recomendada: 0 minutos
Esto considera el contenido visible y relevante, e ignora texto colapsado o marcado como opcional.
Hasta ahora, hemos creado una tarea que copia los archivos JAR de la biblioteca al módulo app
. Sin embargo, esta tarea copia todos los archivos generados en el directorio build/libs
, lo que puede no ser ideal. Podemos mejorar esta tarea permitiendo especificar qué archivos JAR deben ser copiados.
Definición de una Extensión
Para lograr esto, definiremos una extensión que nos permita configurar la tarea copyLib
para copiar solo los archivos que necesitamos. Esta extensión se colocará en el paquete extensions
dentro del módulo convention-plugins
.
package extensions
abstract class CopyLibExtension {
abstract var libNames: List<String>
}
En este código, libNames
es una lista de cadenas que representará los nombres (o prefijos) de los archivos JAR que queremos copiar. Ahora, podemos registrar esta extensión en el archivo compile.conventions.gradle.kts
para que esté disponible para la tarea copyLib
.
Registro de la Extensión
Vamos a registrar la extensión CopyLibExtension
en nuestro archivo de convenciones:
- Gradle Kotlin DSL
- Gradle Groovy DSL
import extensions.CopyLibExtension
// Crear la extensión `copyLib`
extensions.create<CopyLibExtension>("copyLib")
import extensions.CopyLibExtension
// Crear la extensión `copyLib`
extensions.create("copyLib", CopyLibExtension)
Configurando la Tarea copyLib
Ahora que tenemos la extensión registrada, podemos configurarla para que solo copie los archivos que coincidan con los nombres especificados en libNames
. La configuración de la extensión la haremos en el archivo build.gradle.kts
o build.gradle
de la biblioteca.
- Gradle Kotlin DSL
- Gradle Groovy DSL
copyLib {
libNames = listOf("lib-.+-all", /* Agregar más nombres si es necesario */)
}
copyLib {
libNames = ["lib-.+-all", /* Agregar más nombres si es necesario */]
}
Para la última parte, dividiremos la solución en partes para que sea más fácil de seguir.
- Parte 1
- Parte 2
- Parte 3
- Parte 4
Primero debemos acceder a la extensión CopyLibExtension
en la tarea copyLib
para obtener los nombres de los archivos JAR que queremos copiar.
- Gradle Kotlin DSL
- Gradle Groovy DSL
// ...
tasks.register<Copy>("copyLib") {
// ...
val libNames = project.extensions.getByType<CopyLibExtension>().libNames
}
// ...
tasks.register(Copy, "copyLib") {
// ...
def libNames = project.extensions.getByType(CopyLibExtension).libNames
}
Luego debemos verificar si la lista de nombres de archivos JAR está vacía. Si no se especifican nombres, lanzaremos una excepción para indicar que se debe configurar la extensión.
- Gradle Kotlin DSL
- Gradle Groovy DSL
// ...
tasks.register<Copy>("copyLib") {
// ...
doFirst { validateLibNames(libNames) }
}
fun validateLibNames(libNames: List<String>) {
if (libNames.isEmpty()) {
throw GradleException("Error: No library names were specified.")
}
}
// ...
tasks.register(Copy, "copyLib") {
// ...
doFirst { validateLibNames(libNames) }
}
def validateLibNames(libNames) {
if (libNames.isEmpty()) {
throw new GradleException("Error: No library names were specified.")
}
}
A continuación, veremos si los archivos JAR especificados en libNames
existen en el directorio build/libs
. Si no existen, lanzaremos una excepción para indicar que los archivos no se encontraron. Esto lo haremos en 3 pasos:
- Verificar que el directorio de destino exista.
- Obtener los nombres de los archivos copiados.
- Encontrar los archivos que faltan.
- Gradle Kotlin DSL
- Gradle Groovy DSL
// ...
tasks.register<Copy>("copyLib") {
// ...
val filesToCopy = mutableListOf<String>()
doLast { verifyCopiedFiles(filesToCopy, "$rootDir/app/libs") }
}
fun verifyCopiedFiles(filesToCopy: List<String>, targetDirPath: String) {
val targetDir = file(targetDirPath)
if (isValidDirectory(targetDir)) {
println("Libraries copied successfully to app/libs")
val copiedFiles = getCopiedFileNames(targetDir)
val missingFiles = findMissingFiles(filesToCopy, copiedFiles)
printMissingFilesReport(missingFiles)
if (missingFiles.isNotEmpty()) {
throw GradleException("Error: Some libraries were not copied.")
}
} else {
throw GradleException("Error: Target directory does not exist or the copy operation failed.")
}
}
fun printMissingFilesReport(missingFiles: List<String>) = if (missingFiles.isEmpty()) {
println("All expected libraries were copied successfully.")
} else {
println("Warning: The following files were not copied: $missingFiles")
}
// ...
tasks.register(Copy, "copyLib") {
group = "build"
description = "Copies the libraries to the application directory"
from("build/libs")
into("$rootDir/app/libs")
def libNames = project.extensions.getByType(CopyLibExtension).libNames
doFirst { validateLibNames(libNames) }
}
def validateLibNames(libNames) {
if (libNames.isEmpty()) {
throw new GradleException("No se han especificado nombres de librerías en `copyLibConfig`")
}
}
Finalmente, implementaremos el filtrado de archivos a incluir en la tarea copyLib
para que solo se copien los archivos JAR especificados en libNames
.
- Gradle Kotlin DSL
- Gradle Groovy DSL
// ...
tasks.register<Copy>("copyLib") {
// ...
include { fileTreeElement ->
val isJarIncluded = shouldIncludeFile(fileTreeElement.name, libNames, filesToCopy)
println("Checking file: ${fileTreeElement.name} (Included: $isJarIncluded)") // Depuración
isJarIncluded
}
}
fun shouldIncludeFile(fileName: String, libNames: List<String>, filesToCopy: MutableList<String>): Boolean {
var isJarIncluded = false
for (libName in libNames) {
// Convertir cada libName a una expresión regular
val regex = Regex("$libName.jar")
// Verificar si el nombre del archivo coincide con la expresión regular
if (regex.matches(fileName)) {
isJarIncluded = true
filesToCopy += fileName // Añadir el archivo a la lista de archivos a copiar
break
}
}
return isJarIncluded
}
// ...
tasks.register(Copy, "copyLib") {
// ...
include { fileTreeElement ->
def isJarIncluded = shouldIncludeFile(fileTreeElement.name, libNames, filesToCopy)
println("Checking file: ${fileTreeElement.name} (Included: $isJarIncluded)") // Depuración
isJarIncluded
}
}
def shouldIncludeFile(String fileName, List<String> libNames, List<String> filesToCopy) {
def isJarIncluded = false
libNames.each { libName ->
// Convertir cada libName a una expresión regular
def regex = ~/^$libName.jar$/ // El operador ~ convierte una cadena en una expresión regular en Groovy
// Verificar si el nombre del archivo coincide con la expresión regular
if (regex.matcher(fileName).matches()) {
isJarIncluded = true
filesToCopy << fileName // Añadir el archivo a la lista de archivos a copiar
return
}
}
return isJarIncluded
}
o un poco más conciso:
- Gradle Kotlin DSL
- Gradle Groovy DSL
// ...
tasks.register<Copy>("copyLib") {
// ...
include { fileTreeElement ->
val isJarIncluded = libNames.any { fileTreeElement.name.matches(Regex("$it.jar")) }
if (isJarIncluded) filesToCopy.add(fileTreeElement.name)
println("Checking file: ${fileTreeElement.name} (Included: $isJarIncluded)") // Depuración
isJarIncluded
}
}
tasks.register("copyLib", Copy) {
// ...
include { fileTreeElement ->
def isJarIncluded = libNames.any { fileTreeElement.name ==~ /${it}\.jar/ }
if (isJarIncluded) {
filesToCopy << fileTreeElement.name
}
println("Checking file: ${fileTreeElement.name} (Included: $isJarIncluded)") // Depuración
return isJarIncluded
}
}
- Enunciado
- Solución
- Solución (más corta)
Implementa las funciones isValidDirectory
, getCopiedFileNames
y findMissingFiles
en el archivo compile.conventions.gradle.kts
para verificar si los archivos JAR especificados en libNames
se copiaron correctamente al directorio app/libs
.
- Gradle Kotlin DSL
- Gradle Groovy DSL
fun isValidDirectory(directory: File) = directory.exists() && directory.isDirectory
fun getCopiedFileNames(directory: File): List<String> {
val copiedFiles = mutableListOf<String>()
for (file in directory.listFiles()!!) {
copiedFiles.add(file.name)
}
return copiedFiles
}
fun findMissingFiles(filesToCopy: List<String>, copiedFiles: List<String>): List<String> {
val missingFiles = mutableListOf<String>()
for (file in filesToCopy) {
if (!copiedFiles.contains(file)) {
missingFiles.add(file)
}
}
return missingFiles
}
boolean isValidDirectory(File directory) {
return directory.exists() && directory.isDirectory()
}
List<String> getCopiedFileNames(File directory) {
def copiedFiles = []
directory.listFiles().each { file ->
copiedFiles.add(file.name)
}
return copiedFiles
}
List<String> findMissingFiles(List<String> filesToCopy, List<String> copiedFiles) {
def missingFiles = []
filesToCopy.each { file ->
if (!copiedFiles.contains(file)) {
missingFiles.add(file)
}
}
return missingFiles
}
- Gradle Kotlin DSL
- Gradle Groovy DSL
fun isValidDirectory(directory: File) = directory.exists() && directory.isDirectory
fun getCopiedFileNames(directory: File) = directory.listFiles()?.map { it.name } ?: emptyList()
fun findMissingFiles(filesToCopy: List<String>, copiedFiles: List<String>) =
filesToCopy.filterNot { it in copiedFiles }
boolean isValidDirectory(File directory) {
directory.exists() && directory.isDirectory()
}
List<String> getCopiedFileNames(File directory) {
directory.listFiles()?.collect { it.name } ?: []
}
List<String> findMissingFiles(List<String> filesToCopy, List<String> copiedFiles) {
filesToCopy.findAll { !copiedFiles.contains(it) }
}
Si ahora corremos la tarea ./gradlew :app:run --args="Hello, world!"
, deberiamos ver un mensaje que indica que sólo las bibliotecas que cumplan con la expresión regular lib-.+-all
fueron copiadas, esto significa que todos los archivos que tengan la forma lib-<algo>-all.jar
serán copiados.
Conclusión
A lo largo de esta sección, hemos mejorado considerablemente el proceso de copia de bibliotecas empaquetadas en archivos JAR, permitiendo especificar qué archivos copiar y verificando que los archivos se copien correctamente. Esto nos permite tener un mayor control sobre el proceso de empaquetado y despliegue de las bibliotecas en Gradle.
Resumen:
- Definición de una extensión: Creamos una extensión para especificar qué archivos JAR deben copiarse.
- Automatización de la tarea
copyLib
: Ajustamos la tarea para que copie solo los archivos necesarios, basándose en los nombres definidos en la extensión. - Verificación de archivos copiados: Implementamos una verificación para asegurarnos de que todos los archivos especificados fueron copiados exitosamente al directorio de destino.
Este proceso garantiza que solo las bibliotecas necesarias sean empaquetadas y utilizadas por el módulo app
, lo que nos ayuda a mantener un flujo de trabajo eficiente y libre de errores. Ahora estamos mejor equipados para gestionar múltiples bibliotecas en proyectos más grandes y complejos, con un control más preciso sobre qué bibliotecas incluir y cómo gestionar su copia.