Maven, Gradle, …


Siempre he usado maven, pero hace unos años me cayó un proyecto ya empezado, con mucho código y que estaba en gradle, así que llevo ya unos añitos trabajando también con gradle. Aquí mis impresiones.

Por un lado, al principio me gustó. Un fichero build.gradle para un proyecto sencillo se escribe desde cero sin demasiados problemas. Apply plugin java, poner repositories, dependencias y ya. Un pom.xml equivalente no se pone tan fácil desde cero, siempre es copiar y pegar de algún sitio donde ya esté hecho.

Pero con el uso y en proyectos complejos que requieren cosas, me he encontrado con bastantes pegas que hacen que gradle no me guste, más allá de usarlo en proyecto sencillos. Comento algunas de las pegas

Los classifier

En los artifactos (jar) se pone el groupId, un artifactId y una versión. A veces se pone también un classifier para poder distinguir dos artefactos que nos interesa agrupar en el mismo groupId, artifactId y version, para poder distinguirlos. Un ejemplo claro es cuando hay librerías nativas de por medio. Imagina una librería java que use gráficos que tire de librerías nativas: dll en windows, .so en linux, etc. Una práctica habitual es meter estas librerías nativas en un jar, ya que un jar no es más que empaquetado como un zip o similar y se le pueden meter todo tipo de ficheros. Luego, en ejecución, nuestro código java debe determinar el sistema operativo en el que está corriendo y cargar la librería nativa que corresponda: la .dll, la .so, etc. Es también habitual, puesto que hay distintos tipos de micros y luego arquitecturas de 32 y 64 bits, no empaquetar todas las posibles librerías nativas en un solo jar, sino meter en un jar distinto cada una de ellas, así tendremos la dlll de 32 y 64 bits, etc. Y aquí es donde entran los classifier en juego. Nuestra librería java sería por ejemplo (formato groupId:artifactId:version:classifier) com.chuidiang:librería:1.0 y cada una de las que tienen librerías nativas dentro llevarían un classifier estilo com.chuidiang:libreria:1.0:windows-32, com.chuidiang:libreria:1.0:windows-64, com.chuidiang:libreria:1.0:linux-64, etc, etc.

Pues gradle, según como montes esto, lo entiende o no. Puedes tener en tu repositorio maven todos los jar correctamente subidos, pero si tu librería tiene dependencia de todas las librerías con classifier, hay casos que gradle no las baja. Hay otros casos que sí, seguramente dependiendo de algún detalle de cómo se han organizado en el repositorio. En maven funciona siempre. Y si el repositorio y la librería no son tuyo y no puedes organizar estas dependencias, no te queda más remedio, como nos ha pasado a nosotros, que poner una a una todas las dependencias con classifier a mano. En nuestro caso, librería es de terceros, en su pom subido al repositorio maven tienen las dependencias runtime de todos las librerías con nativas dentro y gradle lo ignora, no las baja.

Repositorio local propio

Imagina dos proyectos totalmente separados, en tu PC, que no tienen que ver como para montarlos juntos en un multi-proyecto gradle o como módulos maven. Uno es una librería y el otro es un proyecto con su main. E imagina que tu proyecto usa esa librería, así que pones la dependencia correspondiente. Ahora imagina que necesitas tocar la librería, a esta le pones una versión SNAPSHOT para decir que se está modificando y para probar rápido, en tu proyecto pones dependencia de esa versión SNAPSHOT.

Con maven no hay ningún problema. Cuando tocas la librería haces un mvn install y luego desde el proyecto ya estás viendo los cambios.

Gradle, sin embargo, no tiene una forma «nativa» de hacer esto. Por mucho gradle build que hagas en la librería, no verás los cambios en tu proyecto.  Gradle build no guarda los jar que genera en su repositorio/caché de jars, por lo que el proyecto no ve los cambios.

¿Cual es la solución? Ponerle a gradle el plugin de maven, de forma que puedas hacer gradle install que subirá el jar al repositorio local de maven, y poner en tu proyecto un repositorio mavenLocal(). Bueno, es una solución, pero desde mi punto de vista le quita un punto a gradle, para hacer algo que en proyectos relativamente grandes que reutilizan librerías propias hechas aparte, tenemos que tirar de maven.

