COMO Empaquetar Java
- Autores y contribuciones
- Abstracción
- 1. Introducción
- 2. Específicos de Java en Fedora para Usuarios y Desarrolladores
- 3. Especificaciones de Java en Empaquetado de Fedora
- 4. Mejores Prácticas de Empaquetado
- 5. Construcciones de Java Genéricos
- 6. Ant
- 7. Maven
- 8. Errores Comunes
- 9. Migración desde herramientas antiguas
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:
-
Ask for a name.
-
Print out
Hello World fromand the name from previous step.
Para ilustrar ciertos puntos de cosas artificialmente complicadas por creación:
-
Inputclass used only for input of text from terminal. -
Outputclass used only for output on terminal. -
HelloWorldAppclass used as main application.
$ 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/.
Unresolved directive in introduction_for_packagers.adoc - include::{EXAMPLE}java_project/src/org/fedoraproject/helloworld/HelloWorld.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.
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.
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.MFfile. -
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.
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.
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. |
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.
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.
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.
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.
-
¿Qué es la diferencia entre JVM y Java?
-
What is a
CLASSPATHenvironment variable and how can you use it? -
Nombra dos sistemas de compilación típicos de Java y cómo puedes identificar cuál se está utilizando
-
¿Cuál es la diferencia entre los comandos
javayjavac? -
¿Qué son los contenidos de un archivo
JARtípico? -
What is a
pom.xmlfile and what information it contains? -
¿Cómo gestionarías el empaquetado de un software que contiene
lib/junit4.jardentro del archivo tarball de origen? -
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.
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.
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
Summarywith basic one-line summary of package contents. -
Package source URLs denoted with
Source0toSourceNdirectives.-
Source files can then be referenced by
%SOURCE0to%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
Patch0toPatchN. -
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.
-
%prepsection-
-
Unpacks the sources using
setup -qor 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, whereXis the number of patch you want to apply. (You usually need the patch index, so it would be:%patch 0 -p1).
-
%buildsection-
-
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
%buildsection, so if there are no integration tests available, this section is omitted.
-
%installsection-
-
Copies built files that are supposed to be installed into
%{buildroot}directory, which represents target filesystem’s root.
-
%filessection-
-
Enumera todos los archivos que deben instalarse desde
%{buildroot}en el sistema de destino. -
Documentation files are prefixed by
%docand 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-bumpspecfrom 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".
$ 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
-
¿Cómo crearías un RPM binario si te dieran un RPM de código fuente?
-
What is most common content of
Source0specfile tag? -
¿Cuál es la diferencia entre las etiquetas
VersionyRelease? -
¿Cómo se aplica un parche en RPM?
-
¿En qué parte del sistema de archivos deben guardarse los archivos JAR?
-
¿Cuál es el formato del registro de cambios de RPM o qué herramienta utilizarías para generarlo?
-
How would you install an application that needs certain layout (think
ANT_HOME) while honoring distribution filesystem layout guidelines? -
How would you generate script for running a application with main class
org.project.MainClasswhich depends oncommons-langjar?
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:
-
javaces un compilador de Java que traduce los archivos fuente a código de bytes de Java, que posteriormente puede ser interpretado por la JVM. -
jdbes un depurador de línea de comandos sencillo para aplicaciones Java. -
javadoces una herramienta para generar documentación Javadoc. -
javappuede 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 |
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.
%{_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.xmllos archivos utilizaron por Apache Ivy está instalada. Cada XML tiene que tener nombre que estrictamente corresponde a archivo de JAR en%{_javadir}o%{_jnidir}.
%{_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
javacontienen el Kit de Desarrollo de Java (JDK), mientras que los directorios cuyos nombres comienzan conjrecontienen 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.jpackageen/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.
# 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.
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
BuildRequireson the compat package as the virtual provides is also different (see below).
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.
%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.
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.
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.
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.
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
|
See also Querying Fedora repositories |
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 |
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-headlessproporciona 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 enjava-headlessson 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 requerirjavasi 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 requerirjava-headlessojava. Algunos paquetes no relacionados estrictamente con el desarrollo java necesitan acceso a bibliotecas incluidas con JDK, pero no con JRE (por ejemplotools.jar). Eso es uno de lo pocos casos donde puede ser necesario requerirjava-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 |
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.
5.2. Replacing JARs with symlinks using xmvn-subst
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 |
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.
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
-
%buildsection usesantcommand to build the project and run the tests. The used target(s) may vary depending on thebuild.xmlfile. You can useant -pcommand to list the project info or manually look for<target>nodes in thebuild.xmlfile. -
%mvn_artifactmacro 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_installperforms the actual installation. Optional-Jparameter requests installation of generated Javadoc from given directory. -
This method of artifact installation allows using other XMvn macros such as
%mvn_aliasor%mvn_package. -
%mvn_installgenerates.mfilesfile which should be used to populate%filessection with-fswitch. 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.
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
-
-Divy.mode=localtells Ivy to use XMvn local artifact resolution instead of downloading from the Internet. -
Si hay un
ivy-settings.xmlo archivo similar, el cual especifica repositorios remotos, necesita ser deshabilitado, de lo contrario, anularía la resolución local. -
%mvn_artifactsupports installing artifacts described by Ivy configuration files. -
%mvn_installperforms the actual installation. Optional-Jparameter requests installation of generated Javadoc from given directory.
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
ivy:publishIvy 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.
ivy:publishBuildRequires: 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
-
Es posible que el destino de publicación sea nombrado diferentemente. Busca la
build.xmlpara las apariciones deivy:publish. -
%mvn_installinstalará 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:
-
In
%buildsection of the spec file use%mvn_buildmacro. -
In
%installsection, use%mvn_installmacro. -
Use generated file
.mfileslists to populate%filessection with-fswitch.
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.
...
%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 Otro parámetro, |
|
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 |
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.
%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.
%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.
%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.
%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.
...
%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.
...
%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}.
|
|
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.
%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:
%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 |
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.xmlfiles. -
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.
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
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
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 :
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.
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:
# 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:
# 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).
# 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 - |
%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
%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>"
%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"
%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.xmlfile 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_requestargument and the build process would be able to install missing dependencies by itself. You still need to add theBuildRequireslater, because you need to build the package in Koji, where the plugin is not allowed. You should do so usingxmvn-builddep build.log, wherebuild.logis the path to mock’s build log. It will print a list ofBuildRequireslines, which you can directly paste into the specfile. To verify that theBuildRequiresyou just added are correct, you can rebuild the package once more without the plugin enabled. -
Add the artifacts to
BuildRequiresmanually. Maven packages have virtual provides in a formatmvn(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 inBuildRequires, 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.xmlrequires different version of the Maven artifact than the local repository provides -
pom.xmlis 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
testsen el subpaquetetests.
%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
%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 |
Unresolved directive in sections.adoc - include::{EXAMPLE}manpages.adoc[]
Want to help? Learn how to contribute to Fedora Docs ›