Packaging and distributing software
Packaging software by using the RPM package management system
Résumé
Rendre l'open source plus inclusif
Red Hat s'engage à remplacer les termes problématiques dans son code, sa documentation et ses propriétés Web. Nous commençons par ces quatre termes : master, slave, blacklist et whitelist. En raison de l'ampleur de cette entreprise, ces changements seront mis en œuvre progressivement au cours de plusieurs versions à venir. Pour plus de détails, voir le message de notre directeur technique Chris Wright.
Fournir un retour d'information sur la documentation de Red Hat
Nous apprécions vos commentaires sur notre documentation. Faites-nous savoir comment nous pouvons l'améliorer.
Soumettre des commentaires sur des passages spécifiques
- Consultez la documentation au format Multi-page HTML et assurez-vous que le bouton Feedback apparaît dans le coin supérieur droit après le chargement complet de la page.
- Utilisez votre curseur pour mettre en évidence la partie du texte que vous souhaitez commenter.
- Cliquez sur le bouton Add Feedback qui apparaît près du texte en surbrillance.
- Ajoutez vos commentaires et cliquez sur Submit.
Soumettre des commentaires via Bugzilla (compte requis)
- Connectez-vous au site Web de Bugzilla.
- Sélectionnez la version correcte dans le menu Version.
- Saisissez un titre descriptif dans le champ Summary.
- Saisissez votre suggestion d'amélioration dans le champ Description. Incluez des liens vers les parties pertinentes de la documentation.
- Cliquez sur Submit Bug.
Chapitre 1. Getting started with RPM packaging
The RPM Package Manager (RPM) is a package management system that runs on Red Hat Enterprise Linux, CentOS, and Fedora. You can use RPM to distribute, manage, and update software that you create for any of the operating systems mentioned above.
The RPM package management system brings several advantages over distribution of software in conventional archive files.
RPM enables you to:
- Install, reinstall, remove, upgrade and verify packages with standard package management tools, such as DNF or PackageKit.
- Use a database of installed packages to query and verify packages.
- Use metadata to describe packages, their installation instructions, and other package parameters.
- Package software sources, patches and complete build instructions into source and binary packages.
- Add packages to DNF repositories.
- Digitally sign your packages by using GNU Privacy Guard (GPG) signing keys.
Chapitre 2. Preparing software for RPM packaging
This section explains how to prepare software for RPM packaging. To do so, knowing how to code is not necessary. However, you need to understand the basic concepts, such as What is source code and How programs are made.
2.1. What is source code
This part explains what is source code and shows example source codes of a program written in three different programming languages.
Source code is human-readable instructions to the computer, which describe how to perform a computation. Source code is expressed using a programming language.
This document features three versions of the Hello World
program written in three different programming languages:
Each version is packaged differently.
These versions of the Hello World
program cover the three major use cases of an RPM packager.
Exemple 2.1. Hello World written in bash
The bello project implements Hello World
in bash. The implementation only contains the bello
shell script. The purpose of the program is to output Hello World
on the command line.
The bello
file has the following syntax:
#!/bin/bash printf "Hello World\n"
Exemple 2.2. Hello World written in Python
The pello project implements Hello World
in Python. The implementation only contains the pello.py
program. The purpose of the program is to output Hello World
on the command line.
The pello.py
file has the following syntax:
#!/usr/bin/python3 print("Hello World")
Exemple 2.3. Hello World written in C
The cello project implements Hello World
in C. The implementation only contains the cello.c
and the Makefile
files, so the resulting tar.gz
archive will have two files apart from the LICENSE
file.
The purpose of the program is to output Hello World
on the command line.
The cello.c
file has the following syntax:
#include <stdio.h> int main(void) { printf("Hello World\n"); return 0; }
2.2. How programs are made
Methods of conversion from human-readable source code to machine code (instructions that the computer follows to execute the program) include the following:
- The program is natively compiled.
- The program is interpreted by raw interpreting.
- The program is interpreted by byte compiling.
2.2.1. Natively Compiled Code
Natively compiled software is software written in a programming language that compiles to machine code with a resulting binary executable file. Such software can be run stand-alone.
RPM packages built this way are architecture-specific.
If you compile such software on a computer that uses a 64-bit (x86_64) AMD or Intel processor, it does not execute on a 32-bit (x86) AMD or Intel processor. The resulting package has architecture specified in its name.
2.2.2. Interpreted Code
Some programming languages, such as bash or Python, do not compile to machine code. Instead, their programs' source code is executed step by step, without prior transformations, by a Language Interpreter or a Language Virtual Machine.
Software written entirely in interpreted programming languages is not architecture-specific. Hence, the resulting RPM Package has the noarch
string in its name.
Interpreted languages are either Raw-interpreted programs or Byte-compiled programs. These two types differ in program build process and in packaging procedure.
2.2.2.1. Raw-interpreted programs
Raw-interpreted language programs do not need to be compiled and are directly executed by the interpreter.
2.2.2.2. Byte-compiled programs
Byte-compiled languages need to be compiled into byte code, which is then executed by the language virtual machine.
Some languages offer a choice: they can be raw-interpreted or byte-compiled.
2.3. Building software from source
For software written in compiled languages, the source code goes through a build process, producing machine code. This process, commonly called compiling or translating, varies for different languages. The resulting built software can be run, which makes the computer perform the task specified by the programmer.
For software written in raw interpreted languages, the source code is not built, but executed directly.
For software written in byte-compiled interpreted languages, the source code is compiled into byte code, which is then executed by the language virtual machine.
The following subchapters describe how to build software from source code.
2.4. Building software from Natively Compiled Code
This section shows how to build the cello.c
program written in the C language into an executable.
cello.c
#include <stdio.h> int main(void) { printf("Hello World\n"); return 0; }
2.4.1. Manual building
If you want to build the cello.c
program manually, use this procedure:
Procédure
Invoke the C compiler from the GNU Compiler Collection to compile the source code into binary:
gcc -g -o cello cello.c
Execute the resulting output binary
cello
:$ ./cello Hello World
2.4.2. Automated building
Large-scale software commonly uses automated building that is done by creating the Makefile
file and then running the GNU make
utility.
If you want to use the automated building to build the cello.c
program, use this procedure:
Procédure
To set up automated building, create the
Makefile
file with the following content in the same directory ascello.c
.Makefile
cello: gcc -g -o cello cello.c clean: rm cello
Note that the lines under
cello:
andclean:
must begin with a tab space.To build the software, run the
make
command:$ make make: 'cello' is up to date.
Since there is already a build available, run the
make clean
command, and after run themake
command again:$ make clean rm cello $ make gcc -g -o cello cello.c
NoteTrying to build the program after another build has no effect.
$ make make: 'cello' is up to date.
Execute the program:
$ ./cello Hello World
You have now compiled a program both manually and using a build tool.
2.5. Interpreting code
This section shows how to byte-compile a program written in Python and raw-interpret a program written in bash.
In the two examples below, the #!
line at the top of the file is known as a shebang, and is not part of the programming language source code.
The shebang enables using a text file as an executable: the system program loader parses the line containing the shebang to get a path to the binary executable, which is then used as the programming language interpreter. The functionality requires the text file to be marked as executable.
2.5.1. Byte-compiling code
This section shows how to compile the pello.py
program written in Python into byte code, which is then executed by the Python language virtual machine.
Python source code can also be raw-interpreted, but the byte-compiled version is faster. Hence, RPM Packagers prefer to package the byte-compiled version for distribution to end users.
pello.py
#!/usr/bin/python3 print("Hello World")
Procedure for byte-compiling programs varies depending on the following factors:
- Programming language
- Language’s virtual machine
- Tools and processes used with that language
Python is often byte-compiled, but not in the way described here. The following procedure aims not to conform to the community standards, but to be simple. For real-world Python guidelines, see Software Packaging and Distribution.
Use this procedure to compile pello.py
into byte code:
Procédure
Byte-compile the
pello.py
file:$ python -m compileall pello.py $ file pello.pyc pello.pyc: python 2.7 byte-compiled
Execute the byte code in
pello.pyc
:$ python pello.pyc Hello World
2.5.2. Raw-interpreting code
This section shows how to raw-interpret the bello
program written in the bash shell built-in language.
bello
#!/bin/bash printf "Hello World\n"
Programs written in shell scripting languages, like bash, are raw-interpreted.
Procédure
Make the file with source code executable and run it:
$ chmod +x bello $ ./bello Hello World
2.6. Patching software
In RPM packaging, instead of modifying the original source code, we keep it, and use patches on it.
A patch is a source code that updates other source code. It is formatted as a diff, because it represents what is different between two versions of the text. A diff is created using the diff
utility, which is then applied to the source code using the patch utility.
Software developers often use Version Control Systems such as git to manage their code base. Such tools provide their own methods of creating diffs or patching software.
This section explains how to patch the software.
The following example shows how to create a patch from the original source code using diff
, and how to apply the patch using patch
. Patching is used in a later section when creating an RPM.
This procedure shows how to create a patch from the original source code for cello.c
.
Procédure
Preserve the original source code:
$ cp -p cello.c cello.c.orig
The
-p
option is used to preserve mode, ownership, and timestamps.Modify
cello.c
as needed:#include <stdio.h> int main(void) { printf("Hello World from my very first patch!\n"); return 0; }
Generate a patch using the
diff
utility:$ diff -Naur cello.c.orig cello.c --- cello.c.orig 2016-05-26 17:21:30.478523360 -0500 + cello.c 2016-05-27 14:53:20.668588245 -0500 @@ -1,6 +1,6 @@ #include<stdio.h> int main(void){ - printf("Hello World!\n"); + printf("Hello World from my very first patch!\n"); return 0; } \ No newline at end of file
Lines starting with a
-
are removed from the original source code and replaced with the lines that start with+
.Using the
Naur
options with thediff
command is recommended because it fits the majority of usual use cases. However, in this particular case, only the-u
option is necessary. Particular options ensure the following:-
-N
(or--new-file
) - Handles absent files as if they were empty files. -
-a
(or--text
) - Treats all files as text. As a result, the files thatdiff
classifies as binaries are not ignored. -
-u
(or-U NUM
or--unified[=NUM]
) - Returns output in the form of output NUM (default 3) lines of unified context. This is an easily readable format that allows fuzzy matching when applying the patch to a changed source tree. -r
(or--recursive
) - Recursively compares any subdirectories that are found.For more information on common arguments for the
diff
utility, see thediff
manual page.
-
Save the patch to a file:
$ diff -Naur cello.c.orig cello.c > cello-output-first-patch.patch
Restore the original
cello.c
:$ cp cello.c.orig cello.c
The original
cello.c
must be retained, because when an RPM is built, the original file is used, not the modified one. For more information, see Working with SPEC files.
The following procedure shows how to patch cello.c
using cello-output-first-patch.patch
, build the patched program, and run it.
Procédure
Redirect the patch file to the
patch
command:$ patch < cello-output-first-patch.patch patching file cello.c
Check that the contents of
cello.c
now reflect the patch:$ cat cello.c #include<stdio.h> int main(void){ printf("Hello World from my very first patch!\n"); return 1; }
Build and run the patched
cello.c
:$ make clean rm cello $ make gcc -g -o cello cello.c $ ./cello Hello World from my very first patch!
2.7. Arbitrary artifacts
Unix-like systems use the Filesystem Hierarchy Standard (FHS) to specify a directory suitable for a particular file.
Files installed from the RPM packages are placed according to FHS. For example, an executable file should go into a directory that is in the system $PATH
variable.
In the context of this documentation, an Arbitrary Artifact is anything installed from an RPM to the system. For RPM and for the system it can be a script, a binary compiled from the package’s source code, a pre-compiled binary, or any other file.
The following sections describe two common ways of placing Arbitrary Artifacts in the system:
2.8. Placing arbitrary artifacts in the system using the install command
Packagers often use the install
command in cases when build automation tooling such as GNU make is not optimal; for example if the packaged program does not need extra overhead.
The install
command is provided to the system by coreutils, which places the artifact to the specified directory in the file system with a specified set of permissions.
The following procedure uses the bello
file that was previously created as the arbitrary artifact as a subject to this installation method.
Procédure
Run the
install
command to place thebello
file into the/usr/bin
directory with permissions common for executable scripts:$ sudo install -m 0755 bello /usr/bin/bello
As a result,
bello
is now located in the directory that is listed in the$PATH
variable.Execute
bello
from any directory without specifying its full path:$ cd ~ $ bello Hello World
2.9. Placing arbitrary artifacts in the system using the make install command
Using the make install
command is an automated way to install built software to the system. In this case, you need to specify how to install the arbitrary artifacts to the system in the Makefile
that is usually written by the developer.
This procedure shows how to install a build artifact into a chosen location on the system.
Procédure
Add the
install
section to theMakefile
:Makefile
cello: gcc -g -o cello cello.c clean: rm cello install: mkdir -p $(DESTDIR)/usr/bin install -m 0755 cello $(DESTDIR)/usr/bin/cello
Note that the lines under
cello:
,clean:
, andinstall:
must begin with a tab space.NoteThe $(DESTDIR) variable is a GNU make built-in and is commonly used to specify installation to a directory different than the root directory.
Now you can use
Makefile
not only to build software, but also to install it to the target system.Build and install the
cello.c
program:$ make gcc -g -o cello cello.c $ sudo make install install -m 0755 cello /usr/bin/cello
As a result,
cello
is now located in the directory that is listed in the$PATH
variable.Execute
cello
from any directory without specifying its full path:$ cd ~ $ cello Hello World
2.10. Preparing source code for packaging
Developers often distribute software as compressed archives of source code, which are then used to create packages. RPM packagers work with a ready source code archive.
Software should be distributed with a software license.
This procedure uses the GPLv3 license text as an example content of the LICENSE
file.
Procédure
Create a
LICENSE
file, and make sure that it includes the following content:$ cat /tmp/LICENSE This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.
Ressources supplémentaires
2.11. Putting source code into tarball
This section describes how to put each of the three Hello World
programs introduced in section "What is source code" into a gzip-compressed tarball, which is a common way to release the software to be later packaged for distribution.
Exemple 2.4. Putting the bello project into tarball
The bello project implements Hello World
in bash. The implementation only contains the bello
shell script, so the resulting tar.gz
archive will have only one file apart from the LICENSE
file.
This procedure shows how to prepare the bello project for distribution.
Conditions préalables
Considering that this is version 0.1
of the program.
Procédure
Put all required files into a single directory:
$ mkdir /tmp/bello-0.1 $ mv ~/bello /tmp/bello-0.1/ $ cp /tmp/LICENSE /tmp/bello-0.1/
Create the archive for distribution and move it to the
~/rpmbuild/SOURCES/
directory, which is the default directory where therpmbuild
command stores the files for building packages:$ cd /tmp/ $ tar -cvzf bello-0.1.tar.gz bello-0.1 bello-0.1/ bello-0.1/LICENSE bello-0.1/bello $ mv /tmp/bello-0.1.tar.gz ~/rpmbuild/SOURCES/
For more information about the example source code written in bash, see Hello World written in bash.
Exemple 2.5. Putting the pello project into tarball
The pello project implements Hello World
in Python. The implementation only contains the pello.py
program, so the resulting tar.gz
archive will have only one file apart from the LICENSE
file.
This procedure shows how to prepare the pello project for distribution.
Conditions préalables
Considering that this is version 0.1.1
of the program.
Procédure
Put all required files into a single directory:
$ mkdir /tmp/pello-0.1.2 $ mv ~/pello.py /tmp/pello-0.1.2/ $ cp /tmp/LICENSE /tmp/pello-0.1.2/
Create the archive for distribution and move it to the
~/rpmbuild/SOURCES/
directory, which is the default directory where therpmbuild
command stores the files for building packages:$ cd /tmp/ $ tar -cvzf pello-0.1.2.tar.gz pello-0.1.2 pello-0.1.2/ pello-0.1.2/LICENSE pello-0.1.2/pello.py $ mv /tmp/pello-0.1.2.tar.gz ~/rpmbuild/SOURCES/
For more information about the example source code written in Python, see Hello World written in Python.
Exemple 2.6. Putting the cello project into tarball
The cello project implements Hello World
in C. The implementation only contains the cello.c
and the Makefile
files, so the resulting tar.gz
archive will have two files apart from the LICENSE
file.
Note that the patch
file is not distributed in the archive with the program. The RPM Packager applies the patch when the RPM is built. The patch will be placed into the ~/rpmbuild/SOURCES/
directory alongside the .tar.gz
archive.
This procedure shows how to prepare the cello project for distribution.
Conditions préalables
Considering that this is version 1.0
of the program.
Procédure
Put all required files into a single directory:
$ mkdir /tmp/cello-1.0 $ mv ~/cello.c /tmp/cello-1.0/ $ mv ~/Makefile /tmp/cello-1.0/ $ cp /tmp/LICENSE /tmp/cello-1.0/
Create the archive for distribution and move it to the
~/rpmbuild/SOURCES/
directory, which is the default directory where therpmbuild
command stores the files for building packages:$ cd /tmp/ $ tar -cvzf cello-1.0.tar.gz cello-1.0 cello-1.0/ cello-1.0/Makefile cello-1.0/cello.c cello-1.0/LICENSE $ mv /tmp/cello-1.0.tar.gz ~/rpmbuild/SOURCES/
Add the patch:
$ mv ~/cello-output-first-patch.patch ~/rpmbuild/SOURCES/
For more information about the example source code written in C, see Hello World written in C.
Chapitre 3. Packaging software
This section explains the basics of RPM packaging.
3.1. Paquets RPM
Un paquetage RPM est un fichier contenant d'autres fichiers et leurs métadonnées (informations sur les fichiers nécessaires au système).
Plus précisément, un paquetage RPM se compose de l'archive cpio
.
L'archive cpio
contient :
- Dossiers
En-tête RPM (métadonnées du paquet)
Le gestionnaire de paquets
rpm
utilise ces métadonnées pour déterminer les dépendances, l'endroit où installer les fichiers et d'autres informations.
Types de paquets RPM
Il existe deux types de paquets RPM. Les deux types partagent le format de fichier et l'outillage, mais ont des contenus différents et servent des objectifs différents :
Source RPM (SRPM)
Un SRPM contient le code source et un fichier SPEC, qui décrit comment construire le code source en un RPM binaire. En option, les correctifs du code source sont également inclus.
RPM binaire
Un RPM binaire contient les binaires construits à partir des sources et des correctifs.
3.2. Listing RPM packaging tool’s utilities
The following procedure describes how to list the utilities provided by the rpmdevtools
package.
Conditions préalables
You installed the
rpmdevtools
package, which provides several utilities for packaging RPMs:# dnf install rpmdevtools
Procédure
List RPM packaging tool’s utilities:
$ rpm -ql rpmdevtools | grep bin
For more information on the above utilities, see their manual pages or help dialogs.
3.3. Setting up RPM packaging workspace
This section describes how to set up a directory layout that is the RPM packaging workspace by using the rpmdev-setuptree
utility.
Conditions préalables
You installed the
rpmdevtools
package, which provides several utilities for packaging RPMs:# dnf install rpmdevtools
Procédure
Run the
rpmdev-setuptree
utility:$ rpmdev-setuptree $ tree ~/rpmbuild/ /home/user/rpmbuild/ |-- BUILD |-- RPMS |-- SOURCES |-- SPECS `-- SRPMS 5 directories, 0 files
The created directories serve the following purposes:
Annuaire | Objectif |
BUILD |
When packages are built, various |
RPMS |
Binary RPMs are created here, in subdirectories for different architectures, for example in subdirectories |
SOURCES |
Here, the packager puts compressed source code archives and patches. The |
SPECS | The packager puts SPEC files here. |
SRPMS |
When |
3.4. What is a SPEC file
You can understand a SPEC file as a recipe that the rpmbuild
utility uses to build an RPM. A SPEC file provides necessary information to the build system by defining instructions in a series of sections. The sections are defined in the Preamble and the Body part. The Preamble part contains a series of metadata items that are used in the Body part. The Body part represents the main part of the instructions.
The following sections describe each section of a SPEC file.
3.4.1. Preamble items
The table below presents some of the directives that are used frequently in the Preamble section of the RPM SPEC file.
SPEC Directive | Définition |
---|---|
| The base name of the package, which should match the SPEC file name. |
| The upstream version number of the software. |
|
The number of times this version of the software was released. Normally, set the initial value to 1%{?dist}, and increment it with each new release of the package. Reset to 1 when a new |
| A brief, one-line summary of the package. |
| The license of the software being packaged. |
| The full URL for more information about the program. Most often this is the upstream project website for the software being packaged. |
| Path or URL to the compressed archive of the upstream source code (unpatched, patches are handled elsewhere). This should point to an accessible and reliable storage of the archive, for example, the upstream page and not the packager’s local storage. If needed, more SourceX directives can be added, incrementing the number each time, for example: Source1, Source2, Source3, and so on. |
| The name of the first patch to apply to the source code if necessary. The directive can be applied in two ways: with or without numbers at the end of Patch. If no number is given, one is assigned to the entry internally. It is also possible to give the numbers explicitly using Patch0, Patch1, Patch2, Patch3, and so on. These patches can be applied one by one using the %patch0, %patch1, %patch2 macro and so on. The macros are applied within the %prep directive in the Body section of the RPM SPEC file. Alternatively, you can use the %autopatch macro which automatically applies all patches in the order they are given in the SPEC file. |
|
If the package is not architecture dependent, for example, if written entirely in an interpreted programming language, set this to |
|
A comma or whitespace-separated list of packages required for building the program written in a compiled language. There can be multiple entries of |
|
A comma- or whitespace-separated list of packages required by the software to run once installed. There can be multiple entries of |
| If a piece of software can not operate on a specific processor architecture, you can exclude that architecture here. |
|
|
|
This directive alters the way updates work depending on whether the |
|
If |
The Name
, Version
, and Release
directives comprise the file name of the RPM package. RPM package maintainers and system administrators often call these three directives N-V-R or NVR, because RPM package filenames have the NAME-VERSION-RELEASE
format.
The following example shows how to obtain the NVR information for a specific package by querying the rpm
command.
Exemple 3.1. Querying rpm to provide the NVR information for the bash package
# rpm -q bash bash-4.4.19-7.el8.x86_64
Here, bash
is the package name, 4.4.19
is the version, and 7.el8
is the release. The final marker is x86_64
, which signals the architecture. Unlike the NVR, the architecture marker is not under direct control of the RPM packager, but is defined by the rpmbuild
build environment. The exception to this is the architecture-independent noarch
package.
3.4.2. Body items
The items used in the Body section
of the RPM SPEC file are listed in the table below.
SPEC Directive | Définition |
---|---|
| A full description of the software packaged in the RPM. This description can span multiple lines and can be broken into paragraphs. |
|
Command or series of commands to prepare the software to be built, for example, unpacking the archive in |
| Command or series of commands for building the software into machine code (for compiled languages) or byte code (for some interpreted languages). |
|
Command or series of commands for copying the desired build artifacts from the for details. |
| Command or series of commands to test the software. This normally includes things such as unit tests. |
| The list of files that will be insstalled in the end user’s system. |
|
A record of changes that have happened to the package between different |
3.4.3. Advanced items
The SPEC file can also contain advanced items, such as Scriptlets
or Triggers.
They take effect at different points during the installation process on the end user’s system, not the build process.
3.5. BuildRoots
In the context of RPM packaging, buildroot
is a chroot environment. This means that the build artifacts are placed here using the same file system hierarchy as the future hierarchy in the end user’s system, with buildroot
acting as the root directory. The placement of build artifacts should comply with the file system hierarchy standard of the end user’s system.
The files in buildroot
are later put into a cpio
archive, which becomes the main part of the RPM. When RPM is installed on the end user’s system, these files are extracted in the root
directory, preserving the correct hierarchy.
Starting from Red Hat Enterprise Linux 6, the rpmbuild
program has its own defaults. Overriding these defaults leads to several problems; hence, Red Hat does not recommend to define your own value of this macro. You can use the %{buildroot}
macro with the defaults from the rpmbuild
directory.
3.6. RPM macros
An rpm macro is a straight text substitution that can be conditionally assigned based on the optional evaluation of a statement when certain built-in functionality is used. Hence, RPM can perform text substitutions for you.
An example use is referencing the packaged software Version multiple times in a SPEC file. You define Version only once in the %{version}
macro, and use this macro throughout the SPEC file. Every occurrence will be automatically substituted by Version that you defined previously.
If you see an unfamiliar macro, you can evaluate it with the following command:
$ rpm --eval %{_MACRO}
Evaluating the %{_bindir} and the %{_libexecdir} macros
$ rpm --eval %{_bindir} /usr/bin $ rpm --eval %{_libexecdir} /usr/libexec
One of the commonly-used macros is the %{?dist}
macro, which signals which distribution is used for the build (distribution tag).
# On a RHEL 9.x machine $ rpm --eval %{?dist} .el8
3.7. Working with SPEC files
To package new software, you need to create a new SPEC file.
There are two ways to achieve this:
- Writing the new SPEC file manually from scratch
Use the
rpmdev-newspec
utilityThis utility creates an unpopulated SPEC file, and you fill in the necessary directives and fields.
Some programmer-focused text editors pre-populate a new .spec
file with their own SPEC template. The rpmdev-newspec
utility provides an editor-agnostic method.
The following sections use the three example implementations of the Hello World!
program that were described in What is source code.
Each of the programs is also fully described in the table below.
Software Name | Explanation of example |
bello | A program written in a raw interpreted programming language. It demonstrates when the source code does not need to be built, but only needs to be installed. If a pre-compiled binary needs to be packaged, you can also use this method since the binary would also just be a file. |
pello | A program written in a byte-compiled interpreted programming language. It demonstrates byte-compiling the source code and installating the bytecode - the resulting pre-optimized files. |
cello | A program written in a natively compiled programming language. It demonstrates a common process of compiling the source code into machine code and installing the resulting executables. |
The implementations of Hello World!
are:
As a prerequisite, these implementations need to be placed into the ~/rpmbuild/SOURCES
directory.
3.8. Creating a new SPEC file with rpmdev-newspec
The following procedure shows how to create a SPEC file for each of the three aforementioned Hello World!
programs using the rpmdev-newspec
utility.
Procédure
Change to the
~/rpmbuild/SPECS
directory and use therpmdev-newspec
utility:$ cd ~/rpmbuild/SPECS $ rpmdev-newspec bello bello.spec created; type minimal, rpm version >= 4.11. $ rpmdev-newspec cello cello.spec created; type minimal, rpm version >= 4.11. $ rpmdev-newspec pello pello.spec created; type minimal, rpm version >= 4.11.
The
~/rpmbuild/SPECS/
directory now contains three SPEC files namedbello.spec
,cello.spec
, andpello.spec
.Examine the files:
The directives in the files represent those described in What is a SPEC file.
In the following sections, you will populate particular section in the output files of rpmdev-newspec
.
The rpmdev-newspec
utility does not use guidelines or conventions specific to any particular Linux distribution. However, this document targets Red Hat Enterprise Linux, so the %{buildroot}
notation is preferred over the $RPM_BUILD_ROOT
notation when referencing RPM’s Buildroot for consistency with all other defined or provided macros throughout the SPEC file.
3.9. Modifying an original SPEC file for creating RPMs
The following procedure shows how to modify the output SPEC file provided by rpmdev-newspec
for creating the RPMs.
Conditions préalables
-
The source code of the particular program has been placed into the
~/rpmbuild/SOURCES/
directory. -
The unpopulated SPEC file
~/rpmbuild/SPECS/<name>.spec
file has been created by therpmdev-newspec
utility.
Procédure
-
Open the output template of the
~/rpmbuild/SPECS/<name>.spec
file provided by therpmdev-newspec
utility: Populate the first section of the SPEC file:
The first section includes these directives that
rpmdev-newspec
grouped together:Name
-
The
Name
was already specified as an argument torpmdev-newspec
. Version
-
Set the
Version
to match the upstream release version of the source code. Release
-
The
Release
is automatically set to1%{?dist}
, which is initially1
. Increment the initial value whenever updating the package without a change in the upstream releaseVersion
- such as when including a patch. ResetRelease
to1
when a new upstream release happens. Summary
-
The
Summary
is a short, one-line explanation of what this software is.
Populate the
License
,URL
, andSource0
directives:The
License
field is the Software License associated with the source code from the upstream release. The exact format for how to label theLicense
in your SPEC file will vary depending on which specific RPM based Linux distribution guidelines you are following.For example, you can use GPLv3+.
The
URL
field provides URL to the upstream software website. For consistency, utilize the RPM macro variable of%{name}
, and usehttps://example.com/%{name}
.The
Source0
field provides URL to the upstream software source code. It should link directly to the specific version of software that is being packaged. Note that the example URLs given in this documentation include hard-coded values that are possible subject to change in the future. Similarly, the release version can change as well. To simplify these potential future changes, use the%{name}
and%{version}
macros. By using these, you need to update only one field in the SPEC file.Populate the
BuildRequires
,Requires
andBuildArch
directives:BuildRequires
specifies build-time dependencies for the package.Requires
specifies run-time dependencies for the package.This is a software written in an interpreted programming language with no natively compiled extensions. Hence, add the
BuildArch
directive with thenoarch
value. This tells RPM that this package does not need to be bound to the processor architecture on which it is built.Populate the
%description
,%prep
,%build
,%install
,%files
, and%license
directives:These directives can be thought of as section headings, because they are directives that can define multi-line, multi-instruction, or scripted tasks to occur.
The
%description
is a longer, fuller description of the software thanSummary
, containing one or more paragraphs.The
%prep
section specifies how to prepare the build environment. This usually involves expansion of compressed archives of the source code, application of patches, and, potentially, parsing of information provided in the source code for use in a later portion of the SPEC file. In this section you can use the built-in%setup -q
macro.The
%build
section specifies how to build the software.The
%install
section contains instructions forrpmbuild
on how to install the software, once it has been built, into theBUILDROOT
directory.This directory is an empty chroot base directory, which resembles the end user’s root directory. Here you can create any directories that will contain the installed files. To create such directories, you can use the RPM macros without having to hardcode the paths.
The
%files
section specifies the list of files provided by this RPM and their full path location on the end user’s system.Within this section, you can indicate the role of various files using built-in macros. This is useful for querying the package file manifest metadata using the
rpm
command. For example, to indicate that the LICENSE file is a software license file, use the%license
macro.The last section,
%changelog
, is a list of datestamped entries for each Version-Release of the package. They log packaging changes, not software changes. Examples of packaging changes: adding a patch, changing the build procedure in the%build
section.Follow this format for the first line:
Start with an
*
character followed byDay-of-Week Month Day Year Name Surname <email> - Version-Release
Follow this format for the actual change entry:
- Each change entry can contain multiple items, one for each change.
- Each item starts on a new line.
-
Each item begins with a
-
character.
You have now written an entire SPEC file for the required program.
3.10. An example SPEC file for a program written in bash
This section shows an example SPEC file for the bello program that was written in bash.
An example SPEC file for the bello program written in bash
Name: bello Version: 0.1 Release: 1%{?dist} Summary: Hello World example implemented in bash script License: GPLv3+ URL: https://www.example.com/%{name} Source0: https://www.example.com/%{name}/releases/%{name}-%{version}.tar.gz Requires: bash BuildArch: noarch %description The long-tail description for our Hello World Example implemented in bash script. %prep %setup -q %build %install mkdir -p %{buildroot}/%{_bindir} install -m 0755 %{name} %{buildroot}/%{_bindir}/%{name} %files %license LICENSE %{_bindir}/%{name} %changelog * Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org> - 0.1-1 - First bello package - Example second item in the changelog for version-release 0.1-1
The BuildRequires
directive, which specifies build-time dependencies for the package, was deleted because there is no building step for bello
. Bash is a raw interpreted programming language, and the files are just installed to their location on the system.
The Requires
directive, which specifies run-time dependencies for the package, include only bash
, because the bello
script requires only the bash
shell environment to execute.
The %build
section, which specifies how to build the software, is blank, because a bash
does not need to be built.
For installing bello
you only need to create the destination directory and install the executable bash
script file there. Hence, you can use the install
command in the %install
section. RPM macros allow to do this without hardcoding paths.
Ressources supplémentaires
3.11. An example SPEC file for a program written in Python
This section shows an example SPEC file for the pello program written in the Python programming language.
An example SPEC file for the pello program written in Python
Name: python-pello Version: 1.0.2 Release: 1%{?dist} Summary: Example Python library License: MIT URL: https://github.com/fedora-python/Pello Source: %{url}/archive/v%{version}/Pello-%{version}.tar.gz BuildArch: noarch BuildRequires: python3-devel # Build dependencies needed to be specified manually BuildRequires: python3-setuptools # Test dependencies needed to be specified manually # Also runtime dependencies need to be BuildRequired manually to run tests during build BuildRequires: python3-pytest >= 3 %global _description %{expand: Pello is an example package with an executable that prints Hello World! on the command line.} %description %_description %package -n python3-pello Summary: %{summary} %description -n python3-pello %_description %prep %autosetup -p1 -n Pello-%{version} %build # The macro only supported projects with setup.py %py3_build %install # The macro only supported projects with setup.py %py3_install %check %{pytest} # Note that there is no %%files section for the unversioned python module %files -n python3-pello %doc README.md %license LICENSE.txt %{_bindir}/pello_greeting # The library files needed to be listed manually %{python3_sitelib}/pello/ # The metadata files needed to be listed manually %{python3_sitelib}/Pello-*.egg-info/
Ressources supplémentaires
3.12. An example SPEC file for a program written in C
This section shows an example SPEC file for the cello program that was written in the C programming language.
An example SPEC file for the cello program written in C
Name: cello Version: 1.0 Release: 1%{?dist} Summary: Hello World example implemented in C License: GPLv3+ URL: https://www.example.com/%{name} Source0: https://www.example.com/%{name}/releases/%{name}-%{version}.tar.gz Patch0: cello-output-first-patch.patch BuildRequires: gcc BuildRequires: make %description The long-tail description for our Hello World Example implemented in C. %prep %setup -q %patch0 %build make %{?_smp_mflags} %install %make_install %files %license LICENSE %{_bindir}/%{name} %changelog * Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org> - 1.0-1 - First cello package
The BuildRequires
directive, which specifies build-time dependencies for the package, includes two packages that are needed to perform the compilation build process:
-
The
gcc
package -
The
make
package
The Requires
directive, which specifies run-time dependencies for the package, is omitted in this example. All runtime requirements are handled by rpmbuild
, and the cello
program does not require anything outside of the core C standard libraries.
The %build
section reflects the fact that in this example a Makefile
for the cello program was written, hence the GNU make command provided by the rpmdev-newspec
utility can be used. However, you need to remove the call to %configure
because you did not provide a configure script.
The installation of the cello program can be accomplished by using the %make_install
macro that was provided by the rpmdev-newspec
command. This is possible because the Makefile
for the cello program is available.
Ressources supplémentaires
3.13. Building RPMs
RPMs are built with the rpmbuild
command. This command expects a certain directory and file structure, which is the same as the structure that was set up by the rpmdev-setuptree
utility.
Different use cases and desired outcomes require different combinations of arguments to the rpmbuild
command. The two main use cases are:
- Building source RPMs
Building binary RPM
- Rebuilding a binary RPM from a source RPM
- Building a binary RPM from the SPEC file
- Building a binary RPM from a source RPM
This following sections describe how to build an RPM after a SPEC file for a program has been created.
3.14. Building source RPMs
The following procedure describes how to build a source RPM.
Conditions préalables
- A SPEC file for the program that we want to package must already exist.
Procédure
Run the
rpmbuild
command with the specified SPEC file:$ rpmbuild -bs SPECFILE
Substitute SPECFILE with the SPEC file. The
-bs
option stands for the build source.
The following example shows building source RPMs for the bello
, pello
, and cello
projects.
Building source RPMs for bello, pello, and cello.
$ cd ~/rpmbuild/SPECS/ 8$ rpmbuild -bs bello.spec Wrote: /home/admiller/rpmbuild/SRPMS/bello-0.1-1.el8.src.rpm $ rpmbuild -bs pello.spec Wrote: /home/admiller/rpmbuild/SRPMS/pello-0.1.2-1.el8.src.rpm $ rpmbuild -bs cello.spec Wrote: /home/admiller/rpmbuild/SRPMS/cello-1.0-1.el8.src.rpm
Verification steps
-
Make sure that the
rpmbuild/SRPMS
directory includes the resulting source RPMs. The directory is a part of the structure expected byrpmbuild
.
Ressources supplémentaires
3.15. Rebuilding a binary RPM from a source RPM
The following procedure shows how to rebuild a binary RPM from a source RPM (SRPM).
Procédure
To rebuild
bello
,pello
, andcello
from their SRPMs, run:$ rpmbuild --rebuild ~/rpmbuild/SRPMS/bello-0.1-1.el8.src.rpm [output truncated] $ rpmbuild --rebuild ~/rpmbuild/SRPMS/pello-0.1.2-1.el8.src.rpm [output truncated] $ rpmbuild --rebuild ~/rpmbuild/SRPMS/cello-1.0-1.el8.src.rpm [output truncated]
Invoking rpmbuild --rebuild
involves:
-
Installing the contents of the SRPM - the SPEC file and the source code - into the
~/rpmbuild/
directory. - Building using the installed contents.
- Removing the SPEC file and the source code.
To retain the SPEC file and the source code after building, you can:
-
When building, use the
rpmbuild
command with the--recompile
option instead of the--rebuild
option. Install the SRPMs using these commands:
$ rpm -Uvh ~/rpmbuild/SRPMS/bello-0.1-1.el8.src.rpm Updating / installing… 1:bello-0.1-1.el8 [100%] $ rpm -Uvh ~/rpmbuild/SRPMS/pello-0.1.2-1.el8.src.rpm Updating / installing… …1:pello-0.1.2-1.el8 [100%] $ rpm -Uvh ~/rpmbuild/SRPMS/cello-1.0-1.el8.src.rpm Updating / installing… …1:cello-1.0-1.el8 [100%]
The output generated when creating a binary RPM is verbose, which is helpful for debugging. The output varies for different examples and corresponds to their SPEC files.
The resulting binary RPMs are in the ~/rpmbuild/RPMS/YOURARCH
directory where YOURARCH
is your architecture or in the ~/rpmbuild/RPMS/noarch/
directory, if the package is not architecture-specific.
3.16. Building a binary RPM from the SPEC file
The following procedure shows how to build bello
, pello
, and cello
binary RPMs from their SPEC files.
Procédure
Run the
rpmbuild
command with thebb
option:$ rpmbuild -bb ~/rpmbuild/SPECS/bello.spec $ rpmbuild -bb ~/rpmbuild/SPECS/pello.spec $ rpmbuild -bb ~/rpmbuild/SPECS/cello.spec
3.17. Building binary RPMs from source RPMs
It is also possible to build any kind of RPM from a source RPM. To do so, use the following procedure.
Procédure
Run the
rpmbuild
command with one of the below options and with the source package specified:# rpmbuild {-ra|-rb|-rp|-rc|-ri|-rl|-rs} [rpmbuild-options] SOURCEPACKAGE
Ressources supplémentaires
-
rpmbuild(8)
man page
3.18. Checking RPMs for sanity
After creating a package, it is necessary to check the quality of the package.
The main tool for checking package quality is rpmlint.
The rpmlint
tool does the following:
- Improves RPM maintainability.
- Enables sanity checking by performing static analysis of the RPM.
- Enables error checking by performing static analysis of the RPM.
The rpmlint
tool can check binary RPMs, source RPMs (SRPMs), and SPEC files, so it is useful for all stages of packaging, as shown in the following sections.
Note that rpmlint
has very strict guidelines; hence it is sometimes acceptable to skip some of its errors and warnings, as shown in the following examples.
In the examples described in the following sections, rpmlint
is run without any options, which produces a non-verbose output. For detailed explanations of each error or warning, you can run rpmlint -i
instead.
3.19. Checking bello for sanity
This section shows possible warnings and errors that can occur when checking RPM sanity on the example of the bello SPEC file and bello binary RPM.
3.19.1. Checking the bello SPEC File
Exemple 3.2. Output of running the rpmlint
command on the SPEC file for bello
$ rpmlint bello.spec bello.spec: W: invalid-url Source0: https://www.example.com/bello/releases/bello-0.1.tar.gz HTTP Error 404: Not Found 0 packages and 1 specfiles checked; 0 errors, 1 warnings.
For bello.spec
, there is only one warning, which says that the URL listed in the Source0
directive is unreachable. This is expected, because the specified example.com
URL does not exist. Presuming that we expect this URL to work in the future, we can ignore this warning.
Exemple 3.3. Output of running the rpmlint
command on the SRPM for bello
$ rpmlint ~/rpmbuild/SRPMS/bello-0.1-1.el8.src.rpm bello.src: W: invalid-url URL: https://www.example.com/bello HTTP Error 404: Not Found bello.src: W: invalid-url Source0: https://www.example.com/bello/releases/bello-0.1.tar.gz HTTP Error 404: Not Found 1 packages and 0 specfiles checked; 0 errors, 2 warnings.
For the bello
SRPM, there is a new warning, which says that the URL specified in the URL
directive is unreachable. Assuming the link will be working in the future, we can ignore this warning.
3.19.2. Checking the bello binary RPM
When checking binary RPMs, rpmlint
checks for the following items:
- Documentation
- Manual pages
- Consistent use of the filesystem hierarchy standard
Exemple 3.4. Output of running the rpmlint
command on the binary RPM for bello
$ rpmlint ~/rpmbuild/RPMS/noarch/bello-0.1-1.el8.noarch.rpm bello.noarch: W: invalid-url URL: https://www.example.com/bello HTTP Error 404: Not Found bello.noarch: W: no-documentation bello.noarch: W: no-manual-page-for-binary bello 1 packages and 0 specfiles checked; 0 errors, 3 warnings.
The no-documentation
and no-manual-page-for-binary
warnings say that he RPM has no documentation or manual pages, because we did not provide any. Apart from the above warnings, the RPM passed rpmlint
checks.
3.20. Checking pello for sanity
This section shows possible warnings and errors that can occur when checking RPM sanity on the example of the pello SPEC file and pello binary RPM.
3.20.1. Checking the pello SPEC File
Exemple 3.5. Output of running the rpmlint
command on the SPEC file for pello
$ rpmlint pello.spec pello.spec:30: E: hardcoded-library-path in %{buildroot}/usr/lib/%{name} pello.spec:34: E: hardcoded-library-path in /usr/lib/%{name}/%{name}.pyc pello.spec:39: E: hardcoded-library-path in %{buildroot}/usr/lib/%{name}/ pello.spec:43: E: hardcoded-library-path in /usr/lib/%{name}/ pello.spec:45: E: hardcoded-library-path in /usr/lib/%{name}/%{name}.py* pello.spec: W: invalid-url Source0: https://www.example.com/pello/releases/pello-0.1.2.tar.gz HTTP Error 404: Not Found 0 packages and 1 specfiles checked; 5 errors, 1 warnings.
The invalid-url Source0
warning says that the URL listed in the Source0
directive is unreachable. This is expected, because the specified example.com
URL does not exist. Presuming that this URL will work in the future, you can ignore this warning.
The hardcoded-library-path
errors suggest to use the %{_libdir}
macro instead of hard-coding the library path. For the sake of this example, you can safely ignore these errors. However, for packages going into production make sure to check all errors carefully.
Exemple 3.6. Output of running the rpmlint
command on the SRPM for pello
$ rpmlint ~/rpmbuild/SRPMS/pello-0.1.2-1.el8.src.rpm pello.src: W: invalid-url URL: https://www.example.com/pello HTTP Error 404: Not Found pello.src:30: E: hardcoded-library-path in %{buildroot}/usr/lib/%{name} pello.src:34: E: hardcoded-library-path in /usr/lib/%{name}/%{name}.pyc pello.src:39: E: hardcoded-library-path in %{buildroot}/usr/lib/%{name}/ pello.src:43: E: hardcoded-library-path in /usr/lib/%{name}/ pello.src:45: E: hardcoded-library-path in /usr/lib/%{name}/%{name}.py* pello.src: W: invalid-url Source0: https://www.example.com/pello/releases/pello-0.1.2.tar.gz HTTP Error 404: Not Found 1 packages and 0 specfiles checked; 5 errors, 2 warnings.
The new invalid-url URL
error here is about the URL
directive, which is unreachable. Assuming that the URL will be valid in the future, you can safely ignore this error.
3.20.2. Checking the pello binary RPM
When checking binary RPMs, rpmlint
checks for the following items:
- Documentation
- Manual pages
- Consistent use of the Filesystem Hierarchy Standard
Exemple 3.7. Output of running the rpmlint
command on the binary RPM for pello
$ rpmlint ~/rpmbuild/RPMS/noarch/pello-0.1.2-1.el8.noarch.rpm pello.noarch: W: invalid-url URL: https://www.example.com/pello HTTP Error 404: Not Found pello.noarch: W: only-non-binary-in-usr-lib pello.noarch: W: no-documentation pello.noarch: E: non-executable-script /usr/lib/pello/pello.py 0644L /usr/bin/env pello.noarch: W: no-manual-page-for-binary pello 1 packages and 0 specfiles checked; 1 errors, 4 warnings.
The no-documentation
and no-manual-page-for-binary
warnings say that the RPM has no documentation or manual pages, because you did not provide any.
The only-non-binary-in-usr-lib
warning says that you provided only non-binary artifacts in /usr/lib/
. This directory is normally reserved for shared object files, which are binary files. Therefore, rpmlint
expects at least one or more files in /usr/lib/
directory to be binary.
This is an example of an rpmlint
check for compliance with Filesystem Hierarchy Standard. Normally, use RPM macros to ensure the correct placement of files. For the sake of this example, you can safely ignore this warning.
The non-executable-script
error warns that the /usr/lib/pello/pello.py
file has no execute permissions. The rpmlint
tool expects the file to be executable, because the file contains the shebang. For the purpose of this example, you can leave this file without execute permissions and ignore this error.
Apart from the above warnings and errors, the RPM passed rpmlint
checks.
3.21. Checking cello for sanity
This section shows possible warnings and errors that can occur when checking RPM sanity on the example of the cello SPEC file and pello binary RPM.
3.21.1. Checking the cello SPEC File
Exemple 3.8. Output of running the rpmlint
command on the SPEC file for cello
$ rpmlint ~/rpmbuild/SPECS/cello.spec /home/admiller/rpmbuild/SPECS/cello.spec: W: invalid-url Source0: https://www.example.com/cello/releases/cello-1.0.tar.gz HTTP Error 404: Not Found 0 packages and 1 specfiles checked; 0 errors, 1 warnings.
For cello.spec
, there is only one warning, which says that the URL listed in the Source0
directive is unreachable. This is expected, because the specified example.com
URL does not exist. Presuming that this URL will work in the future, you can ignore this warning.
Exemple 3.9. Output of running the rpmlint
command on the SRPM for cello
$ rpmlint ~/rpmbuild/SRPMS/cello-1.0-1.el8.src.rpm cello.src: W: invalid-url URL: https://www.example.com/cello HTTP Error 404: Not Found cello.src: W: invalid-url Source0: https://www.example.com/cello/releases/cello-1.0.tar.gz HTTP Error 404: Not Found 1 packages and 0 specfiles checked; 0 errors, 2 warnings.
For the cello
SRPM, there is a new warning, which says that the URL specified in the URL
directive is unreachable. Assuming the link will be working in the future, you can ignore this warning.
3.21.2. Checking the cello binary RPM
When checking binary RPMs, rpmlint
checks for the following items:
- Documentation
- Manual pages
- Consistent use of the filesystem hierarchy standard
Exemple 3.10. Output of running the rpmlint
command on the binary RPM for cello
$ rpmlint ~/rpmbuild/RPMS/x86_64/cello-1.0-1.el8.x86_64.rpm cello.x86_64: W: invalid-url URL: https://www.example.com/cello HTTP Error 404: Not Found cello.x86_64: W: no-documentation cello.x86_64: W: no-manual-page-for-binary cello 1 packages and 0 specfiles checked; 0 errors, 3 warnings.
The no-documentation
and no-manual-page-for-binary
warnings say that he RPM has no documentation or manual pages, because you did not provide any. Apart from the above warnings, the RPM passed rpmlint
checks.
3.22. Logging RPM activity to syslog
Any RPM activity or transaction can be logged by the System Logging protocol (syslog).
Conditions préalables
To enable the logging of RPM transactions to syslog, make sure that the
syslog
plug-in is installed on the system:# dnf install rpm-plugin-syslog
NoteThe default location for the syslog messages is the
/var/log/messages
file. However, you can configure syslog to use another location to store the messages.
To see the updates on RPM activity, follow the described procedure.
Procédure
-
Open the file that you configured to store the syslog messages, or if you use the default syslog configuration, open the
/var/log/messages
file. -
Search for new lines including the
[RPM]
string.
3.23. Extracting RPM content
In particular cases, for example, if a package required by RPM is damaged, it is necessary to extract the content of the package. In such cases, if an RPM installation is still working despite the damage, you can use the rpm2archive
utility to convert an .rpm file to a tar archive to use the content of the package.
If the RPM installation is severely damaged, you can use the rpm2cpio
utility to convert the RPM package file to a cpio archive.
The following procedure describes how to convert an rpm payload to a tar archive using the rpm2archive
utility.
Procédure
Exécutez la commande suivante :
$ rpm2archive filename.rpm
Replace filename with the name of the .rpm file.
The resulting file has the
.tgz
suffix. For example, to archive thebash
package:$ rpm2archive bash-4.4.19-6.el8.x86_64.rpm $ bash-4.4.19-6.el8.x86_64.rpm.tgz bash-4.4.19-6.el8.x86_64.rpm.tgz
Chapitre 4. Advanced topics
This section covers topics that are beyond the scope of the introductory tutorial but are useful in real-world RPM packaging.
4.1. Signing RPM packages
You can sign RPM packages to ensure that no third party can alter their content. To add an additional layer of security, use the HTTPS protocol when downloading the package.
You can sign a package by using the --addsign
option provided by the rpm-sign
package.
Conditions préalables
- You have created a GNU Privacy Guard (GPG) key as described in Creating a GPG key.
4.1.1. Creating a GPG key
Use the following procedure to create a GNU Privacy Guard (GPG) key required for signing packages.
Procédure
Generate a GPG key pair:
# gpg --gen-key
Check the generated key pair:
# gpg --list-keys
Exporter la clé publique :
# gpg --export -a '<Key_name>' > RPM-GPG-KEY-pmanager
Replace <Key_name> with the real key name that you have selected.
Import the exported public key into an RPM database:
# rpm --import RPM-GPG-KEY-pmanager
4.1.2. Configuring RPM to sign a package
To be able to sign an RPM package, you need to specify the %_gpg_name
RPM macro.
The following procedure describes how to configure RPM for signing a package.
Procédure
Define the
%_gpg_name
macro in your$HOME/.rpmmacros
file as follows:%_gpg_name Key ID
Replace Key ID with the GNU Privacy Guard (GPG) key ID that you will use to sign a package. A valid GPG key ID value is either a full name or email address of the user who created the key.
4.1.3. Adding a signature to an RPM package
This section describes the most usual case when a package is built without a signature. The signature is added just before the release of the package.
To add a signature to an RPM package, use the --addsign
option provided by the rpm-sign
package.
Procédure
Add a signature to a package:
$ rpm --addsign package-name.rpm
Replace package-name with the name of an RPM package you want to sign.
NoteYou must enter the password to unlock the secret key for the signature.
4.2. More on macros
This section covers selected built-in RPM Macros. For an exhaustive list of such macros, see RPM Documentation.
4.2.1. Defining your own macros
The following section describes how to create a custom macro.
Procédure
Include the following line in the RPM SPEC file:
%global <name>[(opts)] <body>
All whitespace surrounding <body>
is removed. Name may be composed of alphanumeric characters, and the character _
and must be at least 3 characters in length. Inclusion of the (opts)
field is optional:
-
Simple
macros do not contain the(opts)
field. In this case, only recursive macro expansion is performed. -
Parametrized
macros contain the(opts)
field. Theopts
string between parentheses is passed togetopt(3)
forargc/argv
processing at the beginning of a macro invocation.
Older RPM SPEC files use the %define <name> <body>
macro pattern instead. The differences between %define
and %global
macros are as follows:
-
%define
has local scope. It applies to a specific part of a SPEC file. The body of a%define
macro is expanded when used. -
%global
has global scope. It applies to an entire SPEC file. The body of a%global
macro is expanded at definition time.
Macros are evaluated even if they are commented out or the name of the macro is given into the %changelog
section of the SPEC file. To comment out a macro, use %%
. For example: %%global
.
Ressources supplémentaires
4.2.2. Using the %setup macro
This section describes how to build packages with source code tarballs using different variants of the %setup
macro. Note that the macro variants can be combined. The rpmbuild
output illustrates standard behavior of the %setup
macro. At the beginning of each phase, the macro outputs Executing(%…)
, as shown in the below example.
Exemple 4.1. Example %setup
macro output
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.DhddsG
The shell output is set with set -x
enabled. To see the content of /var/tmp/rpm-tmp.DhddsG
, use the --debug
option because rpmbuild
deletes temporary files after a successful build. This displays the setup of environment variables followed by for example:
cd '/builddir/build/BUILD' rm -rf 'cello-1.0' /usr/bin/gzip -dc '/builddir/build/SOURCES/cello-1.0.tar.gz' | /usr/bin/tar -xof - STATUS=$? if [ $STATUS -ne 0 ]; then exit $STATUS fi cd 'cello-1.0' /usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .
The %setup
macro:
- Ensures that we are working in the correct directory.
- Removes residues of previous builds.
- Unpacks the source tarball.
- Sets up some default privileges.
4.2.2.1. Using the %setup -q macro
The -q
option limits the verbosity of the %setup
macro. Only tar -xof
is executed instead of tar -xvvof
. Use this option as the first option.
4.2.2.2. Using the %setup -n macro
The -n
option is used to specify the name of the directory from expanded tarball.
This is used in cases when the directory from expanded tarball has a different name from what is expected (%{name}-%{version}
), which can lead to an error of the %setup
macro.
For example, if the package name is cello
, but the source code is archived in hello-1.0.tgz
and contains the hello/
directory, the SPEC file content needs to be as follows:
Name: cello Source0: https://example.com/%{name}/release/hello-%{version}.tar.gz … %prep %setup -n hello
4.2.2.3. Using the %setup -c macro
The -c
option is used if the source code tarball does not contain any subdirectories and after unpacking, files from an archive fills the current directory.
The -c
option then creates the directory and steps into the archive expansion as shown below:
/usr/bin/mkdir -p cello-1.0 cd 'cello-1.0'
The directory is not changed after archive expansion.
4.2.2.4. Using the %setup -D and %setup -T macros
The -D
option disables deleting of source code directory, and is particularly useful if the %setup
macro is used several times. With the -D
option, the following lines are not used:
rm -rf 'cello-1.0'
The -T
option disables expansion of the source code tarball by removing the following line from the script:
/usr/bin/gzip -dc '/builddir/build/SOURCES/cello-1.0.tar.gz' | /usr/bin/tar -xvvof -
4.2.2.5. Using the %setup -a and %setup -b macros
The -a
and -b
options expand specific sources:
The -b
option stands for before
, and it expands specific sources before entering the working directory. The -a
option stands for after
, and it expands those sources after entering. Their arguments are source numbers from the SPEC file preamble.
In the following example, the cello-1.0.tar.gz
archive contains an empty examples
directory. The examples are shipped in a separate examples.tar.gz
tarball and they expand into the directory of the same name. In this case, use -a 1
, if you want to expand Source1
after entering the working directory:
Source0: https://example.com/%{name}/release/%{name}-%{version}.tar.gz Source1: examples.tar.gz … %prep %setup -a 1
In the following example, examples are provided in a separate cello-1.0-examples.tar.gz
tarball, which expands into cello-1.0/examples
. In this case, use -b 1
, to expand Source1
before entering the working directory:
Source0: https://example.com/%{name}/release/%{name}-%{version}.tar.gz Source1: %{name}-%{version}-examples.tar.gz … %prep %setup -b 1
4.2.3. Common RPM macros in the %files section
The following table lists advanced RPM Macros that are needed in the %files
section of a SPEC file.
Macro | Définition |
---|---|
%license |
The macro identifies the file listed as a LICENSE file and it will be installed and labeled as such by RPM. Example: |
%doc |
The macro identifies a file listed as documentation and it will be installed and labeled as such by RPM. The macro is used for documentation about the packaged software and also for code examples and various accompanying items. In the event code examples are included, care should be taken to remove executable mode from the file. Example: |
%dir |
The macro ensures that the path is a directory owned by this RPM. This is important so that the RPM file manifest accurately knows what directories to clean up on uninstall. Example: |
%config(noreplace) |
The macro ensures that the following file is a configuration file and therefore should not be overwritten (or replaced) on a package install or update if the file has been modified from the original installation checksum. If there is a change, the file will be created with |
4.2.4. Displaying the built-in macros
Red Hat Enterprise Linux provides multiple built-in RPM macros.
Procédure
To display all built-in RPM macros, run:
rpm --showrc
NoteThe output is quite sizeable. To narrow the result, use the command above with the
grep
command.To find information about the RPMs macros for your system’s version of RPM, run:
rpm -ql rpm
NoteRPM macros are the files titled
macros
in the output directory structure.
4.2.5. RPM distribution macros
Different distributions provide different sets of recommended RPM macros based on the language implementation of the software being packaged or the specific guidelines of the distribution.
The sets of recommended RPM macros are often provided as RPM packages, ready to be installed with the dnf
package manager.
Once installed, the macro files can be found in the /usr/lib/rpm/macros.d/
directory.
Procédure
To display the raw RPM macro definitions, run:
rpm --showrc
The above output displays the raw RPM macro definitions.
To determine what a macro does and how it can be helpful when packaging RPMs, run the
rpm --eval
command with the name of the macro used as its argument:rpm --eval %{_MACRO}
Ressources supplémentaires
-
rpm
man page
4.2.6. Creating custom macros
You can override the distribution macros in the ~/.rpmmacros
file with your custom macros. Any changes that you make affect every build on your machine.
Defining any new macros in the ~/.rpmmacros
file is not recommended. Such macros would not be present on other machines, where users may want to try to rebuild your package.
Procédure
To override a macro, run:
%_topdir /opt/some/working/directory/rpmbuild
You can create the directory from the example above, including all subdirectories through the rpmdev-setuptree
utility. The value of this macro is by default ~/rpmbuild
.
%_smp_mflags -l3
The macro above is often used to pass to Makefile, for example make %{?_smp_mflags}
, and to set a number of concurrent processes during the build phase. By default, it is set to -jX
, where X
is a number of cores. If you alter the number of cores, you can speed up or slow down a build of packages.
4.3. Epoch, Scriptlets and Triggers
This section covers Epoch
, Scriptlets
, and Triggers
, which represent advanced directives for RMP SPEC files.
All these directives influence not only the SPEC file, but also the end machine on which the resulting RPM is installed.
4.3.1. The Epoch directive
The Epoch
directive enables to define weighted dependencies based on version numbers.
If this directive is not listed in the RPM SPEC file, the Epoch
directive is not set at all. This is contrary to common belief that not setting Epoch
results in an Epoch
of 0. However, the dnf
utility treats an unset Epoch
as the same as an Epoch
of 0 for the purposes of depsolving.
However, listing Epoch
in a SPEC file is usually omitted because in majority of cases introducing an Epoch
value skews the expected RPM behavior when comparing versions of packages.
Exemple 4.2. Using Epoch
If you install the foobar
package with Epoch: 1
and Version: 1.0
, and someone else packages foobar
with Version: 2.0
but without the Epoch
directive, the new version will never be considered an update. The reason being that the Epoch
version is preferred over the traditional Name-Version-Release
marker that signifies versioning for RPM Packages.
Using of Epoch
is thus quite rare. However, Epoch
is typically used to resolve an upgrade ordering issue. The issue can appear as a side effect of upstream change in software version number schemes or versions incorporating alphabetical characters that cannot always be compared reliably based on encoding.
4.3.2. Scriptlets directives
Scriptlets are a series of RPM directives that are executed before or after packages are installed or deleted.
Use Scriptlets only for tasks that cannot be done at build time or in an start up script.
A set of common Scriptlet directives exists. They are similar to the SPEC file section headers, such as %build
or %install
. They are defined by multi-line segments of code, which are often written as a standard POSIX shell script. However, they can also be written in other programming languages that RPM for the target machine’s distribution accepts. RPM Documentation includes an exhaustive list of available languages.
The following table includes Scriptlet directives listed in their execution order. Note that a package containing the scripts is installed between the %pre
and %post
directive, and it is uninstalled between the %preun
and %postun
directive.
Directive | Définition |
---|---|
| Scriptlet that is executed just before installing or removing any package. |
| Scriptlet that is executed just before installing the package on the target system. |
| Scriptlet that is executed just after the package was installed on the target system. |
| Scriptlet that is executed just before uninstalling the package from the target system. |
| Scriptlet that is executed just after the package was uninstalled from the target system. |
| Scriptlet that is executed at the end of the transaction. |
4.3.3. Turning off a scriptlet execution
The following procedure describes how to turn off the execution of any scriptlet using the rpm
command together with the --no_scriptlet_name_
option.
Procédure
For example, to turn off the execution of the
%pretrans
scriptlets, run:# rpm --nopretrans
You can also use the
-- noscripts
option, which is equivalent to all of the following:-
--nopre
-
--nopost
-
--nopreun
-
--nopostun
-
--nopretrans
-
--noposttrans
-
Ressources supplémentaires
-
rpm(8)
man page.
4.3.4. Scriptlets macros
The Scriptlets directives also work with RPM macros.
The following example shows the use of systemd scriptlet macro, which ensures that systemd is notified about a new unit file.
$ rpm --showrc | grep systemd -14: __transaction_systemd_inhibit %{__plugindir}/systemd_inhibit.so -14: _journalcatalogdir /usr/lib/systemd/catalog -14: _presetdir /usr/lib/systemd/system-preset -14: _unitdir /usr/lib/systemd/system -14: _userunitdir /usr/lib/systemd/user /usr/lib/systemd/systemd-binfmt %{?*} >/dev/null 2>&1 || : /usr/lib/systemd/systemd-sysctl %{?*} >/dev/null 2>&1 || : -14: systemd_post -14: systemd_postun -14: systemd_postun_with_restart -14: systemd_preun -14: systemd_requires Requires(post): systemd Requires(preun): systemd Requires(postun): systemd -14: systemd_user_post %systemd_post --user --global %{?*} -14: systemd_user_postun %{nil} -14: systemd_user_postun_with_restart %{nil} -14: systemd_user_preun systemd-sysusers %{?*} >/dev/null 2>&1 || : echo %{?*} | systemd-sysusers - >/dev/null 2>&1 || : systemd-tmpfiles --create %{?*} >/dev/null 2>&1 || : $ rpm --eval %{systemd_post} if [ $1 -eq 1 ] ; then # Initial installation systemctl preset >/dev/null 2>&1 || : fi $ rpm --eval %{systemd_postun} systemctl daemon-reload >/dev/null 2>&1 || : $ rpm --eval %{systemd_preun} if [ $1 -eq 0 ] ; then # Package removal, not upgrade systemctl --no-reload disable > /dev/null 2>&1 || : systemctl stop > /dev/null 2>&1 || : fi
4.3.5. The Triggers directives
Triggers are RPM directives which provide a method for interaction during package installation and uninstallation.
Triggers may be executed at an unexpected time, for example on update of the containing package. Triggers are difficult to debug, therefore they need to be implemented in a robust way so that they do not break anything when executed unexpectedly. For these reasons, Red Hat recommends to minimize the use of Triggers.
The order of execution on a single package upgrade and the details for each existing Triggers are listed below:
all-%pretrans … any-%triggerprein (%triggerprein from other packages set off by new install) new-%triggerprein new-%pre for new version of package being installed … (all new files are installed) new-%post for new version of package being installed any-%triggerin (%triggerin from other packages set off by new install) new-%triggerin old-%triggerun any-%triggerun (%triggerun from other packages set off by old uninstall) old-%preun for old version of package being removed … (all old files are removed) old-%postun for old version of package being removed old-%triggerpostun any-%triggerpostun (%triggerpostun from other packages set off by old un install) … all-%posttrans
The above items are found in the /usr/share/doc/rpm-4.*/triggers
file.
4.3.6. Using non-shell scripts in a SPEC file
The -p
scriptlet option in a SPEC file enables the user to invoke a specific interpreter instead of the default shell scripts interpreter (-p /bin/sh
).
The following procedure describes how to create a script, which prints out a message after installation of the pello.py
program:
Procédure
-
Open the
pello.spec
file. Find the following line:
install -m 0644 %{name}.py* %{buildroot}/usr/lib/%{name}/
Under the above line, insert:
%post -p /usr/bin/python3 print("This is {} code".format("python"))
- Build your package as described in Building RPMs.
Install your package:
# dnf install /home/<username>/rpmbuild/RPMS/noarch/pello-0.1.2-1.el8.noarch.rpm
Check the output message after the installation:
Installing : pello-0.1.2-1.el8.noarch 1/1 Running scriptlet: pello-0.1.2-1.el8.noarch 1/1 This is python code
To use a Python 3 script, include the following line under install -m
in a SPEC file:
%post -p /usr/bin/python3
To use a Lua script, include the following line under install -m
in a SPEC file:
%post -p <lua>
This way, you can specify any interpreter in a SPEC file.
4.4. RPM conditionals
RPM Conditionals enable conditional inclusion of various sections of the SPEC file.
Conditional inclusions usually deal with:
- Architecture-specific sections
- Operating system-specific sections
- Compatibility issues between various versions of operating systems
- Existence and definition of macros
4.4.1. RPM conditionals syntax
RPM conditionals use the following syntax:
If expression is true, then do some action:
%if expression … %endif
If expression is true, then do some action, in other case, do another action:
%if expression … %else … %endif
4.4.2. The %if conditionals
This section provides examples of using the %if
RPM conditionals.
Exemple 4.3. Using the %if conditional to handle compatibility between Red Hat Enterprise Linux 8 and other operating systems
%if 0%{?rhel} == 8 sed -i '/AS_FUNCTION_DESCRIBE/ s/^/#/' configure.in sed -i '/AS_FUNCTION_DESCRIBE/ s/^/#/' acinclude.m4 %endif
This conditional handles compatibility between RHEL 8 and other operating systems in terms of support of the AS_FUNCTION_DESCRIBE macro. If the package is built for RHEL, the %rhel
macro is defined, and it is expanded to RHEL version. If its value is 8, meaning the package is build for RHEL 8, then the references to AS_FUNCTION_DESCRIBE, which is not supported by RHEL 8, are deleted from autoconfig scripts.
Exemple 4.4. Using the %if conditional to handle definition of macros
%define ruby_archive %{name}-%{ruby_version} %if 0%{?milestone:1}%{?revision:1} != 0 %define ruby_archive %{ruby_archive}-%{?milestone}%{?!milestone:%{?revision:r%{revision}}} %endif
This conditional handles definition of macros. If the %milestone
or the %revision
macros are set, the %ruby_archive
macro, which defines the name of the upstream tarball, is redefined.
4.4.3. Specialized variants of %if conditionals
The %ifarch
conditional, %ifnarch
conditional and %ifos
conditional are specialized variants of the %if
conditionals. These variants are commonly used, hence they have their own macros.
The %ifarch conditional
The %ifarch
conditional is used to begin a block of the SPEC file that is architecture-specific. It is followed by one or more architecture specifiers, each separated by commas or whitespace.
Exemple 4.5. An example use of the %ifarch conditional
%ifarch i386 sparc … %endif
All the contents of the SPEC file between %ifarch
and %endif
are processed only on the 32-bit AMD and Intel architectures or Sun SPARC-based systems.
The %ifnarch conditional
The %ifnarch
conditional has a reverse logic than %ifarch
conditional.
Exemple 4.6. An example use of the %ifnarch conditional
%ifnarch alpha … %endif
All the contents of the SPEC file between %ifnarch
and %endif
are processed only if not done on a Digital Alpha/AXP-based system.
The %ifos conditional
The %ifos
conditional is used to control processing based on the operating system of the build. It can be followed by one or more operating system names.
Exemple 4.7. An example use of the %ifos conditional
%ifos linux … %endif
All the contents of the SPEC file between %ifos
and %endif
are processed only if the build was done on a Linux system.
4.5. Emballage des RPMs de Python 3
Vous pouvez installer des paquets Python sur votre système soit à partir du dépôt PyPI en amont en utilisant le programme d'installation pip
, soit en utilisant le gestionnaire de paquets DNF. DNF utilise le format de paquetage RPM, qui offre plus de contrôle en aval sur le logiciel.
Le format d'empaquetage des paquets Python natifs est défini par les spécifications de la Python Packaging Authority (PyPA). La plupart des projets Python utilisent les utilitaires distutils
ou setuptools
pour l'empaquetage et définissent des informations sur les paquets dans le fichier setup.py
. Cependant, les possibilités de créer des paquets Python natifs ont évolué au fil du temps. Pour plus d'informations sur les normes d'empaquetage émergentes, voir pyproject-rpm-macros.
Ce chapitre décrit comment empaqueter un projet Python qui utilise setup.py
dans un paquetage RPM. Cette approche présente les avantages suivants par rapport aux paquets Python natifs :
-
Les dépendances sur les paquets Python et non-Python sont possibles et strictement appliquées par le gestionnaire de paquets
DNF
. - Vous pouvez signer les paquets de manière cryptographique. La signature cryptographique permet de vérifier, d'intégrer et de tester le contenu des paquets RPM avec le reste du système d'exploitation.
- Vous pouvez exécuter des tests pendant le processus de construction.
4.5.1. Description du fichier SPEC pour un paquetage Python
Un fichier SPEC contient des instructions que l'utilitaire rpmbuild
utilise pour construire un RPM. Les instructions sont incluses dans une série de sections. Un fichier SPEC se compose de deux parties principales dans lesquelles les sections sont définies :
- Préambule (contient une série de métadonnées utilisées dans le corps du texte)
- Corps (contient la partie principale des instructions)
Un fichier RPM SPEC pour les projets Python présente certaines spécificités par rapport aux fichiers RPM SPEC non-Python.
Le nom de tout paquetage RPM d'une bibliothèque Python doit toujours inclure le préfixe python3-
ou python3.11-
.
D'autres particularités sont présentées dans l'exemple de fichier SPEC suivant pour le paquet python3*-pello
. Pour la description de ces spécificités, voir les notes sous l'exemple.
%global python3_pkgversion 3.11 1 Name: python-pello 2 Version: 1.0.2 Release: 1%{?dist} Summary: Example Python library License: MIT URL: https://github.com/fedora-python/Pello Source: %{url}/archive/v%{version}/Pello-%{version}.tar.gz BuildArch: noarch BuildRequires: python%{python3_pkgversion}-devel 3 # Build dependencies needed to be specified manually BuildRequires: python%{python3_pkgversion}-setuptools # Test dependencies needed to be specified manually # Also runtime dependencies need to be BuildRequired manually to run tests during build BuildRequires: python%{python3_pkgversion}-pytest >= 3 %global _description %{expand: Pello is an example package with an executable that prints Hello World! on the command line.} %description %_description %package -n python%{python3_pkgversion}-pello 4 Summary: %{summary} %description -n python%{python3_pkgversion}-pello %_description %prep %autosetup -p1 -n Pello-%{version} %build # The macro only supported projects with setup.py %py3_build 5 %install # The macro only supported projects with setup.py %py3_install %check 6 %{pytest} # Note that there is no %%files section for the unversioned python module %files -n python%{python3_pkgversion}-pello %doc README.md %license LICENSE.txt %{_bindir}/pello_greeting # The library files needed to be listed manually %{python3_sitelib}/pello/ # The metadata files needed to be listed manually %{python3_sitelib}/Pello-*.egg-info/
- 1
- En définissant la macro
python3_pkgversion
, vous définissez la version de Python pour laquelle ce paquet sera construit. Pour construire pour la version 3.9 de Python, mettez la macro à sa valeur par défaut3
ou supprimez entièrement la ligne. - 2
- Lorsque vous compilez un projet Python dans un RPM, ajoutez toujours le préfixe
python-
au nom original du projet. Le nom original est icipello
et, par conséquent, le name of the Source RPM (SRPM) estpython-pello
. - 3
- BuildRequires spécifie les paquets nécessaires pour construire et tester ce paquet. Dans BuildRequires, incluez toujours les éléments fournissant les outils nécessaires à la construction des paquets Python :
python3-devel
(oupython3.11-devel
) et les projets pertinents nécessaires au logiciel spécifique que vous empaquetez, par exemple,python3-setuptools
(oupython3.11-setuptools
) ou les dépendances d'exécution et de test nécessaires à l'exécution des tests dans la section \feck. - 4
- Lorsque vous choisissez un nom pour le RPM binaire (le paquet que les utilisateurs pourront installer), ajoutez un préfixe de version de Python. Utilisez le préfixe
python3-
pour Python 3.9 par défaut ou le préfixepython3.11-
pour Python 3.11. Vous pouvez utiliser la macro%{python3_pkgversion}
, qui évalue3
pour la version 3.9 de Python par défaut, à moins que vous ne lui attribuiez une version explicite, par exemple3.11
(voir note de bas de page 1). - 5
- Les macros %py3_build et %py3_install exécutent respectivement les commandes
setup.py build
etsetup.py install
, avec des arguments supplémentaires pour spécifier les emplacements d'installation, l'interpréteur à utiliser et d'autres détails. - 6
- La section \feck doit exécuter les tests du projet empaqueté. La commande exacte dépend du projet lui-même, mais il est possible d'utiliser la macro %pytest pour exécuter la commande
pytest
d'une manière adaptée à RPM.
4.5.2. Macros communes pour les RPMs Python 3
Dans un fichier SPEC, utilisez toujours les macros décrites dans le tableau suivant Macros for Python 3 RPMs plutôt que de coder en dur leurs valeurs. Vous pouvez redéfinir la version de Python 3 utilisée dans ces macros en définissant la macro python3_pkgversion
au sommet de votre fichier SPEC (voir Section 4.5.1, « Description du fichier SPEC pour un paquetage Python »). Si vous définissez la macro python3_pkgversion
, les valeurs des macros décrites dans le tableau suivant refléteront la version de Python 3 spécifiée.
Macro | Définition normale | Description |
---|---|---|
%{python3_pkgversion} | 3 |
La version de Python utilisée par toutes les autres macros. Peut être redéfinie à |
%{python3} | /usr/bin/python3 | L'interpréteur Python 3 |
%{python3_version} | 3.9 | La version majeure et mineure de l'interpréteur Python 3 |
%{python3_sitelib} | /usr/lib/python3.9/site-packages | L'emplacement où les modules Pure-Python sont installés |
%{python3_sitearch} | /usr/lib64/python3.9/site-packages | Emplacement où sont installés les modules contenant des modules d'extension spécifiques à l'architecture |
%py3_build |
Exécute la commande | |
%py3_install |
Exécute la commande | |
%{py3_shebang_flags} | s |
L'ensemble des drapeaux par défaut pour la macro des directives de l'interpréteur Python, |
%py3_shebang_fix |
Modifie les directives de l'interpréteur Python en |
Ressources supplémentaires
4.5.3. Utilisation des dépendances générées automatiquement pour les RPM Python
La procédure suivante décrit comment utiliser les dépendances générées automatiquement lors de l'empaquetage d'un projet Python sous forme de RPM.
Conditions préalables
- Il existe un fichier SPEC pour le RPM. Pour plus d'informations, voir Description du fichier SPEC pour un paquetage Python.
Procédure
Assurez-vous que l'un des répertoires suivants contenant les métadonnées fournies en amont est inclus dans le RPM résultant :
-
.dist-info
.egg-info
Le processus de construction du RPM génère automatiquement des versions virtuelles de
pythonX.Ydist
à partir de ces répertoires, par exemple :python3.9dist(pello)
Le générateur de dépendances Python lit ensuite les métadonnées en amont et génère des exigences d'exécution pour chaque paquet RPM en utilisant les
pythonX.Ydist
virtual provides générés. Par exemple, une balise d'exigences générée peut ressembler à ce qui suit :Requires: python3.9dist(requests)
-
- Inspecter les demandes générées.
Pour supprimer certaines des exigences générées, utilisez l'une des approches suivantes :
-
Modifier les métadonnées fournies en amont dans la section
%prep
du fichier SPEC. - Utiliser le filtrage automatique des dépendances décrit dans la documentation en amont.
-
Modifier les métadonnées fournies en amont dans la section
-
Pour désactiver le générateur automatique de dépendances, incluez la macro
%{?python_disable_dependency_generator}
au-dessus de la déclarationÞscription
du paquet principal.
Ressources supplémentaires
4.6. Gestion des directives de l'interpréteur dans les scripts Python
Dans Red Hat Enterprise Linux 9, les scripts Python exécutables sont censés utiliser des directives d'interpréteur (également connues sous le nom de hashbangs ou shebangs) qui spécifient explicitement au minimum la version majeure de Python. Par exemple :
#!/usr/bin/python3 #!/usr/bin/python3.9 #!/usr/bin/python3.11
Le script /usr/lib/rpm/redhat/brp-mangle-shebangs
buildroot policy (BRP) est exécuté automatiquement lors de la construction de tout paquetage RPM et tente de corriger les directives de l'interpréteur dans tous les fichiers exécutables.
Le script BRP génère des erreurs lorsqu'il rencontre un script Python avec une directive d'interprétation ambiguë, telle que :
#!/usr/bin/python
ou
#!/usr/bin/env python
4.6.1. Modifier les directives de l'interpréteur dans les scripts Python
Utilisez la procédure suivante pour modifier les directives de l'interpréteur dans les scripts Python qui provoquent des erreurs de compilation au moment de la compilation du RPM.
Conditions préalables
- Certaines directives de l'interpréteur dans vos scripts Python provoquent une erreur de compilation.
Procédure
Pour modifier les directives de l'interpréteur, effectuez l'une des tâches suivantes :
Utilisez la macro suivante dans la section
%prep
de votre fichier SPEC :# %py3_shebang_fix SCRIPTNAME..
SCRIPTNAME peut être un fichier, un répertoire ou une liste de fichiers et de répertoires.
En conséquence, tous les fichiers listés et tous les fichiers
.py
dans les répertoires listés verront leurs directives d'interprétation modifiées pour pointer vers%{python3}
. Les drapeaux existants de la directive d'interprétation originale seront préservés et des drapeaux supplémentaires définis dans la macro%{py3_shebang_flags}
seront ajoutés. Vous pouvez redéfinir la macro%{py3_shebang_flags}
dans votre fichier SPEC pour modifier les drapeaux qui seront ajoutés.Appliquer le script
pathfix.py
du paquetpython3-devel
:# pathfix.py -pn -i %{python3} PATH ..
Vous pouvez spécifier plusieurs chemins. Si un
PATH
est un répertoire,pathfix.py
recherche de manière récursive tous les scripts Python correspondant au modèle^[a-zA-Z0-9_] \.py$
, et pas seulement ceux dont la directive d'interprétation est ambiguë. Ajoutez la commande ci-dessus à la section%prep
ou à la fin de la section%install
.-
Modifiez les scripts Python fournis afin qu'ils soient conformes au format attendu. À cette fin, vous pouvez également utiliser le script
pathfix.py
en dehors du processus de compilation du RPM. Lorsque vous exécutezpathfix.py
en dehors d'une compilation RPM, remplacez%{python3}
de l'exemple précédent par un chemin d'accès à la directive de l'interpréteur, tel que/usr/bin/python3
ou/usr/bin/python3.11
.
Ressources supplémentaires
4.7. RubyGems packages
This section explains what RubyGems packages are, and how to re-package them into RPM.
4.7.1. What RubyGems are
Ruby is a dynamic, interpreted, reflective, object-oriented, general-purpose programming language.
Programs written in Ruby are typically packaged using the RubyGems project, which provides a specific Ruby packaging format.
Packages created by RubyGems are called gems, and they can be re-packaged into RPM as well.
This documentation refers to terms related to the RubyGems concept with the gem
prefix, for example .gemspec is used for the gem specification
, and terms related to RPM are unqualified.
4.7.2. How RubyGems relate to RPM
RubyGems represent Ruby’s own packaging format. However, RubyGems contain metadata similar to those needed by RPM, which enables the conversion from RubyGems to RPM.
According to Ruby Packaging Guidelines, it is possible to re-package RubyGems packages into RPM in this way:
- Such RPMs fit with the rest of the distribution.
- End users are able to satisfy dependencies of a gem by installing the appropriate RPM-packaged gem.
RubyGems use similar terminology as RPM, such as SPEC files, package names, dependencies and other items.
To fit into the rest of RHEL RPM distribution, packages created by RubyGems must follow the conventions listed below:
Names of gems must follow this pattern:
rubygem-%{gem_name}
To implement a shebang line, the following string must be used:
#!/usr/bin/ruby
4.7.3. Creating RPM packages from RubyGems packages
To create a source RPM for a RubyGems package, the following files are needed:
- A gem file
- An RPM SPEC file
The following sections describe how to create RPM packages from packages created by RubyGems.
4.7.3.1. RubyGems SPEC file conventions
A RubyGems SPEC file must meet the following conventions:
-
Contain a definition of
%{gem_name}
, which is the name from the gem’s specification. - The source of the package must be the full URL to the released gem archive; the version of the package must be the gem’s version.
Contain the
BuildRequires:
a directive defined as follows to be able to pull in the macros needed to build.BuildRequires:rubygems-devel
-
Not contain any RubyGems
Requires
orProvides
, because those are autogenerated. Not contain the
BuildRequires:
directive defined as follows, unless you want to explicitly specify Ruby version compatibility:Requires: ruby(release)
The automatically generated dependency on RubyGems (
Requires: ruby(rubygems)
) is sufficient.
4.7.3.2. RubyGems macros
The following table lists macros useful for packages created by RubyGems. These macros are provided by the rubygems-devel
packages.
Macro name | Extended path | Utilisation |
---|---|---|
%{gem_dir} | /usr/share/gems | Top directory for the gem structure. |
%{gem_instdir} | %{gem_dir}/gems/%{gem_name}-%{version} | Directory with the actual content of the gem. |
%{gem_libdir} | %{gem_instdir}/lib | The library directory of the gem. |
%{gem_cache} | %{gem_dir}/cache/%{gem_name}-%{version}.gem | The cached gem. |
%{gem_spec} | %{gem_dir}/specifications/%{gem_name}-%{version}.gemspec | The gem specification file. |
%{gem_docdir} | %{gem_dir}/doc/%{gem_name}-%{version} | The RDoc documentation of the gem. |
%{gem_extdir_mri} | %{_libdir}/gems/ruby/%{gem_name}-%{version} | The directory for gem extension. |
4.7.3.3. RubyGems SPEC file example
This section provides an example SPEC file for building gems together with an explanation of its particular sections.
An example RubyGems SPEC file
%prep %setup -q -n %{gem_name}-%{version} # Modify the gemspec if necessary # Also apply patches to code if necessary %patch0 -p1 %build # Create the gem as gem install only works on a gem file gem build ../%{gem_name}-%{version}.gemspec # %%gem_install compiles any C extensions and installs the gem into ./%%gem_dir # by default, so that we can move it into the buildroot in %%install %gem_install %install mkdir -p %{buildroot}%{gem_dir} cp -a ./%{gem_dir}/* %{buildroot}%{gem_dir}/ # If there were programs installed: mkdir -p %{buildroot}%{_bindir} cp -a ./%{_bindir}/* %{buildroot}%{_bindir} # If there are C extensions, copy them to the extdir. mkdir -p %{buildroot}%{gem_extdir_mri} cp -a .%{gem_extdir_mri}/{gem.build_complete,*.so} %{buildroot}%{gem_extdir_mri}/
The following table explains the specifics of particular items in a RubyGems SPEC file:
SPEC directive | RubyGems specifics |
---|---|
%prep |
RPM can directly unpack gem archives, so you can run the |
%build |
This directive includes commands or series of commands for building the software into machine code. The
The
The |
%install |
The installation is performed into the |
Ressources supplémentaires
4.7.3.4. Converting RubyGems packages to RPM SPEC files with gem2rpm
The gem2rpm
utility converts RubyGems packages to RPM SPEC files.
Les sections suivantes décrivent comment :
-
Install the
gem2rpm
utility -
Display all
gem2rpm
options -
Use
gem2rpm
to covert RubyGems packages to RPM SPEC files -
Edit
gem2rpm
templates
4.7.3.4.1. Installing gem2rpm
The following procedure describes how to install the gem2rpm
utility.
Procédure
-
To install
gem2rpm
from RubyGems.org, run:
$ gem install gem2rpm
4.7.3.4.2. Displaying all options of gem2rpm
The following procedure describes how to display all options of the gem2rpm
utility.
Procédure
To see all options of
gem2rpm
, run:gem2rpm --help
4.7.3.4.3. Using gem2rpm to covert RubyGems packages to RPM SPEC files
The following procedure describes how to use the gem2rpm
utility to covert RubyGems packages to RPM SPEC files.
Procédure
Download a gem in its latest version, and generate the RPM SPEC file for this gem:
$ gem2rpm --fetch <gem_name> > <gem_name>.spec
The described procedure creates an RPM SPEC file based on the information provided in the gem’s metadata. However, the gem misses some important information that is usually provided in RPMs, such as the license and the changelog. The generated SPEC file thus needs to be edited.
4.7.3.4.4. gem2rpm templates
The gem2rpm
template is a standard Embedded Ruby (ERB) file, which includes variables listed in the following table.
Variable | Explication |
---|---|
package |
The |
spécimen |
The |
config |
The |
runtime_dependencies |
The |
development_dependencies |
The |
tests |
The |
files |
The |
main_files |
The |
doc_files |
The |
format |
The |
4.7.3.4.5. Listing available gem2rpm templates
Use the following procedure describes to list all available gem2rpm
templates.
Procédure
To see all available templates, run:
$ gem2rpm --templates
4.7.3.4.6. Editing gem2rpm templates
You can edit the template from which the RPM SPEC file is generated instead of editing the generated SPEC file.
Use the following procedure to edit the gem2rpm
templates.
Procédure
Save the default template:
$ gem2rpm -T > rubygem-<gem_name>.spec.template
- Edit the template as needed.
Generate the SPEC file using the edited template:
$ gem2rpm -t rubygem-<gem_name>.spec.template <gem_name>-<latest_version.gem > <gem_name>-GEM.spec
You can now build an RPM package using the edited template as described in Building RPMs.
4.8. How to handle RPM packages with Perls scripts
Since RHEL 8, the Perl programming language is not included in the default buildroot. Therefore, the RPM packages that include Perl scripts must explicitly indicate the dependency on Perl using the BuildRequires:
directive in RPM SPEC file.
4.8.2. Using a specific Perl module
If a specific Perl module is required at build time, use the following procedure:
Procédure
Apply the following syntax in your RPM SPEC file:
BuildRequires: perl(MODULE)
NoteApply this syntax to Perl core modules as well, because they can move in and out of the
perl
package over time.
4.8.3. Limiting a package to a specific Perl version
To limit your package to a specific Perl version, follow this procedure:
Procédure
Use the
perl(:VERSION)
dependency with the desired version constraint in your RPM SPEC file:For example, to limit a package to Perl version 5.30 and higher, use:
BuildRequires: perl(:VERSION) >= 5.30
Do not use a comparison against the version of the perl
package because it includes an epoch number.
4.8.4. Ensuring that a package uses the correct Perl interpreter
Red Hat provides multiple Perl interpreters, which are not fully compatible. Therefore, any package that delivers a Perl module must use at run time the same Perl interpreter that was used at build time.
To ensure this, follow the procedure below:
Procédure
Include versioned
MODULE_COMPAT
Requires
in RPM SPEC file for any package that delivers a Perl module:Requires: perl(:MODULE_COMPAT_%(eval `perl -V:version`; echo $version))
Chapitre 5. New features in RHEL 9
This section documents the most notable changes in RPM packaging between Red Hat Enterprise Linux 8 and 9.
5.1. Dynamic build dependencies
Red Hat Enterprise Linux 9 introduces the %generate_buildrequires
section that enables generating dynamic build dependencies.
Additional build dependencies can now be generated programmatically at RPM build time, using the newly available %generate_buildrequires
script. This is useful when packaging software written in a language in which a specialized utility is commonly used to determine run-time or build-time dependencies, such as Rust, Golang, Node.js, Ruby, Python, or Haskell.
You can use the %generate_buildrequires
script to dynamically determine which BuildRequires
directives are added to a SPEC file at build-time. If present, %generate_buildrequires
is executed after the %prep
section and can access the unpacked and patched source files. The script must print the found build dependencies to standard output using the same syntax as a regular BuildRequires
directive.
The rpmbuild
utility then checks if the dependencies are met before continuing the build.
If some dependencies are missing, a package with the .buildreqs.nosrc.rpm
suffix is created, which contains the found BuildRequires
and no source files. You can use this package to install the missing build dependencies with the dnf builddep
command before restarting the build.
For more information, see the DYNAMIC BUILD DEPENDENCIES
section in the rpmbuild(8)
man page.
Ressources supplémentaires
-
rpmbuild(8)
man page -
yum-builddep(1)
man page
5.2. Improved patch declaration
5.2.1. En option, numérotation automatique des patchs et des sources
The Patch:
and Source:
tags without a number are now automatically numbered based on the order in which they are listed.
The numbering is run internally by the rpmbuild
utility starting from the last manually numbered entry, or 0
if there is no such entry.
Par exemple :
Patch: one.patch Patch: another.patch Patch: yet-another.patch
5.2.2. %patchlist
et %sourcelist
sections
It is now possible to list patch and source files without preceding each item with the respective Patch:
and Source:
tags by using the newly added %patchlist
and %sourcelist
sections.
For example, the following entries:
Patch0: one.patch Patch1: another.patch Patch2: yet-another.patch
can now be replaced with:
%patchlist one.patch another.patch yet-another.patch
5.2.3. topatch
accepte désormais les plages de correctifs
The %autopatch
macro now accepts the -m
and -M
parameters to limit the minimum and maximum patch number to apply, respectively:
-
The
-m
parameter specifies the patch number (inclusive) to start at when applying patches. -
The
-M
parameter specifies the patch number (inclusive) to stop at when applying patches.
This feature can be useful when an action needs to be performed in between certain patch sets.
5.3. Autres caractéristiques
Other new features related to RPM packaging in Red Hat Enterprise Linux 9 include:
- Générateurs de dépendances rapides basés sur des macros
-
Powerful macro and
%if
expressions, including ternary operator and native version comparison - Méta-dépendances (non ordonnées)
-
Caret version operator (
^
), which can be used to express a version that is higher than the base version. This operator complements the tilde (~
) operator, which has the opposite semantics. -
%enif
,%enifos
et%enifarch
déclarations
Chapitre 6. Ressources supplémentaires
This section provides references to various topics related to RPMs, RPM packaging, and RPM building.