Y se me presenta además un problema adicional. Si por el motivo que sea la dependencia de la librería SNAPSHOT está en la caché de gradle, ya la hemos liado. Aunque hagas gradle install para subir una nueva versión SNAPSHOT a tu repositorio maven local, el gradle de proyecto se lía un poco, como ya la tienen en la caché, no mira si hay actualizaciones en mavenLocal. La opción –refresh-dependencies no me ha parecido funcionar, debe mirar solo en repositorios externos. Así que mi única forma de hacer esto es borrar de la caché de gradle esas versiones snapshot, cosa que según documentación no se debe hacer (tocar la caché).

Plugins

Maven es más veterano y tiene muchos más plugins hechos y probados que gradle. Con gradle, desde luego, los hay o te los puedes hacer, pero es complicado. Un ejemplo claro que vi hace tiempo es el trabajar con OSGI. En maven hay un plugin, que tendrá sus cosas, pero funciona correctamente. En gradle, cuando miré, había uno de aquella manera que apenas facilitaba nada.

Gradle, por supuesto, es mucho más versátil que maven, en el build.gradle prácticamente puedes hacer programación en groovy y hacer lo que quieras, pero no es fácil y precisamente esta versatilidad, creo que es también un problema. ¿Por qué?. En maven, si quieres hacer un plugin, hay unas reglas muy definidas, te haces una clase que debe heredar de AbstractMojo y hacer lo que ahí diga, con las reglas que definen esa clase abstracta. En gradle puedes hacer lo que quieras como quieras. En maven quizás te limita, en gradle tienes libertad de acción

¿Cual es el problema? Pues principalmente entender su configuración si eres simplemente un usuario del plugin o su forma de funcionar. En maven las configuraciones de los plugin se escriben siempre igual, puedes no entender qué significan algunos valores de la configuración o incluso si funcionan correctamente, pero sabes donde ponerlos. En gradle es poco menos que imposible saberlo si no tienes una buena documentación, que no siempre es así.

Aparte, en gradle, posibles efectos secundarios con otros plugins porque el desarrollador del otro plugin puede haber hecho algo que interfiera con el tuyo o el tuyo con el de él, mantenimiento del plugin según avanza gradle, porque igual lo que usa el plugin de gradle luego no está o cambia, etc, etc.

Un ejemplo concreto, hace poco me puse a mirar cómo ofuscar código desde maven/gradle. Lo intenté con Proguard.

¿Mi experiencia con proguard y maven?. Correcta. Busqué el maven-gradle-plugin en google, tuve que aprender algo de proguard para saber qué ofuscar, que no ofuscar y cómo, hice mis experimentos y funcionó correctamente sin problemas. No tuve que aprender cómo meter el plugin en maven, sólo como configurarlo siguiendo los pasos de maven para configuración de plugins.

¿Mi experiencia con gradle?. Bueno, seguramente la culpa no es de gradle, sino del plugin. La página de Proguard tiene un plugin para gradle, así que ese usé. La primera pega, la versión del plugin que dice dicha página (7.0.0) no está en los repositorios, tuve que buscar en mvnrepository la más moderna.

Bueno, eso no es importante. Siguiente problema, un mensaje de error como este «ERROR: Output jar must be specified after an input jar or it will be empty» realmente quiere decir que no encuentra el input jar. Una horita de google para darse cuenta de que habia puesto mal el path del jar que quiero ofuscar.

Y luego el tercer problema, donde ya desistí. proguard necesita de alguna forma ver las dependencias externas que tiene tu proyecto. Si tu proeycto necesita log4j, apache-commons, etc, necesita verlas para hacer bien su trabajo. En maven no tuve que hacer absolutamente nada. En gradle me da el error de que nos las encuentra y tienes que «currarte» el ponérselas accesibles. Googleando, no hay documentación a este respecto y los posts que he visto donde intentan resolver este problema hablan de hacerse un task de gradle que se baje las dependencias en un directorio y luego decirle al plugin de proguard que las busque ahí.

Bien, lo dicho, seguramente no es culpa de gradle, sino de la gente de proguard con su plugin, igual la versión antigua porque la 7.0.0 no está, igual mi versión de gradle, no lo sé. Pero con maven fue un rato tenerlo en marcha y con gradle veo que voy a tener que echar un buen rato.

Así que, por mi parte, seguiré usando maven en proyectos más o menos serios/grandes/con necesidad de plugins y dejaré gradle para mis pequeños programas de prueba o donde no prevea la utilización de las cosas que son más o menos estándar en gradle.

Entradas relacionadas:

  • No hay entradas relacionadas.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.