COMO Empaquetar Java

Tabla de Contenidos

Autores y contribuciones

Los autores de este documento son:

  • Mikolaj Izdebski, Red Hat

  • Nicolas Mailhot, Proyecto JPackage

  • Stanislav Ochotnicky, Red Hat

  • Ville Skyttä, Proyecto JPackage

  • Michal Srb, Red Hat

  • Michael Simacek, Red Hat

  • Marián Konček, Red Hat

This document is free software; see the license for terms of use, distribution and / or modification.

El código fuente de este documento está disponible en el repositorio git. Las instrucciones para compilarlo desde el código fuente están disponibles en el archivo README.

This document is developed as part of Javapackages community project, which welcomes new contributors. Requests and comments related to this document should be reported at Red Hat Bugzilla.

Before contributing please read the README file, which among other things includes basic rules which should be followed when working on this document. You can send patches to the Red Hat Bugzilla. They should be in git format and should be prepared against master branch in git. Alternatively you can also send pull requests at Github repository.

Abstracción

This document aims to help developers create and maintain Java packages in Fedora. It does not supersede or replace Java Packaging Guidelines, but rather aims to document tools and techniques used for packaging Java software on Fedora.

1. Introducción

El empaquetado limpio de Java ha sido históricamente una tarea abrumadora. La falta de un estándar que aborde el lugar físico de los archivos en el sistema, sumada al uso común de términos de licencia que solo permiten la redistribución gratuita de componentes clave como parte de un conjunto mayor, ha propiciado el lanzamiento sistemático de aplicaciones autosuficientes con copias integradas de componentes externos.

Como consecuencia, las aplicaciones solo se prueban con las versiones de los componentes que las integran, un sistema Java completo sufre la duplicación constante de los mismos módulos, e integrar varias partes puede ser una pesadilla, ya que dependen inevitablemente de los mismos elementos, solo que con versiones diferentes y sutilmente incompatibles (diferentes requisitos, diferentes errores). Cualquier actualización de seguridad o compatibilidad debe realizarse para cada uno de esos elementos duplicados.

Este problema se agrava por la práctica actual de plegar extensiones en la propia JVM después de un tiempo; un elemento que podría integrarse de forma segura en una aplicación de repente entrará en conflicto con una parte de la JVM y provocará fallas sutiles.

No es sorprendente entonces que los sistemas Java complejos tiendan a fosilizarse muy rápidamente, y que el costo de mantener las dependencias actuales se vuelva tan alto que la gente básicamente se dé por vencida.

Esta situación es incompatible con la plataforma Linux, que evoluciona rápidamente. Para lograr el objetivo de un empaquetado RPM de aplicaciones Java intuitivo para el usuario y el administrador, fue necesario desarrollar una infraestructura personalizada y reglas de empaquetado estrictas.

1.1. Introducción básica para empaquetar, razones, problemas, racionalidad

This section includes basic introduction to Java packaging world to people coming from different backgrounds. The goal is to understand language of all groups involved. If you are a Java developer coming into contact with RPM packaging for the first time start reading Java developer section. On the other hand if you are coming from RPM packaging background an introduction to Java world is probably a better starting point.

Cabría señalar que, especialmente en esta sección, quizá sacrifica la corrección por la simplicidad.

1.2. Para Empaquetadores

Java is a programming language which is usually compiled into bytecode for JVM (Java Virtual Machine). For more details about the JVM and bytecode specification see JVM documentation.

1.2.1. Ejemplo del Proyecto Java

To better illustrate various parts of Java packaging we will dissect simple Java Hello world application. Java sources are usually organized using directory hierarchies. Shared directory hierarchy creates a namespace called package in Java terminology. To understand naming mechanisms of Java packages see Java package naming conventions.

Vamos a crear una sencilla aplicación "Hola mundo" que ejecutará los siguientes pasos al ejecutarse:

  1. Ask for a name.

  2. Print out Hello World from and the name from previous step.

Para ilustrar ciertos puntos de cosas artificialmente complicadas por creación:

  • Input class used only for input of text from terminal.

  • Output class used only for output on terminal.

  • HelloWorldApp class used as main application.

Directorio de proyectos de ejemplo
$ find .
.
./Makefile
./src
./src/org
./src/org/fedoraproject
./src/org/fedoraproject/helloworld
./src/org/fedoraproject/helloworld/output
./src/org/fedoraproject/helloworld/output/Output.java
./src/org/fedoraproject/helloworld/input
./src/org/fedoraproject/helloworld/input/Input.java
./src/org/fedoraproject/helloworld/HelloWorld.java

En este proyecto, todos los paquetes se encuentran en la jerarquía de directorios src/.

Listando HelloWorld.java
Unresolved directive in introduction_for_packagers.adoc - include::{EXAMPLE}java_project/src/org/fedoraproject/helloworld/HelloWorld.java[]
Paquetes Java
org/fedoraproject/helloworld/input/Input.java
org/fedoraproject/helloworld/output/Output.java
org/fedoraproject/helloworld/HelloWorld.java

Although the directory structure of our package is hierarchical, there is no real parent-child relationship between packages. Each package is therefore seen as independent. The above example makes use of three separate packages:

  • org.fedoraproject.helloworld.input

  • org.fedoraproject.helloworld.output

  • org.fedoraproject.helloworld

La configuración del entorno consta de dos partes principales:

  • Telling JVM which Java class contains main() method.

  • Adding required JAR files on JVM classpath.

Compilación de nuestro proyecto

The sample project can be compiled to a bytecode by Java compiler. Java compiler can be typically invoked from command line by command javac.

javac $(find -name '*.java')

For every .java file corresponding .class file will be created. The .class files contain Java bytecode which is meant to be executed on JVM.

One could put invocation of javac to Makefile and simplify the compilation a bit. It might be sufficient for such a simple project, but it would quickly become hard to build more complex projects with this approach. Java world knows several high-level build systems which can highly simplify building of Java projects. Among others, probably the most known are Apache Maven and Apache Ant.

See also Maven and Ant sections.

Archivos JAR

Having our application split across many .class files would not be very practical, so those .class files are assembled into ZIP files with specific layout and called JAR files. Most commonly these special ZIP files have .jar suffix, but other variations exist (.war, .ear). They contain:

  • Compiled bytecode of our project.

  • Additional metadata stored in META-INF/MANIFEST.MF file.

  • Resource files such as images or localisation data.

  • Optionaly the source code of our project (called source JAR then).

They can also contain additional bundled software which is something we do not want to have in packages. You can inspect the contents of given JAR file by extracting it. That can be done with following command:

jar -xf something.jar

The detailed description of JAR file format is in the JAR File Specification.

Classpath

The classpath is a way of telling JVM where to look for user classes and 3rd party libraries. By default, only current directory is searched, all other locations need to be specified explicitly by setting up CLASSPATH environment variable, or via -cp (-classpath) option of the Java Virtual Machine.

Configuración de la ruta de clases classpath
java -cp /usr/share/java/log4j.jar:/usr/share/java/junit.jar mypackage/MyClass.class
CLASSPATH=/usr/share/java/log4j.jar:/usr/share/java/junit.jar java mypackage/MyClass.class

Tenga en cuenta que dos archivos JAR están separados por dos puntos en una definición de classpath.

See official documentation for more information about classpath.

Script de cobertura

Classic compiled applications use dynamic linker to find dependencies (linked libraries), whereas dynamic languages such as Python, Ruby, Lua have predefined directories where they search for imported modules. JVM itself has no embedded knowledge of installation paths and thus no automatic way to resolve dependencies of Java projects. This means that all Java applications have to use wrapper shell scripts to setup the environment before invoking the JVM and running the application itself. Note that this is not necessary for libraries.

1.2.2. Identificación del sistema de construcción

The build system used by upstream can be usually identified by looking at their configuration files, which reside in project directory structure, usually in its root or in specialized directories with names such as build or make.

Maven

Build managed by Apache Maven is configured by an XML file that is by default named pom.xml. In its simpler form it usually looks like this:

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example.myproject</groupId>
  <artifactId>myproject</artifactId>
  <packaging>jar</packaging>
  <version>1.0</version>
  <name>myproject</name>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

It describes project’s build process in a declarative way, without explicitly specifying exact steps needed to compile sources and assemble pieces together. It also specifies project’s dependencies which are usually the main point of interest for packagers. Another important feature of Maven that packagers should know about are plugins. Plugins extend Maven with some particular functionality, but unfortunately some of them may get in the way of packaging and need to be altered or removed. There are RPM macros provided to facilitate modifying Maven dependencies and plugins.

Ant

Apache Ant también está configurado por un archivo XML. Es por convención build.xml y en su forma sencilla parece esto:

<project name="MyProject" default="dist" basedir=".">
  <property name="src" location="src"/>
  <property name="build" location="build"/>
  <property name="dist" location="dist"/>

  <target name="init" description="Crear directorio de compilación">
    <mkdir dir="${build}"/>
  </target>

  <target name="compile" depends="init"
        description="Compilar la fuente">
    <javac srcdir="${src}" destdir="${build}"/>
  </target>

  <target name="dist" depends="compile"
        description="Generar jar">
    <mkdir dir="${dist}/lib"/>

    <jar jarfile="${dist}/myproject.jar" basedir="${build}"/>
  </target>

  <target name="clean" description="Vaciar archivos de compilación">
    <delete dir="${build}"/>
    <delete dir="${dist}"/>
  </target>
</project>

Ant build file consists mostly of targets, which are collections of steps needed to accomplish intended task. They usually depend on each other and are generally similar to Makefile targets. Available targets can be listed by invoking ant -p in project directory containing build.xml file. If the file is named differently than build.xml you have to tell Ant which file should be used by using -f option with the name of the actual build file.

