RPM Packaging Guide
Basic and advanced software packaging scenarios using the RPM package manager
Abstract
Chapter 1. Getting started with RPM packaging Copy linkLink copied to clipboard!
The following section introduces the concept of RPM packaging and its main advantages.
1.1. Introduction to RPM packaging Copy linkLink copied to clipboard!
The RPM Package Manager (RPM) is a package management system that runs on RHEL, CentOS, and Fedora. You can use RPM to distribute, manage, and update software that you create for any of the operating systems mentioned above.
1.2. RPM advantages Copy linkLink copied to clipboard!
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 Yum 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
Yumrepositories. - Digitally sign your packages by using GNU Privacy Guard (GPG) signing keys.
1.3. Creating your first rpm package Copy linkLink copied to clipboard!
Creating an RPM package can be complicated. Here is a complete, working RPM Spec file with several things skipped and simplified.
Save this file as hello-world.spec.
Now use these commands:
rpmdev-setuptree rpmbuild -ba hello-world.spec
$ rpmdev-setuptree
$ rpmbuild -ba hello-world.spec
The command rpmdev-setuptree creates several working directories. As those directories are stored permanently in $HOME, this command does not need to be used again.
The command rpmbuild creates the actual rpm package. The output of this command can be similar to:
The file /home/<username>/rpmbuild/RPMS/x86_64/hello-world-1-1.x86_64.rpm is your first RPM package. It can be installed in the system and tested.
Chapter 2. Preparing software for RPM packaging Copy linkLink copied to clipboard!
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 source code is and How programs are made.
2.1. What source code is Copy linkLink copied to clipboard!
This part explains what source code is 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.
2.1.1. Source code examples Copy linkLink copied to clipboard!
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.
2.1.1.1. Hello World written in bash Copy linkLink copied to clipboard!
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"
#!/bin/bash
printf "Hello World\n"
2.1.1.2. Hello World written in Python Copy linkLink copied to clipboard!
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")
#!/usr/bin/python3
print("Hello World")
2.1.1.3. Hello World written in C Copy linkLink copied to clipboard!
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:
2.2. How programs are made Copy linkLink copied to clipboard!
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 Copy linkLink copied to clipboard!
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 Copy linkLink copied to clipboard!
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 Copy linkLink copied to clipboard!
Raw-interpreted language programs do not need to be compiled and are directly executed by the interpreter.
2.2.2.2. Byte-compiled programs Copy linkLink copied to clipboard!
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 Copy linkLink copied to clipboard!
This part describes how to build software from source code.
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.
2.3.1. Natively Compiled Code Copy linkLink copied to clipboard!
This section shows how to build the cello.c program written in the C language into an executable.
cello.c
2.3.1.1. Manual building Copy linkLink copied to clipboard!
If you want to build the cello.c program manually, use this procedure:
Procedure
Invoke the C compiler from the GNU Compiler Collection to compile the source code into binary:
gcc -g -o cello cello.c
gcc -g -o cello cello.cCopy to Clipboard Copied! Toggle word wrap Toggle overflow Execute the resulting output binary
cello:./cello
$ ./cello Hello WorldCopy to Clipboard Copied! Toggle word wrap Toggle overflow
2.3.1.2. Automated building Copy linkLink copied to clipboard!
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:
Procedure
To set up automated building, create the
Makefilefile with the following content in the same directory ascello.c.Makefilecello: gcc -g -o cello cello.c clean: rm cello
cello: gcc -g -o cello cello.c clean: rm celloCopy to Clipboard Copied! Toggle word wrap Toggle overflow Note that the lines under
cello:andclean:must begin with a tab space.To build the software, run the
makecommand:make
$ make make: 'cello' is up to date.Copy to Clipboard Copied! Toggle word wrap Toggle overflow Since there is already a build available, run the
make cleancommand, and after run themakecommand again:make clean make
$ make clean rm cello $ make gcc -g -o cello cello.cCopy to Clipboard Copied! Toggle word wrap Toggle overflow NoteTrying to build the program after another build has no effect.
make
$ make make: 'cello' is up to date.Copy to Clipboard Copied! Toggle word wrap Toggle overflow Execute the program:
./cello
$ ./cello Hello WorldCopy to Clipboard Copied! Toggle word wrap Toggle overflow
You have now compiled a program both manually and using a build tool.
2.3.2. Interpreting code Copy linkLink copied to clipboard!
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.3.2.1. Byte-compiling code Copy linkLink copied to clipboard!
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")
#!/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:
Procedure
Byte-compile the
pello.pyfile:python -m compileall pello.py file pello.pyc
$ python -m compileall pello.py $ file pello.pyc pello.pyc: python 2.7 byte-compiledCopy to Clipboard Copied! Toggle word wrap Toggle overflow Execute the byte code in
pello.pyc:python pello.pyc
$ python pello.pyc Hello WorldCopy to Clipboard Copied! Toggle word wrap Toggle overflow
2.3.2.2. Raw-interpreting code Copy linkLink copied to clipboard!
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"
#!/bin/bash
printf "Hello World\n"
Programs written in shell scripting languages, like bash, are raw-interpreted.
Procedure
Make the file with source code executable and run it:
chmod +x bello ./bello
$ chmod +x bello $ ./bello Hello WorldCopy to Clipboard Copied! Toggle word wrap Toggle overflow
2.4. Patching software Copy linkLink copied to clipboard!
This section explains how to patch the 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.
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; see Section 3.2, “Working with SPEC files”.
This procedure shows how to create a patch from the original source code for cello.c.
Procedure
Preserve the original source code:
cp -p cello.c cello.c.orig
$ cp -p cello.c cello.c.origCopy to Clipboard Copied! Toggle word wrap Toggle overflow The
-poption is used to preserve mode, ownership, and timestamps.Modify
cello.cas needed:Copy to Clipboard Copied! Toggle word wrap Toggle overflow Generate a patch using the
diffutility:Copy to Clipboard Copied! Toggle word wrap Toggle overflow Lines starting with a
-are removed from the original source code and replaced with the lines that start with+.Using the
Nauroptions with thediffcommand is recommended because it fits the majority of usual use cases. However, in this particular case, only the-uoption 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 thatdiffclassifies as binaries are not ignored. -
-u(or-U NUMor--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
diffutility, see thediffmanual page.
-
Save the patch to a file:
diff -Naur cello.c.orig cello.c > cello-output-first-patch.patch
$ diff -Naur cello.c.orig cello.c > cello-output-first-patch.patchCopy to Clipboard Copied! Toggle word wrap Toggle overflow Restore the original
cello.c:cp cello.c.orig cello.c
$ cp cello.c.orig cello.cCopy to Clipboard Copied! Toggle word wrap Toggle overflow The original
cello.cmust be retained, because when an RPM is built, the original file is used, not the modified one. For more information, see Section 3.2, “Working with SPEC files”.
The following procedure shows how to patch cello.c using cello-output-first-patch.patch, built the patched program, and run it.
Redirect the patch file to the
patchcommand:patch < cello-output-first-patch.patch
$ patch < cello-output-first-patch.patch patching file cello.cCopy to Clipboard Copied! Toggle word wrap Toggle overflow Check that the contents of
cello.cnow reflect the patch:Copy to Clipboard Copied! Toggle word wrap Toggle overflow Build and run the patched
cello.c:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.5. Installing arbitrary artifacts Copy linkLink copied to clipboard!
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.
This section describes two common ways of placing Arbitrary Artifacts in the system:
2.5.1. Using the install command Copy linkLink copied to clipboard!
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.
Procedure
Run the
installcommand to place thebellofile into the/usr/bindirectory with permissions common for executable scripts:sudo install -m 0755 bello /usr/bin/bello
$ sudo install -m 0755 bello /usr/bin/belloCopy to Clipboard Copied! Toggle word wrap Toggle overflow As a result,
bellois now located in the directory that is listed in the$PATHvariable.Execute
bellofrom any directory without specifying its full path:cd ~ bello
$ cd ~ $ bello Hello WorldCopy to Clipboard Copied! Toggle word wrap Toggle overflow
2.5.2. Using the make install command Copy linkLink copied to clipboard!
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.
Procedure
Add the
installsection to theMakefile:MakefileCopy to Clipboard Copied! Toggle word wrap Toggle overflow 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
Makefilenot only to build software, but also to install it to the target system.Build and install the
cello.cprogram:make sudo make install
$ make gcc -g -o cello cello.c $ sudo make install install -m 0755 cello /usr/bin/celloCopy to Clipboard Copied! Toggle word wrap Toggle overflow As a result,
cellois now located in the directory that is listed in the$PATHvariable.Execute
cellofrom any directory without specifying its full path:cd ~ cello
$ cd ~ $ cello Hello WorldCopy to Clipboard Copied! Toggle word wrap Toggle overflow
2.6. Preparing source code for packaging Copy linkLink copied to clipboard!
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.
Procedure
Create a
LICENSEfile, and make sure that it includes the following content:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
Additional resources
- The code created in this section can be found here.
2.7. Putting source code into tarball Copy linkLink copied to clipboard!
This section describes how to put each of the three Hello World programs introduced in Section 2.1.1, “Source code examples” into a gzip-compressed tarball, which is a common way to release the software to be later packaged for distribution.
2.7.1. Putting the bello project into tarball Copy linkLink copied to clipboard!
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.
Prerequisites
Considering that this is version 0.1 of the program.
Procedure
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/
$ mkdir /tmp/bello-0.1 $ mv ~/bello /tmp/bello-0.1/ $ cp /tmp/LICENSE /tmp/bello-0.1/Copy to Clipboard Copied! Toggle word wrap Toggle overflow Create the archive for distribution and move it to the
~/rpmbuild/SOURCES/directory, which is the default directory where therpmbuildcommand stores the files for building packages:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
For more information about the example source code written in bash, see Section 2.1.1.1, “Hello World written in bash”.
2.7.2. Putting the pello project into tarball Copy linkLink copied to clipboard!
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.
Prerequisites
Considering that this is version 0.1.1 of the program.
Procedure
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/
$ mkdir /tmp/pello-0.1.2 $ mv ~/pello.py /tmp/pello-0.1.2/ $ cp /tmp/LICENSE /tmp/pello-0.1.2/Copy to Clipboard Copied! Toggle word wrap Toggle overflow Create the archive for distribution and move it to the
~/rpmbuild/SOURCES/directory, which is the default directory where therpmbuildcommand stores the files for building packages:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
For more information about the example source code written in Python, see Section 2.1.1.2, “Hello World written in Python”.
2.7.3. Putting the cello project into tarball Copy linkLink copied to clipboard!
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 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.
Prerequisites
Considering that this is version 1.0 of the program.
Procedure
Put all required files into a single directory:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Create the archive for distribution and move it to the
~/rpmbuild/SOURCES/directory, which is the default directory where therpmbuildcommand stores the files for building packages:Copy to Clipboard Copied! Toggle word wrap Toggle overflow Add the patch:
mv ~/cello-output-first-patch.patch ~/rpmbuild/SOURCES/
$ mv ~/cello-output-first-patch.patch ~/rpmbuild/SOURCES/Copy to Clipboard Copied! Toggle word wrap Toggle overflow
For more information about the example source code written in C, see Section 2.1.1.3, “Hello World written in C”.
Chapter 3. Packaging software Copy linkLink copied to clipboard!
3.1. RPM packages Copy linkLink copied to clipboard!
This section covers the basics of the RPM packaging format.
3.1.1. What an RPM is Copy linkLink copied to clipboard!
An RPM package is a file containing other files and their metadata (information about the files that are needed by the system).
Specifically, an RPM package consists of the cpio archive.
The cpio archive contains:
- Files
RPM header (package metadata)
The
rpmpackage manager uses this metadata to determine dependencies, where to install files, and other information.
Types of RPM packages
There are two types of RPM packages. Both types share the file format and tooling, but have different contents and serve different purposes:
Source RPM (SRPM)
An SRPM contains source code and a SPEC file, which describes how to build the source code into a binary RPM. Optionally, the patches to source code are included as well.
Binary RPM
A binary RPM contains the binaries built from the sources and patches.
3.1.2. Listing RPM packaging tool’s utilities Copy linkLink copied to clipboard!
The following procedures show how to list the utilities provided by the rpmdevtools package.
Prerequisites
To be able to use the RPM packaging tools, you need to install the rpmdevtools package, which provides several utilities for packaging RPMs.
yum install rpmdevtools
# yum install rpmdevtools
Procedure
List RPM packaging tool’s utilities:
rpm -ql rpmdevtools | grep bin
$ rpm -ql rpmdevtools | grep binCopy to Clipboard Copied! Toggle word wrap Toggle overflow
Additional information
- For more information on the above utilities, see their manual pages or help dialogs.
3.1.3. Setting up RPM packaging workspace Copy linkLink copied to clipboard!
This section describes how to set up a directory layout that is the RPM packaging workspace by using the rpmdev-setuptree utility.
Prerequisites
The rpmdevtools package must be installed on your system:
yum install rpmdevtools
# yum install rpmdevtools
Procedure
-
Run the
rpmdev-setuptreeutility:
The created directories serve these purposes:
| Directory | Purpose |
| 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.1.4. What a SPEC file is Copy linkLink copied to clipboard!
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.
3.1.4.1. Preamble Items Copy linkLink copied to clipboard!
The table below presents some of the directives that are used frequently in the Preamble section of the RPM SPEC file.
| SPEC Directive | Definition |
|---|---|
|
| 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.
Example 3.1. Querying rpm to provide the NVR information for the bash package
rpm -q bash
$ rpm -q bash
bash-4.2.46-34.el7.x86_64
Here, bash is the package name, 4.2.46 is the version, and 34.el7 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.1.4.2. Body Items Copy linkLink copied to clipboard!
The items used in the Body section of the RPM SPEC file are listed in the table below.
| SPEC Directive | Definition |
|---|---|
|
| 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 |
|
| Command or series of commands to test the software. This normally includes things such as unit tests. |
|
| The list of files that will be installed in the end user’s system. |
|
|
A record of changes that have happened to the package between different |
3.1.4.3. Advanced items Copy linkLink copied to clipboard!
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.1.5. BuildRoots Copy linkLink copied to clipboard!
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 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 6, the rpmbuild program has its own defaults. Overriding these defaults leads to several problems; hence, {RH} 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.1.6. RPM macros Copy linkLink copied to clipboard!
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}
$ rpm --eval %{_MACRO}
Evaluating the %{_bindir} and the %{_libexecdir} macros
rpm --eval %{_bindir}
rpm --eval %{_libexecdir}
$ 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 8.x machine
rpm --eval %{?dist}
# On a RHEL 8.x machine
$ rpm --eval %{?dist}
.el8
3.2. Working with SPEC files Copy linkLink copied to clipboard!
This section describes how to create and modify a SPEC file.
Prerequisites
This section uses the three example implementations of the Hello World! program that were described in Section 2.1.1, “Source code examples”.
Each of the programs is also fully described in the below table.
| 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.2.1. Ways to create a new SPEC file Copy linkLink copied to clipboard!
To package new software, you need to create a new SPEC file.
There are two to achieve this:
- Writing the new SPEC file manually from scratch
Use the
rpmdev-newspecutilityThis 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.
3.2.2. Creating a new SPEC file with rpmdev-newspec Copy linkLink copied to clipboard!
The following procedure shows how to create a SPEC file for each of the three aforementioned Hello World! programs using the rpmdev-newspec utility.
Procedure
Change to the
~/rpmbuild/SPECSdirectory and use therpmdev-newspecutility:Copy to Clipboard Copied! Toggle word wrap Toggle overflow The
~/rpmbuild/SPECS/directory now contains three SPEC files namedbello.spec,cello.spec, andpello.spec.
fd. Examine the files:
+
The rpmdev-newspec utility does not use guidelines or conventions specific to any particular Linux distribution. However, this document targets , 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.2.3. Modifying an original SPEC file for creating RPMs Copy linkLink copied to clipboard!
The following procedure shows how to modify the output SPEC file provided by rpmdev-newspec for creating the RPMs.
Prerequisites
Make sure that:
-
The source code of the particular program has been placed into the
~/rpmbuild/SOURCES/directory. -
The unpopulated SPEC file
~/rpmbuild/SPECS/<name>.specfile has been created by therpmdev-newspecutility.
Procedure
-
Open the output template of the
~/rpmbuild/SPECS/<name>.specfile provided by therpmdev-newspecutility: Populate the first section of the SPEC file:
The first section includes these directives that
rpmdev-newspecgrouped together:-
Name -
Version -
Release SummaryThe
Namewas already specified as an argument torpmdev-newspec.Set the
Versionto match the upstream release version of the source code.The
Releaseis 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. ResetReleaseto1when a new upstream release happens.The
Summaryis a short, one-line explanation of what this software is.
-
Populate the
License,URL, andSource0directives:The
Licensefield is the Software License associated with the source code from the upstream release. The exact format for how to label theLicensein your SPEC file will vary depending on which specific RPM based Linux distribution guidelines you are following.For example, you can use GPLv3+.
The
URLfield provides URL to the upstream software website. For consistency, utilize the RPM macro variable of%{name}, and use https://example.com/%{name}.The
Source0field 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,RequiresandBuildArchdirectives:BuildRequiresspecifies build-time dependencies for the package.Requiresspecifies run-time dependencies for the package.This is a software written in an interpreted programming language with no natively compiled extensions. Hence, add the
BuildArchdirective with thenoarchvalue. 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%licensedirectives: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
%descriptionis a longer, fuller description of the software thanSummary, containing one or more paragraphs.The
%prepsection 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 -qmacro.The
%buildsection specifies how to build the software.The
%installsection contains instructions forrpmbuildon how to install the software, once it has been built, into theBUILDROOTdirectory.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
%filessection 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 command[]
rpmcommand. For example, to indicate that the LICENSE file is a software license file, use the%licensemacro.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%buildsection.Follow this format for the first line:
Start with an
*character followed byDay-of-Week Month Day Year Name Surname <email> - Version-ReleaseFollow 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.
For examples of SPEC file written in different programming languages, see:
3.2.4. An example SPEC file for a program written in bash Copy linkLink copied to clipboard!
This section shows an example SPEC file for the bello program that was written in bash. For more information about bello, see Section 2.1.1, “Source code examples”.
An example SPEC file for the bello program written in bash
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.
3.2.5. An example SPEC file for a program written in Python Copy linkLink copied to clipboard!
This section shows an example SPEC file for the pello program written in the Python programming language. For more information about pello, see Section 2.1.1, “Source code examples”.
An example SPEC file for the pello program written in Python
The pello program is written in a byte-compiled interpreted language. Hence, the shebang is not applicable because the resulting file does not contain the entry.
Because the shebang is not applicable, you may want to apply one of the following approaches:
- Create a non-byte-compiled shell script that will call the executable.
- Provide a small bit of the Python code that is not byte-compiled as the entry point into the program’s execution.
These approaches are useful especially for large software projects with many thousands of lines of code, where the performance increase of pre-byte-compiled code is sizeable.
The BuildRequires directive, which specifies build-time dependencies for the package, includes two packages:
-
The
pythonpackage is needed to perform the byte-compile build process -
The
bashpackage is needed to execute the small entry-point script
The Requires directive, which specifies run-time dependencies for the package, includes only the python package. The pello program requires the python package to execute the byte-compiled code at runtime.
The %build section, which specifies how to build the software, corresponds to the fact that the software is byte-compiled.
To install pello, you need to create a wrapper script because the shebang is not applicable in byte-compiled languages. There are multiple options to accomplish this, such as:
-
Making a separate script and using that as a separate
SourceXdirective. - Creating the file in-line in the SPEC file.
This example shows creating a wrapper script in-line in the SPEC file to demonstrate that the SPEC file itself is scriptable. This wrapper script will execute the Python byte-compiled code by using a here document.
The %install section in this example also corresponds to the fact that you will need to install the byte-compiled file into a library directory on the system such that it can be accessed.
3.2.6. An example SPEC file for a program written in C Copy linkLink copied to clipboard!
This section shows an example SPEC file for the cello program that was written in the C programming language. For more information about cello, see Section 2.1.1, “Source code examples”.
An example SPEC file for the cello program written in C
The BuildRequires directive, which specifies build-time dependencies for the package, includes two packages that are needed to perform the compilation build process:
-
The
gccpackage -
The
makepackage
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.
3.3. Building RPMs Copy linkLink copied to clipboard!
This section describes how to build an RPM after a SPEC file for a program has been created.
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. This section describes the two main use cases:
- Building source RPMs
- Building binary RPMs
3.3.1. Building source RPMs Copy linkLink copied to clipboard!
This paragraph is the procedure module introduction: a short description of the procedure.
Prerequisites
A SPEC file for the program that we want to package must already exist. For more information on creating SPEC files, see Working with SPEC files.
Procedure
The following procedure describes how to build a source RPM.
Run the
rpmbuildcommand with the specified SPEC file:rpmbuild -bs SPECFILE
$ rpmbuild -bs SPECFILECopy to Clipboard Copied! Toggle word wrap Toggle overflow Substitute SPECFILE with the SPEC file. The
-bsoption 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.
Verification steps
-
Make sure that the
rpmbuild/SRPMSdirectory includes the resulting source RPMs. The directory is a part of the structure expected byrpmbuild.
3.3.2. Building binary RPMs Copy linkLink copied to clipboard!
The following methods are vailable for building binary RPMs:
- Rebuilding a binary RPM from a source RPM
- Building a binary RPM from the SPEC file
- Building a binary RPM from a source RPM
3.3.2.1. Rebuilding a binary RPM from a source RPM Copy linkLink copied to clipboard!
The following procedure shows how to rebuild a binary RPM from a source RPM (SRPM).
Procedure
To rebuild
bello,pello, andcellofrom their SRPMs, run:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
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
rpmbuildcommand with the--recompileoption instead of the--rebuildoption. Install the SRPMs using these commands:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
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.3.2.2. Building a binary RPM from the SPEC file Copy linkLink copied to clipboard!
The following procedure shows how to build bello, pello, and cello binary RPMs from their SPEC files.
Procedure
Run the
rpmbuildcommand with thebboption:rpmbuild -bb ~/rpmbuild/SPECS/bello.spec rpmbuild -bb ~/rpmbuild/SPECS/pello.spec rpmbuild -bb ~/rpmbuild/SPECS/cello.spec
$ rpmbuild -bb ~/rpmbuild/SPECS/bello.spec $ rpmbuild -bb ~/rpmbuild/SPECS/pello.spec $ rpmbuild -bb ~/rpmbuild/SPECS/cello.specCopy to Clipboard Copied! Toggle word wrap Toggle overflow
3.3.2.3. Building RPMs from source RPMs Copy linkLink copied to clipboard!
It is also possible to build any kind of RPM from a source RPM. To do so, use the following procedure.
Procedure
Run the
rpmbuildcommand with one of the below options and with the source package specified:rpmbuild {-ra|-rb|-rp|-rc|-ri|-rl|-rs} [rpmbuild-options] SOURCEPACKAGE# rpmbuild {-ra|-rb|-rp|-rc|-ri|-rl|-rs} [rpmbuild-options] SOURCEPACKAGECopy to Clipboard Copied! Toggle word wrap Toggle overflow
Additional resources
For more details on building RPMs from source RPMs, see the BUILDING PACKAGES section on the rpmbuild(8) man page.
3.4. Checking RPMs for sanity Copy linkLink copied to clipboard!
After creating a package, 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 examples.
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 following examples, 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.4.1. Checking bello for sanity Copy linkLink copied to clipboard!
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.4.1.1. Checking the bello SPEC File Copy linkLink copied to clipboard!
Example 3.2. Output of running the rpmlint command on the SPEC file for bello
rpmlint bello.spec
$ 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.
Example 3.3. Output of running the rpmlint command on the SRPM for bello
rpmlint ~/rpmbuild/SRPMS/bello-0.1-1.el8.src.rpm
$ 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.4.1.2. Checking the bello binary RPM Copy linkLink copied to clipboard!
When checking binary RPMs, rpmlint checks for the following items:
- Documentation
- Manual pages
- Consistent use of the filesystem hierarchy standard
Example 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
$ 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 the RPM has no documentation or manual pages, because we did not provide any. Apart from the above warnings, the RPM passed rpmlint checks.
3.4.2. Checking pello for sanity Copy linkLink copied to clipboard!
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.4.2.1. Checking the pello SPEC File Copy linkLink copied to clipboard!
Example 3.5. Output of running the rpmlint command on the SPEC file for pello
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.
Example 3.6. Output of running the rpmlint command on the SRPM for pello
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.4.2.2. Checking the pello binary RPM Copy linkLink copied to clipboard!
When checking binary RPMs, rpmlint checks for the following items:
- Documentation
- Manual pages
- Consistent use of the Filesystem Hierarchy Standard
Example 3.7. Output of running the rpmlint command on the binary RPM for pello
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.4.3. Checking cello for sanity Copy linkLink copied to clipboard!
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.4.3.1. Checking the cello SPEC File Copy linkLink copied to clipboard!
Example 3.8. Output of running the rpmlint command on the SPEC file for cello
rpmlint ~/rpmbuild/SPECS/cello.spec
$ rpmlint ~/rpmbuild/SPECS/cello.spec
/home/<username>/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.
Example 3.9. Output of running the rpmlint command on the SRPM for cello
rpmlint ~/rpmbuild/SRPMS/cello-1.0-1.el8.src.rpm
$ 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.4.3.2. Checking the cello binary RPM Copy linkLink copied to clipboard!
When checking binary RPMs, rpmlint checks for the following items:
- Documentation
- Manual pages
- Consistent use of the filesystem hierarchy standard
Example 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
$ 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.
Chapter 4. Advanced topics Copy linkLink copied to clipboard!
This section covers topics that are beyond the scope of the introductory tutorial but are useful in real-world RPM packaging.
4.1. Signing packages Copy linkLink copied to clipboard!
Packages are signed to make sure no third party can alter their content. A user can add an additional layer of security by using the HTTPS protocol when downloading the package.
There are three ways to sign a package:
4.1.1. Creating a GPG key Copy linkLink copied to clipboard!
Procedure
Generate a GNU Privacy Guard (GPG) key pair:
gpg --gen-key
# gpg --gen-keyCopy to Clipboard Copied! Toggle word wrap Toggle overflow Confirm and see the generated key:
gpg --list-keys
# gpg --list-keysCopy to Clipboard Copied! Toggle word wrap Toggle overflow Export the public key:
gpg --export -a '<Key_name>' > RPM-GPG-KEY-pmanager
# gpg --export -a '<Key_name>' > RPM-GPG-KEY-pmanagerCopy to Clipboard Copied! Toggle word wrap Toggle overflow NoteInclude the real name that you have selected for the key instead of <Key_name>.
Import the exported public key into an RPM database:
rpm --import RPM-GPG-KEY-pmanager
# rpm --import RPM-GPG-KEY-pmanagerCopy to Clipboard Copied! Toggle word wrap Toggle overflow
4.1.2. Adding a signature to an already existing package Copy linkLink copied to clipboard!
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 a package, use the --addsign option provided by the rpm-sign package.
Having more than one signature enables to record the package’s path of ownership from the package builder to the end-user.
Procedure
Add a signature to a package:
rpm --addsign blather-7.9-1.x86_64.rpm
$ rpm --addsign blather-7.9-1.x86_64.rpmCopy to Clipboard Copied! Toggle word wrap Toggle overflow NoteYou are supposed to enter the password to unlock the secret key for the signature.
4.1.3. Checking the signatures of a package with multiple signatures Copy linkLink copied to clipboard!
Procedure
To check the signatures of a package with multiple signatures, run the following:
rpm --checksig blather-7.9-1.x86_64.rpm
$ rpm --checksig blather-7.9-1.x86_64.rpm blather-7.9-1.x86_64.rpm: size pgp pgp md5 OKCopy to Clipboard Copied! Toggle word wrap Toggle overflow The two
pgpstrings in the output of therpm --checksigcommand show that the package has been signed twice.
4.1.4. A practical example of adding a signature to an already existing package Copy linkLink copied to clipboard!
This section describes an example situation where adding a signature to an already existing package might be useful.
A division of a company creates a package and signs it with the division’s key. The company’s headquarters then checks the package’s signature and adds the corporate signature to the package, stating that the signed package is authentic.
With two signatures, the package makes its way to a retailer. The retailer checks the signatures and, if they match, adds their signature as well.
The package now makes its way to a company that wants to deploy the package. After checking every signature on the package, they know that it is an authentic copy. Depending on the deploying company’s internal controls, they may choose to add their own signature, to inform their employees that the package has received their corporate approval
4.1.5. Replacing the signature on an already existing package Copy linkLink copied to clipboard!
This procedure describes how to change the public key without having to rebuild each package.
Procedure
To change the public key, run the following:
rpm --resign blather-7.9-1.x86_64.rpm
$ rpm --resign blather-7.9-1.x86_64.rpmCopy to Clipboard Copied! Toggle word wrap Toggle overflow NoteYou are supposed to enter the password to unlock the secret key for the signature.
The --resign option also enables you to change the public key for multiple packages, as shown in the following procedure.
Procedure
To change the public key for multiple packages, execute:
rpm --resign b*.rpm
$ rpm --resign b*.rpmCopy to Clipboard Copied! Toggle word wrap Toggle overflow NoteYou are supposed to enter the password to unlock the secret key for the signature.
4.1.6. Signing a package at build-time Copy linkLink copied to clipboard!
Procedure
Build the package with the
rpmbuildcommand:rpmbuild blather-7.9.spec
$ rpmbuild blather-7.9.specCopy to Clipboard Copied! Toggle word wrap Toggle overflow Sign the package with the
rpmsigncommand using the--addsignoption:rpmsign --addsign blather-7.9-1.x86_64.rpm
$ rpmsign --addsign blather-7.9-1.x86_64.rpmCopy to Clipboard Copied! Toggle word wrap Toggle overflow - Optionally, verify the signature of a package:
rpm --checksig blather-7.9-1.x86_64.rpm
$ rpm --checksig blather-7.9-1.x86_64.rpm
blather-7.9-1.x86_64.rpm: size pgp md5 OK
When building and signing multiple packages, use the following syntax to avoid entering the Pretty Good Privacy (PGP) passphrase multiple times.
rpmbuild -ba --sign b*.spec
$ rpmbuild -ba --sign b*.spec
Note that you are supposed to enter the password to unlock the secret key for the signature.
4.2. More on macros Copy linkLink copied to clipboard!
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 Copy linkLink copied to clipboard!
The following section describes how to create a custom macro.
Procedure
Include the following line in the RPM SPEC file:
%global <name>[(opts)] <body>
%global <name>[(opts)] <body>Copy to Clipboard Copied! Toggle word wrap Toggle overflow
All whitespace surrounding \ 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:
-
Simplemacros do not contain the(opts)field. In this case, only recursive macro expansion is performed. -
Parametrizedmacros contain the(opts)field. Theoptsstring between parentheses is passed togetopt(3)forargc/argvprocessing 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:
-
%definehas local scope. It applies to a specific part of a SPEC file. The body of a%definemacro is expanded when used. -
%globalhas global scope. It applies to an entire SPEC file. The body of a%globalmacro 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.
Additional resources
For comprehensive information on macros capabilities, see RPM Documentation.
4.2.2. Using the %setup macro Copy linkLink copied to clipboard!
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.
Example 4.1. Example %setup macro output
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.DhddsG
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:
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 Copy linkLink copied to clipboard!
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 Copy linkLink copied to clipboard!
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
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 Copy linkLink copied to clipboard!
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'
/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 Copy linkLink copied to clipboard!
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'
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 -
/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 Copy linkLink copied to clipboard!
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
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
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 Copy linkLink copied to clipboard!
This section lists advanced RPM Macros that are needed in the %files section of a SPEC file.
| Macro | Definition |
|---|---|
| %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 Copy linkLink copied to clipboard!
provides multiple built-in RPM macros.
Procedure
To display all built-in RPM macros, run:
rpm --showrc
rpm --showrcCopy to Clipboard Copied! Toggle word wrap Toggle overflow NoteThe output is quite sizeable. To narrow the result, use the command above with the
grepcommand.To find information about the RPMs macros for your system’s version of RPM, run:
rpm -ql rpm
rpm -ql rpmCopy to Clipboard Copied! Toggle word wrap Toggle overflow NoteRPM macros are the files titled
macrosin the output directory structure.
4.2.5. RPM distribution macros Copy linkLink copied to clipboard!
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 yum package manager.
Once installed, the macro files can be found in the /usr/lib/rpm/macros.d/ directory.
To display the raw RPM macro definitions, run:
rpm --showrc
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}
rpm --eval %{_MACRO}
For more information, see the rpm man page.
4.2.5.1. Creating custom macros Copy linkLink copied to clipboard!
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.
To override a macro, run :
%_topdir /opt/some/working/directory/rpmbuild
%_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
%_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 Copy linkLink copied to clipboard!
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 Copy linkLink copied to clipboard!
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 YUM 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.
Example 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 Copy linkLink copied to clipboard!
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.
4.3.2.1. Scriptlets directives Copy linkLink copied to clipboard!
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 | Definition |
|---|---|
|
| 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.2.2. Turning off a scriptlet execution Copy linkLink copied to clipboard!
To turn off the execution of any scriptlet, use the rpm command together with the --no_scriptlet_name_ option.
Procedure
For example, to turn off the execution of the
%pretransscriptlets, run:rpm --nopretrans
# rpm --nopretransCopy to Clipboard Copied! Toggle word wrap Toggle overflow You can also use the
-- noscriptsoption, which is equivalent to all of the following:-
--nopre -
--nopost -
--nopreun -
--nopostun -
--nopretrans -
--noposttrans
-
Additional resources
-
For more details, see the
rpm(8)man page.
4.3.2.3. Scriptlets macros Copy linkLink copied to clipboard!
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.
4.3.3. The Triggers directives Copy linkLink copied to clipboard!
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, {RH} recommends to minimize the use of Triggers.
The order of execution and the details for each existing Triggers are listed below:
The above items are found in the /usr/share/doc/rpm-4.*/triggers file.
4.3.4. Using non-shell scripts in a SPEC file Copy linkLink copied to clipboard!
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:
Procedure
-
Open the
pello.specfile. Find the following line:
install -m 0644 %{name}.py* %{buildroot}/usr/lib/%{name}/install -m 0644 %{name}.py* %{buildroot}/usr/lib/%{name}/Copy to Clipboard Copied! Toggle word wrap Toggle overflow Under the above line, insert:
%post -p /usr/bin/python3 print("This is {} code".format("python"))%post -p /usr/bin/python3 print("This is {} code".format("python"))Copy to Clipboard Copied! Toggle word wrap Toggle overflow Install your package:
yum install /home/<username>/rpmbuild/RPMS/noarch/pello-0.1.2-1.el8.noarch.rpm
# yum install /home/<username>/rpmbuild/RPMS/noarch/pello-0.1.2-1.el8.noarch.rpmCopy to Clipboard Copied! Toggle word wrap Toggle overflow 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
Installing : pello-0.1.2-1.el8.noarch 1/1 Running scriptlet: pello-0.1.2-1.el8.noarch 1/1 This is python codeCopy to Clipboard Copied! Toggle word wrap Toggle overflow
To use a Python 3 script, include the following line under install -m in a SPEC file:
%post -p /usr/bin/python3
%post -p /usr/bin/python3
To use a Lua script, include the following line under install -m in a SPEC file:
%post -p <lua>
%post -p <lua>
This way, you can specify any interpreter in a SPEC file.
4.4. RPM conditionals Copy linkLink copied to clipboard!
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 Copy linkLink copied to clipboard!
RPM conditionals use the following syntax:
If expression is true, then do some action:
%if expression … %endif
%if expression
…
%endif
If expression is true, then do some action, in other case, do another action:
%if expression … %else … %endif
%if expression
…
%else
…
%endif
4.4.2. RPM conditionals examples Copy linkLink copied to clipboard!
This section provides multiple examples of RPM conditionals.
4.4.2.1. The %if conditionals Copy linkLink copied to clipboard!
Example 4.3. Using the %if conditional to handle compatibility between 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
%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.
Example 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
%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.2.2. Specialized variants of %if conditionals Copy linkLink copied to clipboard!
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.
4.4.2.2.1. The %ifarch conditional Copy linkLink copied to clipboard!
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.
Example 4.5. An example use of the %ifarch conditional
%ifarch i386 sparc … %endif
%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.
4.4.2.2.2. The %ifnarch conditional Copy linkLink copied to clipboard!
The %ifnarch conditional has a reverse logic than %ifarch conditional.
Example 4.6. An example use of the %ifnarch conditional
%ifnarch alpha … %endif
%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.
4.4.2.2.3. The %ifos conditional Copy linkLink copied to clipboard!
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.
Example 4.7. An example use of the %ifos conditional
%ifos linux … %endif
%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.
Appendix A. New features of RPM in RHEL 7 Copy linkLink copied to clipboard!
This list documents most noticable changes in RPM packaging between Red Hat Enterprise Linux 6 and 7.
-
A new command,
rpmkeys, used for keyring import and signature verification has been added. -
A new command,
rpmspec, used for spec queries and parsed output has been added. -
A new command,
rpmsign, used for package signing has been added. -
The
posix.exec()andos.exit()extensions embedded in%{lua:…}scripts fail the script unless called from a child process created with theposix.fork()scriptlet. -
The
%pretransscriptlet failure causes the package installation to be skipped. - Scriptlets can be macro-expanded and queryformat-expanded at runtime.
-
Pre-transaction and post-transaction scriptlet dependencies can now be correctly expressed with
Requires(pretrans)andRequires(posttrans)scriptlets. -
The
OrderWithRequirestag for supplying additional ordering hints has been added. The tag followsRequirestag syntax, but does not generate actual dependencies. The ordering hints are treated as if they wereRequireswhen calculating the transaction order, only if the involved packages are present in the same transaction. -
The
%licenseflag can be used in the%filessection. This flag can be used similar to the%docflag to mark files as licenses, which need to be installed despite the--nodocsoption. -
The
%autosetupmacro for automating patch application, with optional distributed version control system integration has been added. - The automatic dependency generator has been rewritten into extensible and customizable rule based system with built-in filtering.
- The OpenPGP V3 public keys are no longer supported.
Chapter 5. Additional resources about RPM packaging Copy linkLink copied to clipboard!
This section provides references to various topics related to RPMs, RPM packaging, and RPM building. Some of these are advanced and extend the introductory material included in this documentation.
Red Hat Software Collections Overview - The Red Hat Software Collections offering provides continuously updated development tools in latest stable versions.
Red Hat Software Collections - The Packaging Guide provides an explanation of Software Collections and details how to build and package them. Developers and system administrators with basic understanding of software packaging with RPM can use this Guide to get started with Software Collections.
Mock - Mock provides a community-supported package building solution for various architectures and different Fedora or RHEL versions than has the build host.
RPM Documentation - The official RPM documentation.
Fedora Packaging Guidelines - The official packaging guidelines for Fedora, useful for all RPM-based distributions.