Some projects that use Apache Ant also use Apache Ivy to simplify dependency handling. Ivy is capable of resolving and downloading artifacts from Maven repositories which are declaratively described in XML. Project usually contains one or more ivy.xml files specifying the module Maven coordinates and its dependencies. Ivy can also be used directly from Ant build files. To detect whether the project you wish to package is using Apache Ivy, look for files named ivy.xml or nodes in the ivy namespace in project’s build file.

Make

While unlikely, it is still possible that you encounter a project whose build is managed by plain old Makefiles. They contain a list of targets which consist of commands (marked with tab at the begining of line) and are invoked by make target or simply make to run the default target.

1.2.3. Cuestionario para Empaquetadores

At this point you should have enough knowledge about Java to start packaging. If you are not able to answer following questions return back to previous sections or ask experienced packagers for different explanations of given topics.

  1. ¿Qué es la diferencia entre JVM y Java?

  2. What is a CLASSPATH environment variable and how can you use it?

  3. Nombra dos sistemas de compilación típicos de Java y cómo puedes identificar cuál se está utilizando

  4. ¿Cuál es la diferencia entre los comandos java y javac?

  5. ¿Qué son los contenidos de un archivo JAR típico?

  6. What is a pom.xml file and what information it contains?

  7. ¿Cómo gestionarías el empaquetado de un software que contiene lib/junit4.jar dentro del archivo tarball de origen?

  8. Nombre al menos tres métodos para agrupar código en proyectos Java

1.3. Para Desarrolladores de Java

Packaging Java software has specifics which we will try to cover in this section aimed at Java developers who are already familiar with Java language, JVM, classpath handling, Maven, pom.xml file structure and dependencies.

Instead we will focus on basic packaging tools and relationships between Java and RPM world. One of the most important questions is: What is the reason to package software in RPM (or other distribution-specific formats). There are several reasons for it, among others:

  • Método unificado de instalación de software para usuarios de la distribución, independientemente de los proyectos en desarrollo

  • Verificación de la autenticidad de los paquetes de software mediante sus firma

  • Actualizaciones de software simplificadas

  • Manipulación automática de dependencias para usuarios

  • Diseño común del sistema de archivos en toda la distribución, impuesto por los estándares de empaquetado

  • Capacidad para administrar, supervisar y consultar paquetes instalados en varias máquinas a través de interfaces unificadas

  • Distribución de metadatos adicionales con el propio software, como las licencias utilizadas, la página web del proyecto, los registros de cambios y otra información que los usuarios o administradores puedan encontrar útil

1.3.1. Ejemplo de proyecto RPM

RPM uses spec files as recipes for building software packages. We will use it to package example project created in previous section. If you did not read it you do not need to; the file listing is available here and the rest is not necessary for this section.

Listado de directorio
Makefile
src
src/org
src/org/fedoraproject
src/org/fedoraproject/helloworld
src/org/fedoraproject/helloworld/output
src/org/fedoraproject/helloworld/output/Output.java
src/org/fedoraproject/helloworld/input
src/org/fedoraproject/helloworld/input/Input.java
src/org/fedoraproject/helloworld/HelloWorld.java

Hemos empaquetado el directorio del proyecto en el archivo helloworld.tar.gz.

Ejemplo del archivo spec
Unresolved directive in introduction_for_developers.adoc - include::{EXAMPLE}rpm_project/helloworld.spec[]

Los archivos spec de RPM contienen varias secciones básicas:

Cabecera, la cual contiene:
  • Package metadata such as its name, version, release, license, …​

  • A Summary with basic one-line summary of package contents.

  • Package source URLs denoted with Source0 to SourceN directives.

    • Source files can then be referenced by %SOURCE0 to %SOURCEn, which expand to actual paths to given files.

    • In practice, the source URL shouldn’t point to a file in our filesystem, but to an upstream release on the web.

  • Patches - using Patch0 to PatchN.

  • Project dependencies.

    • Dependencias de compilación especificadas por BuildRequires, los cuales necesitan ser determinados manualmente.

    • Las dependencias de tiempo de ejecución se detectarán automáticamente. Si no es así, tendrás que especificarlas con Requires.

    • More information on this topic can be found in the dependency handling section.

%description
  • Few sentences about the project and its uses. It will be displayed by package management software.

%prep section
  • Unpacks the sources using setup -q or manually if needed.

  • If a source file doesn’t need to be extracted, it can be copied to build directory by cp -p %SOURCE0 ..

  • Apply patches with %patch X, where X is the number of patch you want to apply. (You usually need the patch index, so it would be: %patch 0 -p1).

%build section
  • Contains project compilation instructions. Usually consists of calling the projects build system such as Ant, Maven or Make.

Sección opcional %check
  • Runs projects integration tests. Unit test are usually run in %build section, so if there are no integration tests available, this section is omitted.

%install section
  • Copies built files that are supposed to be installed into %{buildroot} directory, which represents target filesystem’s root.

%files section
  • Enumera todos los archivos que deben instalarse desde %{buildroot} en el sistema de destino.

  • Documentation files are prefixed by %doc and are taken from build directory instead of buildroot.

  • Directories that this package should own are prefixed with %dir.

%changelog
  • Contains changes to this spec file (not upstream).

  • Has prescribed format. To prevent mistakes in format, it is advised to use tool such as rpmdev-bumpspec from package rpmdevtools to append new changelog entries instead of editing it by hand.

To build RPM from this spec file save it in your current directory and run rpmbuild:

$ rpmbuild -bb helloworld.spec

If everything worked OK, this should produce RPM file ~/rpmbuild/RPMS/x86_64/helloworld-1.0-1.fc18.x86_64.rpm. You can use rpm -i or dnf install commands to install this package and it will add /usr/share/java/helloworld.jar and /usr/bin/helloworld wrapper script to your system. Please note that this specfile is simplified and lacks some additional parts, such as license installation.

Paths and filenames might be slightly different depending on your architecture and distribution. Output of the commands will tell you exact paths.

As you can see to build RPM files you can use rpmbuild command. It has several other options, which we will cover later on.

Other than building binary RPMs (-bb), rpmbuild can also be used to:

  • compilar únicamente RPM de código fuente (SRPM), es decir, los paquetes que contienen archivos de código fuente que posteriormente se pueden compilar como RPM (opción -bs)

  • compilar todo, tanto los RPM binarios como los de código fuente (opción -ba)

See rpmbuild 's manual page for all available options.

1.3.2. Consultar repositorios

Fedora incluye varias herramientas útiles que pueden resultar de gran ayuda a la hora de obtener información de los repositorios RPM.

dnf repoquery is a tool for querying information from RPM repositories. Maintainers of Java packages might typically query the repository for information like "which package contains the Maven artifact groupId:artifactId".

Averigua cual paquete contiene un artefacto determinado
$ dnf repoquery --whatprovides 'mvn(commons-io:commons-io)'
apache-commons-io-1:2.4-9.fc19.noarch

El ejemplo anterior muestra que se puede acceder al artefacto commons-io:commons-io instalando el paquete apache-commons-io.

By default, dnf repoquery uses all enabled repositories in DNF configuration, but it is possible to explicitly specify any other repository. For example following command will query only Rawhide repository:

$ dnf repoquery --available --disablerepo \* --enablerepo rawhide --whatprovides 'mvn(commons-io:commons-io)'
apache-commons-io-1:2.4-10.fc20.noarch

A veces puede resultar útil simplemente enumerar todos los artefactos que proporciona un paquete determinado:

$ dnf repoquery --provides apache-commons-io
apache-commons-io = 1:2.4-9.fc19
jakarta-commons-io = 1:2.4-9.fc19
mvn(commons-io:commons-io) = 2.4
mvn(org.apache.commons:commons-io) = 2.4
osgi(org.apache.commons.io) = 2.4.0

Output above means that package apache-commons-io provides two Maven artifacts: previously mentioned commons-io:commons-io and org.apache.commons:commons-io. In this case the second one is just an alias for same JAR file. See section about artifact aliases for more information on this topic.

Another useful tool is rpm. It can do a lot of stuff, but most importantly it can replace dnf repoquery if one only needs to query local RPM database. Only installed packages, or local .rpm files, can be queried with this tool.

Un caso de uso habitual podría ser comprobar qué artefactos de Maven proporcionan paquetes compilados localmente.

$ rpm -qp --provides simplemaven-1.0-2.fc21.noarch.rpm
mvn(com.example:simplemaven) = 1.0
mvn(simplemaven:simplemaven) = 1.0
simplemaven = 1.0-2.fc21

1.3.3. Cuestionario para Desarrolladores de Java

  1. ¿Cómo crearías un RPM binario si te dieran un RPM de código fuente?

  2. What is most common content of Source0 spec file tag?

  3. ¿Cuál es la diferencia entre las etiquetas Version y Release?

  4. ¿Cómo se aplica un parche en RPM?

  5. ¿En qué parte del sistema de archivos deben guardarse los archivos JAR?

  6. ¿Cuál es el formato del registro de cambios de RPM o qué herramienta utilizarías para generarlo?

  7. How would you install an application that needs certain layout (think ANT_HOME) while honoring distribution filesystem layout guidelines?

  8. How would you generate script for running a application with main class org.project.MainClass which depends on commons-lang jar?

2. Específicos de Java en Fedora para Usuarios y Desarrolladores

Esta sección contiene información sobre implementación de Java por efecto en Fedora, cambiando entre diferente tiempo de ejecución en Java y sobre pocas herramientas útiles las cuáles pueden ser utilizados durante empaquetado / desarrollo.

2.1. Implementación de Java en Fedora

Fedora incluye una implementación de referencia de código abierto de Java Standard Edition llamada OpenJDK. OpenJDK proporciona un entorno de ejecución de Java para aplicaciones Java y un conjunto de herramientas de desarrollo para programadores Java.

Desde el punto de vista del usuario, el comando java es probablemente el más interesante. Se trata de un lanzador de aplicaciones Java que inicia la Máquina Virtual de Java (JVM), carga el archivo .class especificado y ejecuta su método principal.

Aquí está un ejemplo acerca de como ejecutar el mismo proyecto en Java desde la sección [_example_java_project]:

$ java org/fedoraproject/helloworld/HelloWorld.class

OpenJDK proporciona muchas herramientas interesantes para los desarrolladores de Java:

  • javac es un compilador de Java que traduce los archivos fuente a código de bytes de Java, que posteriormente puede ser interpretado por la JVM.

  • jdb es un depurador de línea de comandos sencillo para aplicaciones Java.

  • javadoc es una herramienta para generar documentación Javadoc.

  • javap puede ser utilizado para desensamblar archivos de clase Java.

2.1.1. Cambiar entre diferentes implementaciones de Java

Los usuarios y los desarrolladores pueden querer tener instalados múltiples entornos de Java al mismo tiempo. Es posible en Fedora, pero sólo uno de ellos puede ser el entorno del sistema Java predeterminado. Fedora utiliza alternativas para cambiar entre diferentes JRE/JDK instalados..

# alternatives --config java

Hay 3 programas los cuales proporcionan 'java'.

  Selección    Instrucción
  -----------------------------------------------
   1           java-17-openjdk.x86_64 (/usr/lib/jvm/java-17-openjdk-17.0.2.0.8-1.fc35.x86_64/bin/java)
*+ 2           java-11-openjdk.x86_64 (/usr/lib/jvm/java-11-openjdk-11.0.14.1.1-5.fc35.x86_64/bin/java)
   3           java-latest-openjdk.x86_64 (/usr/lib/jvm/java-18-openjdk-18.0.1.0.10-1.rolling.fc35.x86_64/bin/java)

Introduzca para mantener la selección actual[+], o escriba el número de selección:

El ejemplo anterior muestra cómo elegir el entorno Java predeterminado. El comando java apuntará entonces a la implementación de Java proporcionada por el JRE especificado.

Consulte man alternatives para obtener más información sobre cómo usar alternatives.

Los desarrolladores pueden querer usar un compilador de Java de un JDK diferente. Esto se puede lograr con alternatives --config javac.

2.2. Crear classpath con build-classpath

La mayoría de las aplicaciones Java necesitan especificar la ruta de clases para funcionar correctamente. Fedora incluye varias herramientas que facilitan el trabajo con las rutas de clases.

Complexión-classpath - esta herramienta toma JAR filenames o coordenadas de artefacto como argumentos y les traslada a la cadena classpath-like. Consulte el ejemplo siguiente:

$ build-classpath log4j junit org.ow2.asm:asm
/usr/Acción/java/log4j.jar:/usr/acción/java/junit.jar:/usr/acción/java/objectweb-asm4/asm.jar

log4j corresponde a log4j.El bote almacenado en %{_javadir}. Si el archivo JAR está almacenada en subdirectorio bajo %{_javadir}, es necesariamente para aprobar subdirectory/jarname como un argumento para build-classpath. Ejemplo:

$ build-classpath httpcomponents/httpclient.jar
/usr/share/java/httpcomponents/httpclient.jar

2.3. Creando repositorio de JAR con build-jar-repository

Otra herramienta es build-jar-repository. Puede llenar directorio especificado con enlaces simbólicos/ duros a enlaces para los archivos JAR especificados De modo parecido para build-classpath, los botes pueden ser identificados por sus nombres o artefacto coordintes.

$ build-jar-repository my-repo log4j httpcomponents/httpclient junit:junit
$ ls -l my-repo/
total 0
lrwxrwxrwx. 1 msrb msrb 45 Oct 29 10:39 [httpcomponents][httpclient].jar -> /usr/share/java/httpcomponents/httpclient.jar
lrwxrwxrwx. 1 msrb msrb 25 Oct 29 10:39 [junit:junit].jar -> /usr/share/java/junit.jar
lrwxrwxrwx. 1 msrb msrb 25 Oct 29 10:39 [log4j].jar -> /usr/share/java/log4j.jar

El comando similar rebuild-jar-repository puede ser utilizado para recompilar el repositorio JAR anteriormente compilado por build-jar-repository. Consulte man rebuild-jar-repository para más información.

build-classpath-directory es una herramienta pequeña la cual puede ser utilizado para crear cadenas de classpath desde directorio especificado.

$ build-classpath-directory /usr/share/java/xstream
/usr/share/java/xstream/xstream-benchmark.jar:/usr/share/java/xstream/xstream.jar
:/usr/share/java/xstream/xstream-hibernate.jar

3. Especificaciones de Java en Empaquetado de Fedora

3.1. Distribución de Directorio

Esta sección describe las mayoría de los directorios usados para el empaquetado de Java. Cada directorio se nombra en formato de macro RPM, el cual muestra como sería utilizado en los archivos de especificaciones RPM. El nombre simbólico es seguido por la expansión de macro usual (esto es, ubicación del directorio físico en el sistema de archivos) y una descripción breve.

Directorios usados comúnmente por los paquetes normales
%{_javadir} — /usr/share/java

Directorio que aloja todos los archivos JAR que no contienen o usan código nativo y no dependen de una versión estándar de Java concreta. Los archivos JAR se pueden situar directamente en este directorio o en uno de sus subdirectorios. Con frecuencia los paquetes crean sus propios subdirectorios allí, en este caso el nombre del subdirectorio debería coincidir con el nombre del paquete.

%{_jnidir} — /usr/lib/java

Directory where architecture-specific JAR files are installed. In particular, JAR files containing or using native code (Java Native Interface, JNI) should be installed there.

%{_javadocdir} — /usr/share/javadoc

Directorio raíz donde se instala toda la documentación de la API de Java (Javadoc). Cada paquete fuente suele crear un único subdirectorio que contiene la documentación Javadoc agregada para todos los paquetes binarios que produce.

%{_mavenpomdir} — /usr/share/maven-poms

Directorio donde se instalan los archivos del Modelo de Objeto de Proyecto (POM) utilizados por Apache Maven. Cada POM debe tener un nombre que corresponda estrictamente al archivo JAR en %{_javadir} o %{_jnidir}.

%{_ivyxmldir} — /usr/share/ivy-xmls

Directorio donde ivy.xml los archivos utilizaron por Apache Ivy está instalada. Cada XML tiene que tener nombre que estrictamente corresponde a archivo de JAR en %{_javadir} o %{_jnidir}.

Otros directorios
%{_jvmdir} — /usr/lib/jvm

Directorio raíz donde se instalan las distintas máquinas virtuales Java (JVM). Cada JVM crea un subdirectorio, posiblemente con varios nombres alternativos implementados mediante enlaces simbólicos. Los directorios que comienzan con java contienen el Kit de Desarrollo de Java (JDK), mientras que los directorios cuyos nombres comienzan con jre contienen el Entorno de Ejecución de Java (JRE).

%{_jvmsysconfdir} — /etc/jvm
%{_jvmcommonsysconfdir} — /etc/jvm-common

Directorios que contienen archivos de configuración para máquinas virtuales Java (JVM).

%{_jvmprivdir} — /usr/lib/jvm-private
%{_jvmlibdir} — /usr/lib/jvm
%{_jvmdatadir} — /usr/share/jvm
%{_jvmcommonlibdir} — /usr/lib/jvm-common
%{_jvmcommondatadir} — /usr/share/jvm-common

Los directorios que contienen archivos de implementación de Máquinas Java Virtuales (JVM). Describiéndoles en detalle es fuera de alcance para este documento. El propósito de cada directorio está comentado brevemente en el archivo macros.jpackage en /etc/rpm. Más descripción detallada puede encontrarse en la normativa de JPackage.

%{_javaconfdir} — /etc/java

El directorio que contiene archivos de configuración de Java. En particular contiene el archivo principal de la configuración de Java — java.conf.

3.2. Identificación de Archivo JAR

Las aplicaciones Java complejas normalmente constan de múltiples componentes. Cada componente puede tener múltiples implementaciones, llamadas artefactos. Los artefactos en el contexto Java normalmente son archivos JAR, pero también pueden ser archivos WAR y cualquier otra familia de archivo.

Hay múltiples formas incompatibles de identificar (denominar) artefactos Java y cada sistema de compilación a menudo fomenta el uso de un esquema de nomenclatura específico. Esto significa que las distribuciones Linux también necesitan permitir que cada artefacto sea ubicado utilizando varios identificadores diferentes, utilizando posiblemente diferentes esquemas. Por otro lado es prácticamente imposible utilizar todos los esquemas de nomenclatura, por lo que existen algunas simplificaciones.

Este capítulo describe diferentes formar de identificar y localizar artefactos en el repositorio del sistema.

3.2.1. Rutas relativas

Los artefactos JAR son instalados un uno de los árboles de directorio estándar. Usualmente esto es o bien %{_javadir} (/usr/share/java) o bien %{_jnidir} (/usr/lib/java).

La forma más sencilla de identificar artefactos es mediante su ruta relativa desde una de las ubicaciones estándar. Todos los artefactos pueden identificarse de esta manera, ya que cada uno tiene un nombre de archivo único. En este documento, cada ruta que identifica un artefacto se denominará "ruta del artefacto".

Para que las rutas de los artefactos sean más sencillas y legibles, se puede omitir la extensión si es igual a jar. Para los artefactos que no son JAR, la extensión no se puede omitir y debe conservarse.

Además, si la ruta del artefacto apunta a un directorio, representa todos los artefactos contenidos en dicho directorio. Esto permite hacer referencia fácilmente a un conjunto completo de artefactos relacionados, simplemente especificando el nombre del directorio que los contiene.

Si el mismo parche del artefacto tiene extensiones válidas en dos directorios raíz diferentes entonces no está especificado cuales artefactos estarán situados.

3.2.2. Especificación de artefacto

Como notado en sección anterior, cada artefacto puede ser identificado únicamente por su ruta de archivo. Sin embargo esto no siempre la manera preferida para identificación de artefacto.

Modern Java build systems provide a way of identifying artifacts with an abstract identifier, or more often, a pair of identifiers. The first if usually called group ID or organization ID while the second is just artifact ID. This pair of identifiers will be called artifact coordinates in this document. Besides group ID and artifact ID, artifact coordinates may also include other optional information about artifact, such as extension, classifier and version.

In Linux distributions it is important to stay close to upstreams providing software being packaged, so the ability to identify artifacts in the same way as upstream does is very important from the packaging point of view. Every artifact can optionally be identified by artifact coordinates assigned during package build. Packages built with Maven automatically use this feature, but all other packages, even these built with pure javac, can use this feature too (see description of %mvn_artifact and %add_maven_depmap macros).

3.2.3. Alias

Los alias funcionan de dos maneras:

  • Enlaces simbólicos para rutas

  • Relaciones adicionales para las especificaciones de artefactos

In the real world the same project can appear under different names as it was evolving or released differently. Therefore other projects may refer to those alternative names instead of using the name currently prefered by upstream.

Aliases de artefacto

XMvn provides a way to attach multiple artifact coordinates to a single artifact. Dependent projects that use alternative coordinates can then be built without the need to patch their POMs or alter the build by other means. It will also generate virtual provides for the alias, so it can be also used in Requires and BuildRequires. Creating an alias is achieved by %mvn_alias macro.

Ejemplo de invocación
# com.example.foo:bar (el artefacto real existente en el proyecto) también
# está disponible como com.example.foo:bar-all
%mvn_alias com.example.foo:bar com.example.foo:bar-all

# No es necesario repetir la parte de las coordenadas que permanece igual
# (groupID en este caso)
%mvn_alias com.example.foo:bar :bar-all

# Puedes especificar varios alias a la vez.
%mvn_alias com.example.foo:bar :bar-all :bar-lib

# La macro admite varios atajos para generar múltiples alisaes.
# Ramas - {} - captura su contenido, los cuales entonces ser referenced en la
# parte alias con @N, donde N es el índice del grupo de captura.
# * actúa como comodín (coteja cualquiera)
# El siguiente genera aliases finalizados con sombreados para todos artefactos
# en el proyecto
%mvn_alias 'com.ejemplo.talcual:{}' :@1-sombreado

3.2.4. Versiones de compatibilidad

Manipular para compatibilidad de paquetes, jars versionados, etc.

In Fedora we prefer to always have only the latest version of a given project. Unfortunately, this is not always possible as some projects change too much and it would be too hard to port dependent packages to the current version. It is not possible to just update the package and keep the old version around as the names, file paths and dependency provides would clash. The recommended practice is to update the current package to the new version and create new package representing the old version (called compat package). The compat package needs to have the version number (usually only the major number, unless further distinction is necessary) appended to the name, thus effectivelly having different name from RPM’s point of view. Such compat package needs to perform some additional steps to ensure that it can be installed and used along the non-compat one.

You should always evaluate whether creating a compat package is really necessary. Porting dependent projects to new versions of dependencies may be a complicated task, but your effort would be appreciated and it is likely that the patch will be accepted upstream at some point in time. If the upstream is already inactive and the package is not required by anything, you should also consider retiring it.

Versiones de compatibilidad de Maven

XMvn supports marking particular artifact as compat, performing the necessary steps to avoid clashes with the non-compat version. An artifact can be marked as compat by %mvn_compat_version. It accepts an artifact argument which will determine which artifact will be compat. The format for specifying artifact coordinates is the same as with %mvn_alias. In the common case you will want to mark all artifacts as compat. You can specify multiple compat versions at a time.

Resolución de dependencias de artefactos de compatibilidad

Cuando XMvn realiza la resolución de dependencias para un artefacto de dependencia en un proyecto, verifica la versión de la dependencia y la compara con todas las versiones del artefacto instaladas en el directorio de compilación. Si ninguno de los artefactos compatibles coincide, resolverá el artefacto al que no sea compatible. Esto tiene algunas implicaciones:

  • The versions are compared for exact match. The compat package should provide all applicable versions that are present in packages that are supposed to be used with this version.

  • The dependent packages need to have correct BuildRequires on the compat package as the virtual provides is also different (see below).

Los nombres de archivo y los proveedores virtuales

In order to prevent file name clashes, compat artifacts have the first specified compat version appended to the filename. Virtual provides for compat artifacts also contain the version as the last part of the coordinates. There are multiple provides for each specified compat version. Non-compat artifact do not have any version in the virtual provides.

Ejemplo de invocación de %mvn_compat_version
# Asumiendo el paquete tiene barra de nombre y versión 3
# Conjuntos el compat versión de foo:artefacto de barra a 3
%mvn_compat_versión foo:barra 3
# El archivo de artefacto instalada (suponiendo que es un jar y no había ninguno
# %mvn_llamadas de lima) será en %{_javadir}/barra/de barra-3.Bote
# El generado proporciona para foo:la barra será
# mvn(foo:barra:3) = 3
# mvn(foo:barra:pom:3) = 3

# Establece las versiones de compatibilidad de todos los artefactos en la compilación a 3 y 3.2.
%mvn_compat_version : 3 3.2

3.3. Manipular Dependencia

RPM has multiple types of metadata to describe dependency relationships between packages. The two basic types are Requires and Provides. Requires denote that a package needs something to be present at runtime to work correctly and the package manager is supposed to ensure that requires are met. A single Requires item can specify a package or a virtual provide. RPM Provides are a way to express that a package provides certain capability that other packages might need. In case of Maven packages, the Provides are used to denote that a package contains certain Maven artifact. They add more flexibility to the dependency management as single package can have any number of provides, and they can be moved across different packages without breaking other packages' requires. Provides are usually generated by automatic tools based on the information from the built binaries or package source.

Gestión de dependencias para paquetes Maven

The Java packaging tooling on Fedora provides automatic Requires and Provides generation for packages built using XMvn. The Provides are based on Maven artifact coordinates of artifacts that were installed by the currently being built. They are generated for each subpackage separately. They follow a general format mvn(groupId:artifactId:extension:classifier:version), where the extension is omitted if its jar and classifier is omitted if empty. Version is present only for compat artifacts, but the trailing colon has to be present unless it is a Jar artifact with no classifier.

# Ejemplo de provisión para artefacto Jar
mvn(org.eclipse.jetty:jetty-server)
# Ejemplo de provisión para artefacto POM
mvn(org.eclipse.jetty:jetty-parent:pom:)
# Ejemplo de provisión para artefacto Jar con clasificador
mvn(org.sonatype.sisu:sisu-guice::no_aop:)

The generated Requires are based on dependencies specified in Maven POMs in the project. Only dependencies with scope set to either compile, runtime or not set at all are used for Requires generation. Requires do not rely on package names and instead always use virtual provides that were described above, in exactly the same format, in order to be satisfiable by the already existing provides. For packages consisting of multiple subpackages, Requires are generated separately for each subpackage. Additionally, Requires that point to an artifact in a different subpackage of the same source package are generated with exact versions to prevent version mismatches between artifacts belonging to the same project.

The requires generator also always generates Requires on java-headless and javapackages-tools.

Manejo de dependencias para paquetes que no son Maven y que envían archivos POM

If the package is built built using different tool than Apache Maven, but still ships Maven POM(s), the you will still get automatic provides generation if you install the POM using %mvn_artifact and %mvn_install. The requires generation will also be executed but the outcome largely depends on whether the POM contains accurate dependency insformation. If it contains dependency information, you should double-check that it is correct and up-to-date. Otherwise you need to add Requires tags manually as described in the next section.

Manejo de dependencias para paquetes que no son Maven y que no incluyen archivos POM

For packages without POMs it is necessary to specify Requires tags manually. In order to build the package you needed to specify BuildRequires tags. Your Requires tags will therefore likely be a subset of your BuildRequires, excluding build tools and test only dependencies.

Consulta de Requisitos y Prestaciones de paquetes compilados

Los Requisitos y Provisiones generados de los paquetes compilados se pueden consultar usando rpm:

rpm -qp --provides path/to/example-1.0.noarch.rpm
rpm -qp --requires path/to/example-1.0.noarch.rpm
Generando BuildRequires

While Requires and Provides generation is automated for Maven projects, BuildRequires still remains a manual task. However, there are tools to simplify it to some extent. XMvn ships a script xmvn-builddep that takes a build.log output from mock and prints Maven-style BuildRequires on artifacts that were actually used during the build. It does not help you to figure out what the BuildRequires are before you actually build it, but it may help you to have a minimal set of BuildRequires that are less likely to break, as they do not rely on transitive dependencies.

3.4. Paquetes de Javadoc

Javadoc subpackages in Fedora provide automatically generated API documentation for Java libraries and applications. Java Development Kit comes with tool called javadoc. This tool can be used for generating the documentation from specially formated comments in Java source files. Output of this tool, together with license files, usually represents only content of javadoc subpackages. Note javadoc invocation is typically handled by build system and package maintainer does not need to deal with it directly.

Javadoc subpackage shouldn’t depend on its base package and vice versa. The rationale behind this rule is that documentation can usually be used independently from application / library and therefore base package does not need to be always installed. Users are given an option to install application and documentation separately.

You can learn more about javadoc from official documentation.

3.5. Paquetes principales de Java

3.5.1. JVM

Fedora permite que múltiples Java Virtual Machines (JVM) sean empaquetadas independientemente. Los paquetes Java no deberían depender directamente de ninguna JVM en particular, sino que requiere de uno de los tres paquetes JVM virtuales dependiendo de la funcionalidad que se requiera.

java-headless

Este paquete proporciona un Java Runtime Environment (Entorno Java en Tiempo de Ejecución) (JRE) en funcionamiento con alguna funcionalidad inhabilitada. El soporte de gráficos y audio puede no estar disponible en este caso. java-headless proporciona una funcionalidad que es bastante para la mayoría de los casos y evita tirar de diversas bibliotecas de gráficos y audio como dependencias. Los requisitos en java-headless son apropiados para la mayoría de los paquetes Java.

java

Incluye la misma funcionalidad base que java-headless, pero también implementa subsistemas de audio y gráficos. Los paquetes deberían requerir java si necesitan alguna funcionalidad de estos subsistemas, por ejemplo la creación de IGU (Interfaz Gráfica de Usuario) usando la biblioteca AWT (Conjunto de Herramientas de X Window).

java-devel

Proporciona el Java Development Kit (Conjunto de Desarrollo Java) (JDK) completo. En la mayoría de los casos solo los paquetes relacionados con el desarrollo Java deberían tener dependencias en tiempo de ejecución sobre java-devel. Los paquetes en tiempo de ejecución deberían requerir java-headless o java. Algunos paquetes no relacionados estrictamente con el desarrollo java necesitan acceso a bibliotecas incluidas con JDK, pero no con JRE (por ejemplo tools.jar). Eso es uno de lo pocos casos donde puede ser necesario requerir java-devel.

Los paquetes que requieren una versión mínima estándar de Java pueden añadir dependencias versionadas en uno de los paquetes virtuales que proporcionan entorno Java . Por ejemplo si paquetes que dependen de la funcionalidad de JDK 8 pueden requerir java-headless >= 1:1.8.0.

Époco en versiones de paquetes JVM

Por compatibilidad con el proyecto JPackage los paquetes que proporcionan Java 1.6.0 o posterior usan época igual a 1. Esto fue necesario porque el paquete java-1.5.0-ibm del proyecto JPackage tenía época `1`por alguna razón, de modo que los paquetes que proporcionan otras implementaciones de JVM también tienen que usar época distinta de cero para mantener la ordenación correcta de versiones.

3.5.2. Herramientas de Paquetes Java

Las Herramientas de Paquetes Java están empaquetados como varios paquetes RPM binarios:

maven-local

Este paquete proporciona un entorno completo el cual es requerido para compilar paquetes Java utilizando sistema de compilación Apache Maven. Esto incluye una versión del sistema predeterminada del Java Developement Kit (JDK), Maven, un número de complementos Maven comúnmente utilizados para compilar paquetes, varios macros y herramientas de utilidades. `maven-local`usualmente está declarado como dependencia de compilación de paquetes Maven.

ivy-local

Analógicamente para maven-local, este paquete proporciona un entorno requerido para compilar paquetes de Java utilizando Apache Ivy como gestor de dependencia.

javapackages-local

El paquete que proporciona un entorno básico necesario para generar e instalar metadatos para el sistema del repositorio de artefacto.

javapackages-tools

Empaqueta propiedad básica de directorios Java y proporciona soporte para tiempo de ejecución para paquetes de Java. La gran mayoría de los paquetes Java dependen de javapackages-tools.

4. Mejores Prácticas de Empaquetado

Packaging Java has certain specifics that will be covered in this section which will cover basic packaging principles:

  • No vincular

  • Funcionando en desarrollo

  • Comentar solución

  • Versión de biblioteca única

  • Enlaces a otros documentos apropiados

5. Construcciones de Java Genéricos

This chapter talks about basic build steps in Java such as invoking javac and using spec macros like build-claspath and build-jar-repository.

5.1. Generación de Scripts de Intérprete de Aplicaciones

As mentioned in section about Java packaging basics, all Java applications need wrapper shell scripts to setup the environment before running JVM and associated Java code.

The javapackages-tools package contains a convenience %jpackage_script macro that can be used to create scripts that work for the majority of packages. See its definition and documentation in /usr/lib/rpm/macros.d/macros.jpackage. One thing to pay attention to is the 6th argument to it - whether to prefer a JRE over a full SDK when looking up a JVM to invoke. Most packages that do not require the full Java SDK will want to set that to true to avoid unexpected results when looking up a JVM when some of the installed JREs do not have the corresponding SDK (*-devel package) installed.

%install
...
%jpackage_script msv.textui.Driver "" "" msv-msv:msv-xsdlib:relaxngDatatype:isorelax msv true
...

The previous example installs the msv script (5th argument) with main class being msv.textui.Driver (1st argument). No optional flags (2nd argument) or options (3rd argument) are used. This script will add several libraries to classpath before executing main class (4th argument, JAR files separated with :). build-classpath is run on every part of 4th argument to create full classpaths.

Sometimes it may be needed to replace all JAR files in current directory with symlinks to the system JARs located in %{_javadir}. This task can be achieved using tool called xmvn-subst.

$ ls -l
-rw-r--r--. 1 msrb msrb  40817 Oct 22 09:16 cli.jar
-rw-r--r--. 1 msrb msrb 289983 Oct 22 09:17 junit4.jar
-rw-r--r--. 1 msrb msrb 474276 Oct 22 09:14 log4j.jar
$ xmvn-subst .
[INFO] Linked ./cli.jar to /usr/share/java/commons-cli.jar
[INFO] Linked ./log4j.jar to /usr/share/java/log4j.jar
[INFO] Linked ./junit4.jar to /usr/share/java/junit.jar
$ ls -la
lrwxrwxrwx. 1 msrb msrb   22 Oct 22 10:08 cli.jar -> /usr/share/java/commons-cli.jar
lrwxrwxrwx. 1 msrb msrb   22 Oct 22 10:08 junit4.jar -> /usr/share/java/junit.jar
lrwxrwxrwx. 1 msrb msrb   22 Oct 22 10:08 log4j.jar -> /usr/share/java/log4j.jar

The example above shows how easy the symlinking can be. However, there are some limitations. Original JAR files need to carry metadata which tell xmvn-subst for what artifact given file should be substituted. Otherwise xmvn-subst won’t be able to identify the Maven artifact from JAR file.

Consulte xmvn-subst -h para ver todas las opciones disponibles.

6. Ant

Apache Ant es una biblioteca Java y una herramienta de línea de comando cuya misión es conducir los procesos en los archivos creado como objetivos y puntos de extensión dependientes unos de otros.

Apache Ant is one of the most popular Java build tools after Apache Maven. The main difference between these two tools is that Ant is procedural and Maven is declarative. When using Ant, it is neccessary to exactly describe the processes which lead to the result. It means that one needs to specify where the source files are, what needs to be done and when it needs to be done. On the other hand, Maven relies on conventions and doesn’t require specifying most of the process unless you need to override the defaults.

If upstream ships a Maven POM file, it must be installed even if you don’t build with Maven. If not, you should try to search Maven Central Repository for it, ship it as another source and install it.

Archivo de especificaciones común
BuildRequires: ant
BuildRequires: javapackages-local
...
%build
ant test

%install
%mvn_artifact pom.xml lib/%{name}.jar

%mvn_install -J api/

%files -f .mfiles
%files javadoc -f .mfiles-javadoc
Details
  • %build section uses ant command to build the project and run the tests. The used target(s) may vary depending on the build.xml file. You can use ant -p command to list the project info or manually look for <target> nodes in the build.xml file.

  • %mvn_artifact macro is used to request installation of an artifact that was not built using Maven. It expects a POM file and a JAR file. For POM only artifacts, the JAR part is omitted. See [_installing_additional_artifacts] for more information.

  • %mvn_install performs the actual installation. Optional -J parameter requests installation of generated Javadoc from given directory.

  • This method of artifact installation allows using other XMvn macros such as %mvn_alias or %mvn_package.

  • %mvn_install generates .mfiles file which should be used to populate %files section with -f switch. For each subpackage there would be separate generated file named .mfiles-subpackage-name.

  • All packages are required to own directories which they create (and which are not owned by other packages). JAR files are by default installed into subdirectory of %{_javadir}. To override this behavior, use %mvn_file.

6.1. Apache Ivy

Apache Ivy provides an automatic dependency management for Ant managed builds. It uses Maven repositories for retrieving artifacts and supports many declarative features of Maven such as handling transitive dependencies.

XMvn admite la resolución local de artefactos Ivy, su instalación y requiere generación.

Archivo spec
BuildRequires: ivy-local
...
%build
ant -Divy.mode=local test

%install
%mvn_artifact ivy.xml lib/sample.jar

%mvn_install -J api/

%files -f .mfiles
%files -javadoc -f .mfiles-javadoc
Details
  • -Divy.mode=local tells Ivy to use XMvn local artifact resolution instead of downloading from the Internet.

  • Si hay un ivy-settings.xml o archivo similar, el cual especifica repositorios remotos, necesita ser deshabilitado, de lo contrario, anularía la resolución local.

  • %mvn_artifact supports installing artifacts described by Ivy configuration files.

  • %mvn_install performs the actual installation. Optional -J parameter requests installation of generated Javadoc from given directory.

Manipulación de archivos Ivy

A subset of macros used to modify Maven POMs also work with ivy.xml files allowing the maintainer to add / remove / change dependencies without the need of making patches and rebasing them with each change. You can use dependency handling macros %pom_add_dep, %pom_remove_dep, %pom_change_dep and generic %pom_xpath_* macros. For more details, see corresponding manpages.

# Eliminar la dependencia del artefacto con org="com.ejemplo" y
# nombre="java-proyecto" desde el archivo ivy.xml en el directorio actual
%pom_remove_dep com.example:java-project

# Añade una dependencia del artefacto con org="com.example" y
# name="foobar" a ./submodule/ivy.xml
%pom_add_dep com.ejemplo:foobar submodule
Utilizar la tarea ivy:publish

Ivy supports publishing built artifact with ivy:publish task. If your build.xml file already contains a task that calls ivy:publish, you can set the resolver attribute of the ivy:publish element to xmvn. This can be done with simple %pom_xpath_set call. Then when the task is run, XMvn can pick the published artifacts and install them during the run of %mvn_install without needing you to manually specify them with %mvn_artifact.

Archivo spec utilizando la tarea ivy:publish
BuildRequires: ivy-local
...
%prep
%pom_xpath_set ivy:publish/@resolver xmvn build.xml

%build
ant -Divy.mode=local test publish-local

%install
%mvn_install -J api/

%files -f .mfiles
%files -javadoc -f .mfiles-javadoc
Details
  • Es posible que el destino de publicación sea nombrado diferentemente. Busca la build.xml para las apariciones de ivy:publish.

  • %mvn_install instalará todos los artefactos publicados.

7. Maven

Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project’s build, reporting and documentation from a central piece of information.

Maven is by far the most consistent Java build system, allowing large amount of automation. In most common situations only following steps are necessary:

  1. In %build section of the spec file use %mvn_build macro.

  2. In %install section, use %mvn_install macro.

  3. Use generated file .mfiles lists to populate %files section with -f switch.

Secciones comunes del archivo spec
BuildRequires:  maven-local
...
%build
%mvn_build
...

%install
%mvn_install
...

%files -f .mfiles
%dir %{_javadir}/%{name}

%files javadoc -f .mfiles-javadoc

The macros %mvn_build and %mvn_install automatically handle building of the JAR files and their subsequent installation to the correct directory. The corresponding POM and metadata files are also installed.

7.1. Empaquetado del proyecto Maven

This step by step guide will show you how to package Maven project. Let’s start with probably the simplest spec file possible.

Unresolved directive in packaging_maven_project.adoc - include::{EXAMPLE}maven_project/simplemaven.spec[]

The spec file above is a real world example how it may look like for simple Maven project. Both %build and %install sections consist only of one line.

Another interesting line:

10: BuildRequires:  maven-local

All Maven projects need to have BuildRequires on maven-local. They also need to have Requires and BuildRequires on jpackages-utils, but build system adds these automatically. The package maintainer does not need to list them explicitly.

31: %dir %{_javadir}/%{name}

By default, resulting JAR files will be installed in %{_javadir}/%{name}, therefore the package needs to own this directory.

The build could fail from many reasons, but one probably most common is build failure due to missing dependencies.

We can try to remove these missing dependencies from pom.xml and make Maven stop complaining about them. However, these removed dependencies may be crucial for building of the project and therefore it may be needed to package them later. Let’s remove the dependencies from pom.xml.

Retira dependencias desde pom.xml
...
%prep
%setup -q

# Añada las líneas siguientes a la sección %prep de un archivo específico
%pom_remove_dep :commons-io
%pom_remove_dep :junit

The package maintainer can use a wide variety of “pom_” macros for modifying pom.xml files. See the [_macros_for_pom_modification] section for more information.

Now try to build the project again. The build will fail with a compilation failure.

Oops, another problem. This time Maven thought it had all the necessary dependencies, but Java compiler found otherwise.

Now it is possible to either patch the source code not to depend on missing libraries or to package them. The second approach is usually correct. It is not necessary to package every dependency right away. The maintainer could package compile time dependencies first and keep the rest for later (test dependencies, etc.). But Maven needs to know that it should not try to run tests now. This can be achieved by passing -f option to %mvn_build macro. Maven will stop complaining about missing test scoped dependencies from now on.

Another reason to disable the test phase is to speed up the local build process. This can also be achieved by specifying an additional switch --without=tests to the fedpkg or the mock tool instead of adding a switch to %mvn_build.

Otro parámetro, --without=javadoc, hace que la compilación omita la generación de Javadoc.

It is always recommended to run all available test suites during build. It greatly improves quality of the package.

We already have package which provides commons-io:commons-io artifact, let’s add it to the BuildRequires. Also disable tests for now.

BuildRequires:  maven-local
BuildRequires:  apache-commons-io
...
%prep
%setup -q

# Comentario fuera de líneas siguientes en la sección %prep
#%%pom_sacar_dep :commons-io
#%%pom_sacar_dep :junit

%build
# Omitir pruebas por ahora, falta la dependencia junit:junit:4.11
%mvn_build -f

One can easily search for package which provides the desired artifact. Try dnf repoquery --whatprovides 'mvn(commons-io:commons-io)', or see how to query repositories.

Now try to build the project one more time. The build should succeed now. Congrats, you managed to create an RPM from Maven project!

There is plenty of other things maintainer may want to do. For example, they may want to provide symbolic links to the JAR file in %{_javadir}.

Esto se puede lograr fácilmente con la macro %mvn_file:

%prep
%setup -q

%mvn_file : %{name}/%{name} %{name}

See [_alternative_jar_file_names] section for more information.

Another quite common thing to do is adding aliases to Maven artifact. Try to run rpm -qp --provides on your locally built RPM package:

$ rpm -qp --provides simplemaven-1.0-1.fc21.noarch.rpm
mvn(com.example:simplemaven) = 1.0
simplemaven = 1.0-1.fc21

The output above tells us that the RPM package provides Maven artifact com.example:simplemaven:1.0. Upstream may change the groupId:artifactId with any new release. And it happens. For example org.apache.commons:commons-io changed to commons-io:commons-io some time ago. It is not a big deal for package itself, but it is a huge problem for other packages that depends on that particular package. Some packages may still have dependencies on old groupId:artifactId, which is suddenly unavailable. Luckily, there is an easy way how to solve the problems like these. Package maintainer can add aliases to actually provided Maven artifact.

Agregar alias al artefacto Maven
%mvn_alias org.example:simplemaven simplemaven:simplemaven

See [_additional_mappings] for more information on %mvn_alias.

Reconstruir el pacakge y control rpm -qp --provides salida otra vez:

$ rpm -qp --provides simplemaven-1.0-2.fc21.noarch.rpm
mvn(com.example:simplemaven) = 1.0
mvn(simplemaven:simplemaven) = 1.0
simplemaven = 1.0-2.fc21

Now it does not matter if some other package depends on either of these listed artifact. Both dependencies will always be satisfied with your package.

One could try to fix dependencies in all the dependent packages instead of adding an alias to single package. It is almost always wrong thing to do.

7.2. Macros para configuración del empaquetado Maven

Pueden configurarse las compilaciones Maven para producir distribuciones alternativas, incluyendo aliases adicionales dentro de metadatos del paquete o creando subpaquetes separados para ciertos artefactos.

7.2.1. Instalar artefactos adicionales

It is possible to explicitly request an installation of any Maven artifact (JAR / POM file). Macro %mvn_install only knows about Maven artifacts that were created during execution of %mvn_build. Normally, any other artifacts which were built by some other method would need to be installed manually. %mvn_build macro does not even need to be used at all. Luckily, all artifacts created outside of %mvn_build can be marked for installation with %mvn_artifact macro. This macro creates configuration for %mvn_install.

Solicitar instalación de artefacto Maven
%prep
...
# Solicita instalación de archivos POM y JAR
%mvn_artifact subpackage/pom.xml target/artifact.jar
# Solicita instalación de artefacto POM (ningún archivo JAR)
%mvn_artifact pom.xml
# Solicita instalación para archivo JAR especificado por las coordinadas del artefacto
%mvn_artifact webapp:webapp:war:3.1 webapp.war

7.2.2. Asignaciones Adicionales

The macro %mvn_alias can be used to add additional mappings for given POM / JAR file. For example, if the POM file indicates that it contains groupId commons-lang, artifactId commons-lang, this macro ensures that we also add a mapping between groupId org.apache.commons and the installed JAR / POM files. This is necessary in cases where the groupId or artifactId may have changed, and other packages might require different IDs than those reflected in the installed POM.

Añadiendo más asignaciones para archivos de ejemplo para JAR/POM
%prep
...
%mvn_alias "commons-lang:commons-lang" "org.apache.commons:commons-lang"

7.2.3. Nombres de Archivo JAR Alternativo

In some cases, it may be important to be able to provide symbolic links to actual JAR files. This can be achieved with %mvn_file macro. This macro allows packager to specify names of the JAR files, their location in %{_javadir} directory and also can create symbolic links to the JAR files. These links can be possibly located outside of the %{_javadir} directory.

Añadiendo enlaces simbólicos de archivo para compatibilidad
%prep
...
%mvn_file :guice google/guice guice

This means that JAR file for artifact with ID "guice" (and any groupId) will be installed in %{_javadir}/google/guice.jar and there also will be a symbolic links to this JAR file located in %{_javadir}/guice.jar. Note the macro will add .jar extensions automatically.

7.2.4. Único Artefacto Por Paquete

If the project consists of multiple artifacts, it is recommended to install each artifact to the separate subpackage. The macro %mvn_build -s will generate separate .mfiles file for every artifact in the project. This file contains list of files related to specific artifact (typically JAR file, POM file and metadata). It can be later used in %files section of the spec file.

Creando un subpaquete para cada artefacto generado
...
%description
Las Herramientas de Complemento Maven contiene...

%package -n maven-plugin-annotations
Sumario:        Anotaciones de complemento Maven en Java 5

%description -n maven-plugin-annotations
Este paquete contiene anotaciones de Java 5 para utilizar en Mojos.

%package -n maven-plugin-plugin
Sumario:        Complemento Maven

%description -n maven-plugin-plugin
El Plugin Plugin se utiliza en…
…

%build
%mvn_build -s

%install
%mvn_install

%files -f .mfiles-maven-plugin-tools
%doc LICENSE NOTICE
%files -n maven-plugin-annotations -f .mfiles-maven-plugin-annotations
%files -n maven-plugin-plugin      -f .mfiles-maven-plugin-plugin
%files -f .mfiles-javadoc
...

7.2.5. Asignación de los Artefactos Maven para los Subpaquetes

The macro %mvn_package allows maintainer to specify in which exact package the selected artifact will end up. It is something between singleton packaging, when each artifact has its own subpackage and default packaging, when all artifacts end up in the same package.

Asignar múltiples artefactos para subpaquete único
...
%prep
%mvn_package ":plexus-compiler-jikes"   plexus-compiler-extras
%mvn_package ":plexus-compiler-eclipse" plexus-compiler-extras
%mvn_package ":plexus-compiler-csharp"  plexus-compiler-extras

%build
%mvn_build

%install
%mvn_install

%files -f .mfiles
%files -f .mfiles-plexus-compiler-extras
%files -f .mfiles-javadoc

In above example, the artifacts plexus-compiler-jikes, plexus-compiler-eclipse, plexus-compiler-csharp will end up in package named plexus-compiler-extras. If there are some other artifacts beside these three mentioned (e.g. some parent POMs), then these will all end up in package named %{name}.

%mvn_package macro supports wildcards and brace expansions, so whole %prep section from previous example can be replaced with single line: %mvn_package ":plexus-compiler-{jikes,eclipse,csharp}" plexus-compiler-extras.

It is possible to assign artifacts into a package called __noinstall. This package name has a special meaning. And as you can guess, artifacts assigned into this package will not be installed anywhere and the package itself will not be created.

Omitir instalación de un artefacto
%prep
...
%mvn_package groupId:artifactId __noinstall

7.2.6. Modificar configuración de XMvn dentro del archivo spec

Some packages might need to modify XMvn’s configuration in order to build successfully or from other reasons. This can be achieved with mvn_config macro. For example, some old package can use enum as an identifier, but it is also keyword since Java 1.5. Such package will probably fail to build on current systems. This problem can be easily solved by passing -source 1.4 to the compiler, so one could add following line to the spec file:

Sobrecargar configuración por defecto de XMvn
%prep
...
%mvn_config buildSettings/compilerSource 1.4

XMvn’s configuration is quite complex, but well documented at the project’s official website. The website should always be used as a primary source of information about XMvn configuration.

Read about XMvn’s configuration basics and see the full configuration reference.

All %mvn_ macros have their own manual page which contains details on how to use them. All possible options should be documented there. These manual pages should be considered most up to date documentation right after source code. Try for example man mvn_file. These pages are also included in the Appendix.

7.3. Macros para modificación POM

Sometimes Maven pom.xml files need to be patched before they are used to build packages. One could use traditional patches to maintain changes, but package maintainers should use %pom_* macros developed specially to ease this task. Using %pom_* macros not only increases readability of the spec file, but also improves maintainability of the package as there are no patches that would need to be rebased with each upstream release.

Hay dos categorías de macros:

  • POM-specific macros - used to manipulate dependencies, modules, etc. Some of them also work on ivy.xml files.

  • Generic XML manipulation macros - used to add / remove / replace XML nodes.

The macros are designed to be called from %prep section of spec files. All the macros also have their own manual page. This document provides an overview of how they are used. For the technical details, refer to their respective manpages.

Especificación de archivo

By default, a macro acts on a pom.xml file (or ivy.xml file) in the current directory. Different path can be explicitly specified via an argument (the last one, unless stated otherwise). Multiple paths can be specified as multiple arguments. If a path is a directory, it looks for a pom.xml file in that directory. For example:

# Las siguientes funciona sobre archivo pom.xml en el directorio actual
%pom_remove_parent

# Los siguiente funciona en submodule/pom.xml
%pom_remove_parent submodule

# Lo siguiente también funciona en submodule/pom.xml
%pom_remove_parent submodule/pom.xml

# Los siguiente funciona en submodule2/pom.xml y submodule2/pom.xml
%pom_remove_parent submodule1 submodule2
Modo recursivo

Most macros also support recursive mode, where the change is applied to the pom.xml and all its modules recursively. This can be used, for example, to remove a dependency from the whole project. It is activated by -r switch.

7.3.1. Macros de manipulación de dependencia

Retirando dependencias

Often dependencies specified in Maven pom.xml files need to be removed because of different reasons. %pom_remove_dep macro can be used to ease this task:

# Elimina la dependencia con groupId "foo" y artifactId "bar" desde pom.xml
%pom_remove_dep foo:bar

# Elimina la dependencia de todos los artefactos con groupId "foo" desde pom.xml
%pom_remove_dep foo:

# Elimina la dependencia de todos los artefactos con artifactId "bar" de pom.xml
%pom_remove_dep :bar

# Elimina la dependencia de todos los artefactos con artifactId "bar" de submodule1/pom.xml
%pom_remove_dep :bar submodule1

# Retira dependencia en todos los artefactos con artifactId "bar" desde pom.xml
# y todos sus submódulos
%pom_remove_dep -r :bar

# Retira todas las dependencias de pom.xml
%pom_remove_dep :
Añadiendo dependencias

Dependencies can also be added to pom.xml with %pom_add_dep macro. Usage is very similar to %pom_remove_dep, see $ man pom_add_dep for more information.

Cambiar dependencias

Sometimes the artifact coordinates used in upstream pom.xml do not correspond to ones used in Fedora and you need to modify them. %pom_change_dep macro will modify all dependencies matching the first argument to artifact coordinates specified by the second argument. Note this macro also works in recursive mode.

# Para todos los artefactos en pom.xml que tengan groupId 'ejemplo' cambiarlo a
# 'com.example' mientras dejando artifactId y otras partes intactas
%pom_change_dep example: com.example:

7.3.2. Adding / removing plugins

%pom_remove_plugin macro works exactly as %pom_remove_dep, except it removes Maven plugin invocations. Some examples:

Retirar complementos Maven de archivos pom.xml
# Inhabilita maven-jar-plugin tal que classpath no está incluido en manifests
%pom_remove_plugin :maven-jar-plugin

# Inutilizar un complemento propietario que no está empaquetado para Fedora
%pom_remove_plugin com.example.mammon:useless-proprietary-plugin submodule

Like in previous case, there is also a macro for adding plugins to pom.xml. See its manual page for more information.

7.3.3. Inhabilita módulos innecesarios

Sometimes some submodules of upstream project cannot be built for various reasons and there is a need to disable them. This can be achieved by using %pom_disable_module, for example:

Inhabilitando módulos de proyecto específico
# Deshabilita child-module-1, un submódulo del archivo pom.xml principal
%pom_disable_module child-module-1

# Deshabilita grandchild-module, un submódulo de child-module-2/pom.xml
%pom_disable_module grandchild-module child-module-2

7.3.4. Trabajar con referencias POM antecesoras

Macro %pom_remove_parent removes reference to a parent POM from Maven POM files. This can be useful when parent POM is not yet packaged (e.g. because of licensing issues) and at the same time it is not really needed for building of the project. There are also macros for adding parent POM reference (%pom_add_parent) and replacing existing reference with new one (%pom_set_parent).

Manipular referencias POM antecesoras
# Retira referencia a un antecesor POM desde ./pom.xml
%pom_remove_parent

# Retira referencia a un POM antecesor desde ./submodule/pom.xml
%pom_remove_parent submodule

# Añade referencia antecesora de POM a ./pom.xml
%pom_add_parent groupId:artifactId

# Sustituye referencia antecesora existente POM en ./pom.xml
%pom_set_parent groupId:artifactId:version

7.3.5. Macros para realizar modificaciones genéricas

El por encima de macros portada los casos más comunes de modificar archivos pom.xml, aun así si hay una necesidad de aplicar algunos parches menos comunes allí también son tres macros genéricas para modificar el archivo pom.xml. Estas macros genéricas también pueden ser aplicadas a otros archivos #XML, como los archivos de build.xml de Ant.

They all take a XPath 1.0 expression that selects XML nodes to be acted on (removed, replaced, etc.).

Manipular espacio de nombre XML

POM files use a specific namespace - http://maven.apache.org/POM/4.0.0. The easiest way to respect this namespace in XPath expressions is prefixing all node names with pom:. For example, pom:environment/pom:os will work because it selects nodes from pom namespace, but environment/os won’t find anything because it looks for nodes that do not belong to any XML namespace. It is needed even if the original POM file didn’t contain proper POM namespace, since it will be added automatically. Note that this requirement is due to limitation of XPath 1.0 and we cannot work it around.

Retirar nodos

%pom_xpath_remove puede ser utilizado para retirar nodos arbitrarios de XML.

# Retira extensiones desde la creación
%pom_xpath_remove "pom:build/pom:extensions" module/pom.xml
Inyectar nodos

%pom_xpath_inject macro is capable of injecting arbitrary XML code to any pom.xml file. The injected code is the last argument - optional file paths go before it (unlike most other macros). To pass a multiline snippet, quote the argument as in the following example.

# Add additional exclusion into maven-wagon dependency
%pom_xpath_inject "pom:dependency[pom:artifactId='maven-wagon']/pom:exclusions" "
<exclusion>
  <groupId>antlr</groupId>
  <artifactId>antlr</artifactId>
</exclusion>"
# The same thing, but with explicit file path
%pom_xpath_inject "pom:dependency[pom:artifactId='maven-wagon']/pom:exclusions" pom.xml "
<exclusion>
  <groupId>antlr</groupId>
  <artifactId>antlr</artifactId>
</exclusion>"
Cambiar el contenido de los nodos

%pom_xpath_set sustituye el contenido de los nodos XML arbitrarios con valores especificados (puede contener nodos XML).

# Cambia groupid de un antecesor
%pom_xpath_set "pom:parent/pom:groupId" "org.apache"
Sustitución de nodos

%pom_xpath_replace sustituye un nodo XML con código específico en XML.

# Cambia groupId de un antecesor (note la diferencia desde %pom_xpath_set)
%pom_xpath_replace "pom:parent/pom:groupId" "<groupId>org.apache</groupId>"

8. Errores Comunes

Esta sección contiene explicaciones y soluciones/métodos alternativos para los errores comunes los cuales pueden ser encontrados durante el empaquetado.

8.1. Ausencia de dependencia

[ERROR] Failed to execute goal on project simplemaven:
Could not resolve dependencies for project com.example:simplemaven:jar:1.0:
The following artifacts could not be resolved: commons-io:commons-io:jar:2.4, junit:junit:jar:4.11:
Cannot access central (http://repo.maven.apache.org/maven2) in offline mode and the artifact commons-io:commons-io:jar:2.4 has not been downloaded from it before. -> [Help 1]

Maven wasn’t able to build project com.example:simplemaven because it couldn’t find some dependencies (in this case commons-io:commons-io:jar:2.4 and junit:junit:jar:4.11)

Aquí tienes múltiples opciones:

  • If you suspect that a dependency is not necessary, you can remove it from pom.xml file and Maven will stop complaining about it. You can use wide variety of macros for modifying POM files. The one for removing dependencies is called %pom_remove_dep.

  • There is a mock plugin that can automate installation of missing dependencies. When you’re using mock, pass additional --enable-plugin pm_request argument and the build process would be able to install missing dependencies by itself. You still need to add the BuildRequires later, because you need to build the package in Koji, where the plugin is not allowed. You should do so using xmvn-builddep build.log, where build.log is the path to mock’s build log. It will print a list of BuildRequires lines, which you can directly paste into the specfile. To verify that the BuildRequires you just added are correct, you can rebuild the package once more without the plugin enabled.

  • Add the artifacts to BuildRequires manually. Maven packages have virtual provides in a format mvn(artifact coordinates), where artifact coordinates are in the format which Maven used in the error message, but without version for non-compat packages (most of the packages you encounter). Virtual provides can be used directly in BuildRequires, so in this case it would be:

BuildRequires:  mvn(commons-io:commons-io)
BuildRequires:  mvn(junit:junit)

8.2. Compilación incorrecta

[ERROR] Failed to execute goal
        org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile)
        on project simplemaven: Compilation failure: Compilation failure:
[ERROR] /builddir/build/BUILD/simplemaven-1.0/src/main/java/com/example/Main.java:[3,29] package org.apache.commons.io does not exist
[ERROR] /builddir/build/BUILD/simplemaven-1.0/src/main/java/com/example/Main.java:[8,9] cannot find symbol
[ERROR] symbol:   class FileUtils
[ERROR] location: class com.example.Main
[ERROR] -> [Help 1]

Java compiler couldn’t find given class on classpath or incompatible version was present. This could be caused by following reasons:

  • pom.xml requires different version of the Maven artifact than the local repository provides

  • pom.xml is missing a necessary dependency

Different versions of same library may provide slightly different API. This means that project doesn’t have to be buildable if different version is provided. If the library in local repository is older than the one required by project, then the library could be updated. If the project requires older version, then the project should be ported to latest stable version of the library (this may require cooperation with project’s upstream). If none of these is possible from some reason, it is still possible to introduce new compat package. See compat packages section for more information on this topic.

Sometimes pom.xml doesn’t list all the necessary dependencies, even if it should. Dependencies can also depend on some other and typically all these will be available to the project which is being built. The problem is that local repository may contain different versions of these dependencies. And even if these versions are fully compatible with the project, they may require slightly different set of dependencies. This could lead to build failure if pom.xml doesn’t specify all necessary dependencies and relies on transitive dependencies. Such a missing dependency may be considered a bug in the project. The solution is to explicitly add missing dependency to the pom.xml. This may be easily done by using %pom_add_dep macro. See the section about macros for POM modification for more information.

8.3. No se pueden generar requisitos

Las siguientes dependencias no se han resuelto y no se pueden generar los requisitos.
Elimine la dependencia de pom.xml o añada los paquetes adecuados a
BuildRequires: org.apache.maven.doxia:doxia-core::tests:UNKNOWN

Most often this error happens when one part of the package depends on an attached artifact which is not being installed. Automatic RPM requires generator then tries to generate requires on artifact which is not being installed. This would most likely result in a broken RPM package so generator halts the build.

Suele haber dos soluciones posibles para este problema:

  • Instala el artefacto adjunto en cuestión. Para el error anterior, la siguiente macro instalaría artefactos con clasificadores tests en el subpaquete tests.

%mvn_package :::tests: %{name}-tests
  • Eliminar la dependencia del artefacto problemático. Esto puede implicar modificaciones en pom.xml, deshabilitar pruebas o incluso cambios en el código, por lo que suele ser más fácil instalar la dependencia.

8.4. Dependencies with scope system

[ERROR] Failed to execute goal org.fedoraproject.xmvn:xmvn-mojo:1.2.0:install (default-cli) on project pom: Some reactor artifacts have dependencies with scope "system".
Such dependencies are not supported by XMvn installer.
You should either remove any dependencies with scope "system" before the build or not run XMvn instaler. -> [Help 1]

Some Maven artifacts try to depend on exact system paths. Most usually this dependency is either on com.sun:tools or sun.jdk:jconsole. Dependencies with system scope cause issues with our tooling and requires generators so they are not supported.

La forma más fácil de resolver esto para las dos dependencias anteriores es eliminando y volviendo a añadir la dependencia sin nodos <scope> o <systemPath>:

%pom_remove_dep com.sun:tools
%pom_add_dep com.sun:tools

9. Migración desde herramientas antiguas

Esta sección describe como migrar paquetes que el uso más antiguo de herramientas obsoletas a unos actuales.

9.1. %add_maven_depmap macro

%add_maven_depmap macro was used to manually install Maven artifacts that were built with Apache Ant or mvn-rpmbuild. It is now deprecated and its invocations should be replaced with %mvn_artifact and %mvn_install.

Artifact files, Maven POM files and their installation directories no longer need to be manually installed, since that is done during run of %mvn_install. The installed files also don’t need to be explicitly enumerated in %files section. Generated file .mfiles should be used instead.

Relevant parts of specfile using %add_maven_depmap:

BuildRequires:  javapackages-tools

Requiere:       alguna-biblioteca
...

%build
ant test

%install
install -d -m 755 $RPM_BUILD_ROOT%{_javadir}
install -m 644 target/%{name}.jar $RPM_BUILD_ROOT%{_javadir}/%{name}.jar

install -d -m 755 $RPM_BUILD_ROOT%{_mavenpomdir}
install -m 644 %{name}.pom $RPM_BUILD_ROOT/%{_mavenpomdir}/JPP-%{name}.pom

# Tenga en cuenta que la siguiente llamada es equivalente a invocar
# la macro sin ningún parámetro
%add_maven_depmap JPP-%{name}.pom %{name}.jar

# javadoc
install -d -m 755 $RPM_BUILD_ROOT%{_javadocdir}/%{name}
cp -pr api/* $RPM_BUILD_ROOT%{_javadocdir}/%{name}

%files
%{_javadir}/*
%{_mavenpomdir}/*
%{_mavendepmapfragdir}/*

%files javadoc
%doc %{_javadocdir}/%{name}

The same specfile migrated to %mvn_artifact and %mvn_install:

# mvn_* macros están localizadas en el paquete javapackages-local
BuildRequires:  javapackages-local

# Dado que XMvn genera automáticamente las etiquetas «Requires», ya no es necesario
# ni recomendable especificar etiquetas «Requires» manualmente, a menos que la información
# sobre dependencias del POM esté incompleta o se necesite depender de paquetes que no sean de Java
#
...

%prep
# El lugar por defecto para instalar archivos de JAR es %{_javadir}/%{name}/
# Porque nuestro original specfile pone el JAR directamente en %{_javadir},
# queremos invalidar este comportamiento. La siguiente invocación dice a
# XMvn que instale el artefacto groupId:artifactId como %{_javadir}/%{name}.jar
%mvn_file groupId:artifactId %{name}

%build
ant test

# Indica a XMvn a cual archivo POM pertenece cada artefacto.
%mvn_artifact %{name}.pom target/%{name}.jar

%install
# No es necesario instalar directorios y artefactos manualmente,
# mvn_install se encargaré de ello

# Opcionalmente, utilice el parámetro -J para especificar la ruta al directorio con la compilación
# javadoc
%mvn_install -J api

# Utilizar el archivo .mfiles generado automáticamente en lugar de especificar archivos individuales
%files -f .mfiles
%files javadoc -f .mfiles-javadoc
Alias y subpaquetes

%add_maven_depmap had -a switch to specify artifact aliases and -f switch to support splitting artifacts across multiple subpackages. To achieve the same things with %mvn_* macros, see [_additional_mappings] and [_assignment_of_the_maven_artifacts_to_the_subpackages].

If the project consists of multiple artifacts and parent POMs are among them, call %mvn_artifact on these parent POMs first.

Unresolved directive in sections.adoc - include::{EXAMPLE}manpages.adoc[]