Movatterモバイル変換


[0]ホーム

URL:


RPM Packaging Guide

Adam Miller, Maxim Svistunov, Marie Doleželová, et al.
Table of Contents

Introduction

The RPM Packaging Guide documents:

How to prepare source code for packaging into an RPM.

This is for people withno background in software development. SeePreparing Software for Packaging.

How to package source code into an RPM.

This is for software developers whoneed to package software into RPMs. SeePackaging Software.

Advanced packaging scenarios.

This is a reference material for RPM packagersdealing with advanced RPM Packaging scenarios. SeeAdvanced Topics.

PDF Version

You can also download aPDF version ofthis document.

Document Conventions

The document uses the following conventions:

  • Command output and contents of text files, including source code, are placedin blocks:

    $ tree ~/rpmbuild//home/user/rpmbuild/|-- BUILD|-- RPMS[command output trimmed]
    Name:           belloVersion:Release:        1%{?dist}Summary:[file contents trimmed]
    #!/usr/bin/env pythonprint("Hello World")
  • Topics of interest or vocabulary terms are referred to either as URLs to theirrespective documentation or website, inbold, or initalics.The first occurrences of some terms link to their respective documentation.

  • Names of utilities, commands, and things normally found in code are written inmonospace font.

Contributing to this guide

You can contribute to this guide by submitting an issue or a pull request onthe GitHub repository.

Both forms of contribution are greatly appreciated and welcome.

Feel free to file an issue ticket with feedback, submit a pull requeston GitHub, or both!

Prerequisites

To follow this tutorial, you need the packages mentioned below.

Note
Some of these packages are installed by default onFedora,CentOS andRHEL. They are listedexplicitly to show which tools are used in this guide.

On Fedora, CentOS 8, and RHEL 8:

$ dnf install gcc rpm-build rpm-devel rpmlint make python bash coreutils diffutils patch rpmdevtools

On CentOS 7 and RHEL 7:

$ yum install gcc rpm-build rpm-devel rpmlint make python bash coreutils diffutils patch rpmdevtools

Why Package Software with RPM?

The RPM Package Manager (RPM) is a package management system that runs on RedHat Enterprise Linux, CentOS, and Fedora. RPM makes it easier for you todistribute, manage, and update software that you create for Red Hat EnterpriseLinux, CentOS, and Fedora. Many software vendors distribute their software via aconventional archive file (such as a tarball). However, there are severaladvantages in packaging software into RPM packages. These advantages areoutlined below.

With RPM, you can:

Install, reinstall, remove, upgrade and verify packages

Users can use standard package management tools (for example Yum or PackageKit)to install, reinstall, remove, upgrade and verify your RPM packages.

Use a database of installed packages to query and verify packages

Because RPM maintains a database of installed packages and their files, userscan easily query and verify packages on their system.

Use metadata to describe packages, their installation instructions, and so on

Each RPM package includes metadata that describes the package’s components,version, release, size, project URL, installation instructions, and so on.

Package pristine software sources into source and binary packages

RPM allows you to take pristine software sources and package them into sourceand binary packages for your users. In source packages, you have the pristinesources along with any patches that were used, plus complete build instructions.This design eases the maintenance of the packages as new versions of yoursoftware are released.

Add packages to Yum repositories

You can add your package to a Yum repository that enables clients to easily findand deploy your software.

Digitally sign your packages

Using a GPG signing key, you can digitally sign your package so that users areable to verify the authenticity of the package.

Your First RPM Package

Creating an RPM package can be complicated. Here is a complete, working RPM Specfile with several things skipped and simplified.

Name:       hello-worldVersion:    1Release:    1Summary:    Most simple RPM packageLicense:    FIXME%descriptionThis is my first RPM package, which does nothing.%prep# we have no source, so nothing here%buildcat > hello-world.sh <<EOF#!/usr/bin/bashecho Hello worldEOF%installmkdir -p %{buildroot}/usr/bin/install -m 755 hello-world.sh %{buildroot}/usr/bin/hello-world.sh%files/usr/bin/hello-world.sh%changelog# let's skip this for now

Save this file ashello-world.spec.

Now use these commands:

$ rpmdev-setuptree$ rpmbuild -ba hello-world.spec

The commandrpmdev-setuptree creates several working directories. As thosedirectories are stored permanently in $HOME, this command does not need to beused again.

The commandrpmbuild creates the actual rpm package. The output of thiscommand can be similar to:

... [SNIP]Wrote: /home/mirek/rpmbuild/SRPMS/hello-world-1-1.src.rpmWrote: /home/mirek/rpmbuild/RPMS/x86_64/hello-world-1-1.x86_64.rpmExecuting(%clean): /bin/sh -e /var/tmp/rpm-tmp.wgaJzv+ umask 022+ cd /home/mirek/rpmbuild/BUILD+ /usr/bin/rm -rf /home/mirek/rpmbuild/BUILDROOT/hello-world-1-1.x86_64+ exit 0

The file/home/mirek/rpmbuild/RPMS/x86_64/hello-world-1-1.x86_64.rpm is yourfirst RPM package. It can be installed in the system and tested.

Preparing Software for Packaging

This chapter is about source code and creating software, which are a necessarybackground for an RPM Packager.

What is Source Code?

Source code is human-readable instructions to the computer, which describehow to perform a computation. Source code is expressed using aprogramming language.

This tutorial features three versions of theHello World program, eachwritten in a different programming language. Programs written in these threedifferent languages are packaged differently, and cover three major use cases ofan RPM packager.

Note
There are thousands of programming languages. This document features onlythree of them, but they are enough for a conceptual overview.

Hello World written inbash:

bello

#!/bin/bashprintf "Hello World\n"

Hello World written inPython:

pello.py

#!/usr/bin/env pythonprint("Hello World")

Hello World written inC:

cello.c

#include<stdio.h>int main(void) {    printf("Hello World\n");return0;}

The purpose of every one of the three programs is to outputHello World onthe command line.

Note
Knowing how to program is not necessary for a software packager, but ishelpful.

How Programs Are Made

There are many methods by which human-readable source code becomes machine code- instructions the computer follows to actually execute the program. However,all methods can be reduced to these three:

  1. The program is natively compiled.

  2. The program is interpreted by raw interpreting.

  3. The program is interpreted by byte compiling.

Natively Compiled Code

Natively compiled software is software written in a programming languagethat compiles to machine code, with a resulting binary executable file. Suchsoftware can be run stand-alone.

RPM packages built this way arearchitecture-specific. This means that if you compile such software on a computer that usesa 64-bit (x86_64) AMD or Intel processor, itwill not execute on a 32-bit (x86) AMD or Intel processor. The resulting packagewill have architecture specified in its name.

Interpreted Code

Some programming languages, such asbash orPython, do not compile to machine code. Instead, theirprograms' source code is executed step by step, without prior transformations,by aLanguage Interpreteror a Language Virtual Machine.

Software written entirely in interpreted programming languages is notarchitecture-specific. Hence, the resulting RPM Package will have stringnoarch in its name.

Interpreted languages are eitherbyte-compiled orraw-interpreted. These twotypes differ in program build process and in packaging procedure.

Raw-interpreted programs

Raw-interpreted language programs do not need to be compiled at all, they aredirectly executed by the interpreter.

Byte-compiled programs

Byte-compiled languages need to be compiled into byte code, which is thenexecuted by the language virtual machine.

Note
Some languages give a choice: they can be raw-interpreted or byte-compiled.

Building Software from Source

This section explains building software from its source code.

  • For software written in compiled languages, the source code goes through abuild process, producing machine code. This process, commonly calledcompiling ortranslating, varies for different languages. The resultingbuilt software can berun or "executed", which makes computer performthe task specified by the programmer.

  • For software written in raw interpreted languages, the source code is notbuilt, but executed directly.

  • For software written in byte-compiled interpreted languages, the source codeis compiled into byte code, which is then executed by the language virtualmachine.

Natively Compiled Code

In this example, you will build thecello.c program written in theClanguage into an executable.

cello.c

#include<stdio.h>int main(void) {    printf("Hello World\n");return0;}
Manual Building

Invoke theCcompiler from the GNUCompiler Collection (GCC) to compile the source code into binary:

gcc -g -o cello cello.c

Execute the resulting output binarycello.

$ ./celloHello World

That is all. You have built and ran natively compiled software from source code.

Automated Building

Instead of building the source code manually, you can automate the building.This is a common practice used by large-scale software. Automating building isdone by creating aMakefile and then running theGNUmake utility.

To set up automated building, create a file namedMakefile in the samedirectory ascello.c:

Makefile

cello:        gcc -g -o cello cello.cclean:        rm cello

Now to build the software, simply runmake:

$ makemake: 'cello' is up to date.

Since there is already a build present,make clean it and runmake again:

$ make cleanrm cello$ makegcc -g -o cello cello.c

Again, trying to build after another build would do nothing:

$ makemake: 'cello' is up to date.

Finally, execute the program:

$ ./celloHello World

You have now compiled a program both manually and using a build tool.

Interpreted Code

The next two examples showcase byte-compiling a program written inPython and raw-interpreting a program written inbash.

Note

In the two examples below, the#! line at the top of the file is known as ashebangand is not part of the programming language source code.

Theshebangenables using a text file as an executable: the systemprogram loader parses the line containing theshebang to get a path to thebinary executable, which is then used as the programming language interpreter.

Byte-Compiled Code

In this example, you will compile thepello.py program written in Pythoninto 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 isfaster. Hence, RPM Packagers prefer to package the byte-compiled version fordistribution to end users.

pello.py

#!/usr/bin/env pythonprint("Hello World")

Procedure for byte-compiling programs is different for different languages. Itdepends on the language, the language’s virtual machine, and the tools andprocesses used with that language.

Note
Python is often byte-compiled, but not in the waydescribed here. The following procedure aims not to conform to the communitystandards, but to be simple. For real-world Python guidelines, seeSoftware Packaging andDistribution.

Byte-compilepello.py:

$ python -m compileall pello.py$ file pello.pycpello.pyc: python 2.7 byte-compiled

Execute the byte code inpello.pyc:

$ python pello.pycHello World
Raw Interpreted Code

In this example, you will raw-interpret thebello program written in thebash shell built-in language.

bello

#!/bin/bashprintf "Hello World\n"

Programs written in shell scripting languages, likebash, are raw-interpreted.Hence, you only need to make the file with source code executable and run it:

$ chmod +x bello$ ./belloHello World

Patching Software

Apatch is source code that updates other source code. It is formatted as adiff, because it represents what is different between two versions of text.Adiff is created using thediff utility, which is then applied to thesource code using thepatch utility.

Note
Software developers often use Version Control Systems such asgit to manage their code base. Such tools provide theirown methods of creating diffs or patching software.

In the following example, we create a patch from the original source code usingdiff and then apply it usingpatch. Patching is used in a later sectionwhen creating an RPM,Working with SPEC files.

How is patching related to RPM packaging? In packaging, instead of simplymodifying the original source code, we keep it, and use patches on it.

To create a patch forcello.c:

  1. Preserve the original source code:

    $ cp cello.c cello.c.orig

    This is a common way to preserve the original source code file.

  2. Changecello.c:

    #include<stdio.h>int main(void) {    printf("Hello World from my very first patch!\n");return0;}
  3. Generate a patch using thediff utility:

    Note
    We use several common arguments for thediff utility. For moreinformation on them, see thediff manual page.
    $ 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; }

    Lines starting with a- are removed from the original source code andreplaced with the lines that start with+.

  4. Save the patch to a file:

    $ diff -Naur cello.c.orig cello.c > cello-output-first-patch.patch
  5. Restore the originalcello.c:

    $ cp cello.c.orig cello.c

    We retain the originalcello.c, because when an RPM is built, the originalfile is used, not a modified one. For more information, seeWorking with SPEC files.

To patchcello.c usingcello-output-first-patch.patch, redirect thepatch file to thepatch command:

$ patch < cello-output-first-patch.patchpatching file cello.c

The contents ofcello.c now reflect the patch:

$ cat cello.c#include<stdio.h>int main(void){    printf("Hello World from my very first patch!\n");    return 0;}

To build and run the patchedcello.c:

$ make cleanrm cello$ makegcc -g -o cello cello.c$ ./celloHello World from my very first patch!

You have created a patch, patched a program, built the patched program, and runit.

Installing Arbitrary Artifacts

A big advantage ofLinuxand other Unix-like systems is theFilesystem Hierarchy Standard(FHS). It specifies in which directory which files should be located. Files installed fromthe RPM packages should be placed according to FHS. For example, an executablefile should go into a directory that is in the systemPATHvariable.

In the context of this guide, anArbitrary Artifact is anything installedfrom an RPM to the system. For RPM and for the system it can be a script, abinary compiled from the package’s source code, a pre-compiled binary, or anyother file.

We will explore two popular ways of placingArbitrary Artifacts in thesystem: using theinstall command and using themake install command.

Using the install command

Sometimes using build automation tooling such asGNU make is not optimal - for example, if thepackaged program is simple and does not need extra overhead. In these cases,packagers often use theinstall command (provided to the system bycoreutils), which placesthe artifact to the specified directory in the filesystem with a specified setof permissions.

The example below is going to use thebello file that we had previouslycreated as the arbitrary artifact subject to our installation method. Note thatyou will either needsudo permissions or run this commandas root excluding thesudo portion of the command.

In this example,install places thebello file into/usr/bin withpermissions common for executable scripts:

$ sudo install -m 0755 bello /usr/bin/bello

Nowbello is in a directory that is listed in the$PATHvariable. Therefore,you can executebello from any directory without specifying its full path:

$ cd ~$ belloHello World

Using the make install command

A popular automated way to install built software to the system is to use themake install command. It requires you to specify how to install thearbitrary artifacts to the system in theMakefile.

Note
UsuallyMakefile is written by the developer and not by the packager.

Add theinstall section to theMakefile:

Makefile

cello:        gcc -g -o cello cello.cclean:        rm celloinstall:        mkdir -p $(DESTDIR)/usr/bin        install -m 0755 cello $(DESTDIR)/usr/bin/cello

The$(DESTDIR)variable is aGNU make built-in and iscommonly used to specify installation to a directory different than the rootdirectory.

Now you can useMakefile not only to build software, but also to install itto the target system.

To build and install thecello.c program:

$ makegcc -g -o cello cello.c$ sudo make installinstall -m 0755 cello /usr/bin/cello

Nowcello is in a directory that is listed in the$PATHvariable. Therefore,you can executecello from any directory without specifying its full path:

$ cd ~$ celloHello World

You have installed a build artifact into a chosen location on the system.

Preparing Source Code for Packaging

Note
The code created in this section can be foundhere.

Developers often distribute software as compressed archives of source code,which are then used to create packages. In this section, you will create suchcompressed archives.

Note
Creating source code archives is not normally done by the RPM Packager,but by the developer. The packager works with a ready source code archive.

Software should be distributed with asoftware license. For theexamples, we will use theGPLv3 license. The licensetext goes into theLICENSE file for each of the example programs. An RPMpackager needs to deal with license files when packaging.

For use with the following examples, create aLICENSE file:

$ cat /tmp/LICENSEThis program is free software: you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program.  If not, see <http://www.gnu.org/licenses/>.

Putting Source Code into Tarball

In the examples below, we put each of the threeHello World programs into agzip-compressed tarball. Software is oftenreleased this way to be later packaged for distribution.

bello

Thebello project implementsHello World inbash. The implementation only contains thebello shell script, so the resultingtar.gz archive will have only onefile apart from theLICENSE file. Let us assume that this is version0.1of the program.

Prepare thebello project for distribution:

  1. Put the files into a single directory:

    $ mkdir /tmp/bello-0.1$ mv ~/bello /tmp/bello-0.1/$ cp /tmp/LICENSE /tmp/bello-0.1/
  2. Create the archive for distribution and move it to~/rpmbuild/SOURCES/:

    $ cd /tmp/$ tar -cvzf bello-0.1.tar.gz bello-0.1bello-0.1/bello-0.1/LICENSEbello-0.1/bello$ mv /tmp/bello-0.1.tar.gz ~/rpmbuild/SOURCES/

pello

Thepello project implementsHello World inPython. The implementation only contains thepello.py program, so the resultingtar.gz archive will have only onefile apart from theLICENSE file. Let us assume that this is version0.1.1 of the program.

Prepare thepello project for distribution:

  1. Put the files into a single directory:

    $ mkdir /tmp/pello-0.1.1$ mv ~/pello.py /tmp/pello-0.1.1/$ cp /tmp/LICENSE /tmp/pello-0.1.1/
  2. Create the archive for distribution and move it to~/rpmbuild/SOURCES/:

    $ cd /tmp/$ tar -cvzf pello-0.1.1.tar.gz pello-0.1.1pello-0.1.1/pello-0.1.1/LICENSEpello-0.1.1/pello.py$ mv /tmp/pello-0.1.1.tar.gz ~/rpmbuild/SOURCES/

cello

Thecello project implementsHello World inC. The implementation only contains thecello.c andMakefile files, so the resultingtar.gz archive will have only two filesapart from theLICENSE file. Let us assume that this is version1.0 ofthe program.

Note that thepatch file is not distributed in the archive with the program.The RPM Packager applies the patch when the RPM is built. The patch will beplaced in the~/rpmbuild/SOURCES/ directory alongside the.tar.gz.

Prepare thecello project for distribution:

  1. Put the 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/
  2. Create the archive for distribution and move it to~/rpmbuild/SOURCES/:

    $ cd /tmp/$ tar -cvzf cello-1.0.tar.gz cello-1.0cello-1.0/cello-1.0/Makefilecello-1.0/cello.ccello-1.0/LICENSE$ mv /tmp/cello-1.0.tar.gz ~/rpmbuild/SOURCES/
  3. Add the patch:

    $ mv ~/cello-output-first-patch.patch ~/rpmbuild/SOURCES/

Now the source code is ready for packaging into an RPM.

Packaging Software

This tutorial explains packaging RPMs for the Red Hat family of Linuxdistributions, primarily:

These distributions use theRPM Packaging Format.

While these distributions are the target environment, this guide is mostlyapplicable to allRPM baseddistributions. However, the instructions need to be adapted fordistribution-specific features, such as prerequisite installation items,guidelines, or macros.

This tutorial assumes no previous knowledge about packaging software for anyOperating System, Linux or otherwise.

Note
If you do not know what a software package or a GNU/Linux distribution is,consider exploring some articles on the topics ofLinux andPackage Managers.

RPM Packages

This section covers the basics of the RPM packaging format. SeeAdvanced Topics for more advanced information.

What is an RPM?

An RPM package is simply a file containing other files and information aboutthem needed by the system. Specifically, an RPM package consists of thecpioarchive, which contains the files, and the RPM header, which contains metadataabout the package. Therpm package manager uses this metadata to determinedependencies, where to install files, and other information.

There are two types of RPM packages:

  • source RPM (SRPM)

  • binary RPM

SRPMs and binary RPMs share the file format and tooling, but have differentcontents and serve different purposes. An SRPM contains source code, optionallypatches to it, and a SPEC file, which describes how to build the source codeinto a binary RPM. A binary RPM contains the binaries built from the sources andpatches.

RPM Packaging Tools

Therpmdevtools package, installed inPrerequisites, provides severalutilities for packaging RPMs. To list these utilities, run:

$ rpm -ql rpmdevtools | grep bin

For more information on the above utilities, see their manual pages or helpdialogs.

RPM Packaging Workspace

To set up a directory layout that is the RPM packaging workspace, use therpmdev-setuptree utility:

$ rpmdev-setuptree$ tree ~/rpmbuild//home/user/rpmbuild/|-- BUILD|-- RPMS|-- SOURCES|-- SPECS`-- SRPMS5 directories, 0 files

The created directories serve these purposes:

Directory

Purpose

BUILD

When packages are built, various%buildroot directories are created here. This is useful for investigating a failed build if the logs output do not provide enough information.

RPMS

Binary RPMs are created here, in subdirectories for different architectures, for example in subdirectoriesx86_64 andnoarch.

SOURCES

Here, the packager puts compressed source code archives and patches. Therpmbuild command looks for them here.

SPECS

The packager puts SPEC files here.

SRPMS

Whenrpmbuild is used to build an SRPM instead of a binary RPM, the resulting SRPM is created here.

What is a SPEC File?

A SPEC file can be thought of as the "recipe" that therpmbuild utility usesto actually build an RPM. It tells the build system what to do by defininginstructions in a series of sections. The sections are defined in thePreamble and theBody. ThePreamble contains a series of metadataitems that are used in theBody. TheBody contains the main part of theinstructions.

Preamble Items

This table lists the items used in the Preamble section of the RPM SPEC file:

SPEC Directive

Definition

Name

The base name of the package, which should match the SPEC file name.

Version

The upstream version number of the software.

Release

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 newVersion of the software is built.

Summary

A brief, one-line summary of the package.

License

The license of the software being packaged.For packages distributed in community distributions such asFedora this must be an open source license abiding by the specific distribution’s licensing guidelines.

URL

The full URL for more information about the program. Most often this is the upstream project website for the software being packaged.

Source0

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.

Patch0

The name of the first patch to apply to the source code if necessary. If needed, more PatchX directives can be added, incrementing the number each time, for example: Patch1, Patch2, Patch3, and so on.

BuildArch

If the package is not architecture dependent, for example, if written entirely in an interpreted programming language, set this toBuildArch: noarch. If not set, the package automatically inherits the Architecture of the machine on which it is built, for examplex86_64.

BuildRequires

A comma- or whitespace-separated list of packages required for building the program written in a compiled language. There can be multiple entries ofBuildRequires, each on its own line in the SPEC file.

Requires

A comma- or whitespace-separated list of packages required by the software to run once installed. There can be multiple entries ofRequires, each on its own line in the SPEC file.

ExcludeArch

If a piece of software can not operate on a specific processor architecture, you can exclude that architecture here.

TheName,Version, andRelease directives comprise the file name ofthe RPM package. RPM Package Maintainers and Systems Administrators often callthese three directivesN-V-R orNVR, because RPM package filenames havetheNAME-VERSION-RELEASE format.

You can get an example of anNAME-VERSION-RELEASE by querying usingrpmfor a specific package:

$ rpm -q pythonpython-2.7.5-34.el7.x86_64

Here,python is the Package Name,2.7.5 is the Version, and34.el7is the Release. The final marker isx86_64, which signals the architecture.Unlike theNVR, the architecture marker is not under direct control of theRPM packager, but is defined by therpmbuild build environment. Theexception to this is the architecture-independentnoarch package.

Body Items

This table lists the items used in the Body section of the RPM SPEC file:

SPEC Directive

Definition

%description

A full description of the software packaged in the RPM. This description can span multiple lines and can be broken into paragraphs.

%prep

Command or series of commands to prepare the software to be built, for example, unpacking the archive inSource0. This directive can contain a shell script.

%build

Command or series of commands for actually building the software into machine code (for compiled languages) or byte code (for some interpreted languages).

%install

Command or series of commands for copying the desired build artifacts from the%builddir (where the build happens) to the%buildroot directory (which contains the directory structure with the files to be packaged). This usually means copying files from~/rpmbuild/BUILD to~/rpmbuild/BUILDROOT and creating the necessary directories in~/rpmbuild/BUILDROOT. This is only run when creating a package, not when the end-user installs the package. SeeWorking with SPEC files for details.

%check

Command or series of commands to test the software. This normally includes things such as unit tests.

%files

The list of files that will be installed in the end user’s system.

%changelog

A record of changes that have happened to the package between differentVersion orRelease builds.

Advanced items

The SPEC file can also contain advanced items. For example, a SPEC file can havescriptlets andtriggers. They take effect at different points during theinstallation process on the end user’s system (not the build process).

See theScriptlets and Triggers for advanced topics.

BuildRoots

In the context of RPM packaging, "buildroot" is achrootenvironment. This means that the build artifacts are placedhere using the same filesystem hierarchy as will be in the end user’s system,with "buildroot" acting as the root directory. The placement of build artifactsshould comply with the filesystem hierarchy standard of the end user’s system.

The files in "buildroot" are later put into acpioarchive, which becomes the main part ofthe RPM. When RPM is installed on the end user’s system, these files areextracted in the root directory, preserving the correct hierarchy.

Note

Starting from Red Hat Enterprise Linux 6 release, therpmbuild program has itsown defaults. As overriding these defaults leads to several problems, Red Hatdoes not recommend to define your own value of this macro. You can use the%{buildroot} macro with the defaults from therpmbuild directory.

RPM Macros

Anrpm macro is a straight textsubstitution that can be conditionally assigned based on the optional evaluationof a statement when certain built-in functionality is used. What this means isthat you can have RPM perform text substitutions for you so that you don’t haveto.

This is useful when, for example, referencing the packaged softwareVersionmultiple times in the SPEC file. You defineVersion only once - in the%{version} macro. Then use%{version} throughout the SPEC file. Everyoccurrence will be automatically substituted byVersion you definedpreviously.

Note

If you see an unfamiliar macro, you can evaluate it with:

$ rpm --eval %{_MACRO}

For example:

$ rpm --eval %{_bindir}/usr/bin$ rpm --eval %{_libexecdir}/usr/libexec

A common macro is%{?dist}, which signifies the “distribution tag”. Itsignals which distribution is used for the build.

For example:

# On a RHEL 7.x machine$ rpm --eval %{?dist}.el7# On a Fedora 23 machine$ rpm --eval %{?dist}.fc23

For more information on macros, seeMore on Macros.

Working with SPEC files

A big part of packaging software into RPMs is editing the SPEC file. In thissection we discuss how to create and modify a spec file.

To package new software, you need to create a new SPEC file. Instead of writingit manually from scratch, use therpmdev-newspec utility. It creates anunpopulated SPEC file, and you fill in the necessary directives and fields.

For this tutorial, we use the three example implementations of the 'HelloWorld!' program created inPreparing Software for Packaging:

Place them in~/rpmbuild/SOURCES.

Create a SPEC file for each of the three programs:

Note
Some programmer-focused text editors pre-populate a new.spec filewith their own SPEC template. Therpmdev-newspec provides an editor-agnosticmethod, which is why it is used in this guide.
$ cd ~/rpmbuild/SPECS$ rpmdev-newspec bellobello.spec created; type minimal, rpm version >= 4.11.$ rpmdev-newspec cellocello.spec created; type minimal, rpm version >= 4.11.$ rpmdev-newspec pellopello.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 them represent the ones described in theWhat is a SPEC File? section. In the following sections, you will populatethese SPEC files.

Note

Therpmdev-newspec utility does not use guidelines or conventions specificto any particular Linux distribution. However, this document targets Fedora,CentOS, and RHEL, so you will notice that:

  • Userm $RPM_BUILD_ROOTwhen building onCentOS (versions previous to 7.0)or onFedora (versions previous to 18).

  • We favor the use of%{buildroot} notation over$RPM_BUILD_ROOT whenreferencing RPM’s Buildroot for consistency with all other defined or providedmacros throughout the SPEC file.

There are three examples below. Each one is fully described, so you can go toa specific one if it matches your packaging needs. Or, read them all to fullyexplore packaging different kinds of software.

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.

bello

The first SPEC file is for thebello bash shell script fromPreparing Software for Packaging.

Ensure that you have:

  1. Placedbello source code into~/rpmbuild/SOURCES/. SeeWorking with SPEC files.

  2. Created the unpopulated SPEC file~/rpmbuild/SPECS/bello.spec. The filehas these contents:

    Name:           belloVersion:Release:        1%{?dist}Summary:License:URL:Source0:BuildRequires:Requires:%description%prep%setup -q%build%configuremake %{?_smp_mflags}%installrm -rf $RPM_BUILD_ROOT%make_install%files%doc%changelog* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org>-

Now, modify~/rpmbuild/SPECS/bello.spec for creatingbello RPMs:

  1. Populate theName,Version,Release, andSummary directives:

    • TheName was already specified as an argument torpmdev-newspec.

    • Set theVersion to match the “upstream” release version of thebellosource code,0.1.

    • TheRelease is automatically set to1%{?dist}, which is initially1. Increment the initial value whenever updating the package without achange in the upstream releaseVersion - such as when including a patch.ResetRelease to1 when a new upstream release happens, for example, ifbello version0.2 is released. Thedisttag macro is covered inRPM Macros.

    • TheSummary is a short, one-line explanation of what this software is.

      After your edits, the first section of the SPEC file shouldresemble:

      Name:           belloVersion:        0.1Release:        1%{?dist}Summary:        Hello World example implemented in bash script
  2. Populate theLicense,URL, andSource0 directives:

    • TheLicense field is theSoftware Licenseassociated withthe source code from the upstream release.

      Follow this format for theLicense field:Fedora License Guidelines

      For example, useGPLv3+.

    • TheURL field provides URL to the upstream software page. For example, usehttps://example.com/bello. However, for consistency, utilize the %{name}macro and instead usehttps://example.com/%{name}.

    • TheSource0 field provides URL to the upstream software source code. Itshould link directly to the version of software that is being packaged. In thisexample, we can usehttps://example.com/bello/releases/bello-0.1.tar.gz.Instead, use the %{name} macro. Also, use the %{version} macro to accommodate forchanges in version. The resulting entry ishttps://example.com/%{name}/releases/%{name}-%{version}.tar.gz.

      After your edits, the first section of the SPEC file should resemble:

      Name:           belloVersion:        0.1Release:        1%{?dist}Summary:        Hello World example implemented in bash scriptLicense:        GPLv3+URL:            https://example.com/%{name}Source0:        https://example.com/%{name}/release/%{name}-%{version}.tar.gz
  3. Populate theBuildRequires andRequires directives and include theBuildArch directive:

    • BuildRequires specifies build-time dependencies for the package. There isno building step forbello, because bash is a raw interpreted programminglanguage, and the files are simply installed to their location on the system.Just delete this directive.

    • Requires specifies run-time dependencies for the package. Thebelloscript requires only thebash shell environment to execute, so specifybashin this directive.

    • Since this is software written in an interpreted programming language with nonatively compiled extensions, add theBuildArch directive with thenoarch value. This tells RPM that this package does not need to be bound tothe processor architecture on which it is built.

      After your edits, the first section of the SPEC file should resemble:

      Name:           belloVersion:        0.1Release:        1%{?dist}Summary:        Hello World example implemented in bash scriptLicense:        GPLv3+URL:            https://example.com/%{name}Source0:        https://example.com/%{name}/release/%{name}-%{version}.tar.gzRequires:       bashBuildArch:      noarch
  4. 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. In our example we will use onlya short description.

    • The%prep section specifies how to prepare the build environment. Thisusually involves expansion of compressed archives of the source code,application of patches, and, potentially, parsing of information provided in thesource code for use in a later portion of the SPEC. In this section we simplyuse the built-in macro%setup -q.

    • The%build section specifies how to actually build the software we arepackaging. However, since abash does not need to be built, simply removewhat was provided by the template and leave this section blank.

    • The%install section contains instructions forrpmbuild on how toinstall the software, once it has been built, into theBUILDROOT directory.This directory is an emptychrootbasedirectory, which resembles the end user’s root directory. Here we should createany directories that will contain the installed files.

      Since for installingbello we only need to create the destination directoryand install the executablebash script file there, we will use theinstall command. RPM macros allow us to do this without hardcoding paths.

      The%install section should look like the following after your edits:

      %installmkdir -p %{buildroot}/%{_bindir}install -m 0755 %{name} %{buildroot}%{_bindir}/%{name}
    • The%files section specifies the list of files provided by this RPM andtheir full path location on the end user’s system. Therefore, the listing forthebello file we are installing is/usr/bin/bello, or, with RPM Macros,%{_bindir}/%{name}.

      Within this section, you can indicate the role of various files using built-inmacros. This is useful for querying the package file manifest metadata using therpm command. For example, to indicate that the LICENSE file is a softwarelicense file, we use the %license macro.

      After your edits, the%files section looks like this:

      %files%license LICENSE%{_bindir}/%{name}
  5. The last section,%changelog, is a list of datestamped entries for eachVersion-Release of the package. They log packaging changes, not softwarechanges. Examples of packaging changes: adding a patch, changing the buildprocedure in%build.

    Follow this format for the first line:

    * Day-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.

    An example datestamped entry:

    %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

You have now written an entire SPEC file forbello. The full SPEC file forbello now resembles:

Name:           belloVersion:        0.1Release:        1%{?dist}Summary:        Hello World example implemented in bash scriptLicense:        GPLv3+URL:            https://www.example.com/%{name}Source0:        https://www.example.com/%{name}/releases/%{name}-%{version}.tar.gzRequires:       bashBuildArch:      noarch%descriptionThe long-tail description for our Hello World Example implemented inbash script.%prep%setup -q%build%installmkdir -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 next section covers how to build the RPM.

pello

Our second SPEC file will be for our example written in thePythonprogramming language that you downloaded (or you created a simulated upstreamrelease in thePreparing Software for Packaging section) and placed itssource code into~/rpmbuild/SOURCES/ earlier. Let’s go ahead and open thefile~/rpmbuild/SPECS/pello.spec and start filling in some fields.

Before we start down this path, we need to address something somewhat uniqueabout byte-compiled interpreted software. Since we will be byte-compiling thisprogram, theshebangis no longer applicable because the resulting file will notcontain the entry. It is common practice to either have a non-byte-compiledshell script that will call the executable or have a small bit of thePython code that isn’t byte-compiled as the “entrypoint” into the program’s execution. This might seem silly for our small examplebut for large software projects with many thousands of lines of code, theperformance increase of pre-byte-compiled code is sizeable.

Note
The creation of a script to call the byte-compiled code or having anon-byte-compiled entry point into the software is something that upstreamsoftware developers most often address before doing a release of their softwareto the world, however this is not always the case and this exercise is meant tohelp address what to do in those situations. For more information on howPython code is normally released and distributed pleasereference theSoftwarePackaging and Distribution documentation.

We will make a small shell script to call our byte compiled code to be the entrypoint into our software. We will do this as a part of our SPEC file itself inorder to demonstrate how you can script actions inside the SPEC file. We willcover the specifics of this in the%install section later.

Let’s go ahead and open the file~/rpmbuild/SPECS/pello.spec and startfilling in some fields.

The following is the output template we were given fromrpmdev-newspec.

Name:           pelloVersion:Release:        1%{?dist}Summary:License:URL:Source0:BuildRequires:Requires:%description%prep%setup -q%build%configuremake %{?_smp_mflags}%installrm -rf $RPM_BUILD_ROOT%make_install%files%doc%changelog* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org>-

Just as with the first example, let’s begin with the first set of directivesthatrpmdev-newspec has grouped together at the top of the file:Name,Version,Release,Summary. TheName is already specified becausewe provided that information to the command line forrpmdev-newspec.

Let’s set theVersion to match what the “upstream” release version of thepello source code is, which we can observe is0.1.1 as set by theexample code we downloaded (or we created in thePreparing Software for Packaging section).

TheRelease is already set to1%{?dist} for us, the numerical valuewhich is initially1 should be incremented every time the package is updatedfor any reason, such as including a new patch to fix an issue, but doesn’t havea new upstream releaseVersion. When a new upstream release happens (forexample, pello version0.1.2 were released) then theRelease numbershould be reset to1. Thedisttag of%{?dist} should look familiarfrom the previous section’s coverage ofRPM Macros.

TheSummary should be a short, one-line explanation of what this softwareis.

After your edits, the first section of the SPEC file should resemble thefollowing:

Name:           pelloVersion:        0.1.1Release:        1%{?dist}Summary:        Hello World example implemented in Python

Now, let’s move on to the second set of directives thatrpmdev-newspec hasgrouped together in our SPEC file:License,URL,Source0.

TheLicense field is theSoftware License associated withthe 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 basedLinux distribution guidelines you arefollowing, we will use the notation standards in theFedora License Guidelines forthis document and as such this field will contain the textGPLv3+

TheURL field is the upstream software’s website, not the source codedownload link but the actual project, product, or company website where someonewould find more information about this particular piece of software. Since we’rejust using an example, we will call thishttps://example.com/pello. However,we will use the RPM macro variable of%{name} in its place for consistency.

TheSource0 field is where the upstream software’s source code should beable to be downloaded from. This URL should link directly to the specificversion of the source code release that this RPM Package is packaging. Onceagain, since this is an example we will use an example value:https://example.com/pello/releases/pello-0.1.1.tar.gz

We should note that this example URL has hard coded values in it that arepossible to change in the future and are potentially even likely to change suchas the release version0.1.1. We can simplify this by only needing to updateone field in the SPEC file and allowing it to be reused. we will use the valuehttps://example.com/%{name}/releases/%{name}-%{version}.tar.gz instead ofthe hard coded examples string previously listed.

After your edits, the top portion of your spec file should look like thefollowing:

Name:           pelloVersion:        0.1.1Release:        1%{?dist}Summary:        Hello World example implemented in PythonLicense:        GPLv3+URL:            https://example.com/%{name}Source0:        https://example.com/%{name}/release/%{name}-%{version}.tar.gz

Next up we haveBuildRequires andRequires, each of which definesomething that is required by the package. However,BuildRequires is to tellrpmbuild what is needed by your package atbuild time andRequiresis what is needed by your package atrun time.

In this example we will need thepython package in order to perform thebyte-compile build process. We will also need thepython package in order toexecute the byte-compiled code at runtime and therefore need to definepython as a requirement using theRequires directive. We will also needthebash package in order to execute the small entry-point script we willuse here.

Something we need to add here since this is software written in an interpretedprogramming language with no natively compiled extensions is aBuildArchentry that is set tonoarch in order to tell RPM that this package does notneed to be bound to the processor architecture that it is built using.

After your edits, the top portion of your spec file should look like thefollowing:

Name:           pelloVersion:        0.1.1Release:        1%{?dist}Summary:        Hello World example implemented in PythonLicense:        GPLv3+URL:            https://example.com/%{name}Source0:        https://example.com/%{name}/release/%{name}-%{version}.tar.gzBuildRequires:  pythonRequires:       pythonRequires:       bashBuildArch:      noarch

The following directives can be thought of as “section headings” because theyare directives that can define multi-line, multi-instruction, or scripted tasksto occur. We will walk through them one by one just as we did with the previousitems.

The%description should be a longer, more full length description of thesoftware being packaged than what is found in theSummary directive. For thesake of our example, this isn’t really going to contain much content but thissection can be a full paragraph or more than one paragraph if desired.

The%prep section is where weprepare our build environment or workspacefor building. Most often what happens here is the expansion of compressedarchives of the source code, application of patches, and potentially parsing ofinformation provided in the source code that is necessary in a later portion ofthe SPEC. In this section we will simply use the provided macro%setup -q.

The%build section is where we tell the system how to actually build thesoftware we are packaging. Here we will perform a byte-compilation of oursoftware. For those who read thePreparing Software for Packagingsection, this portion of the example should look familiar.

The%build section of our SPEC file should look as follows.

%buildpython -m compileall pello.py

The%install section is where we instructrpmbuild how to install ourpreviously built software into theBUILDROOT which is effectively achrootbase directory with nothing in it and we will have toconstruct any paths or directory hierarchies that we will need in order toinstall our software here in their specific locations. However, our RPM Macroshelp us accomplish this task without having to hardcode paths.

We had previously discussed that since we will lose the context of a file with theshebangline in it when we byte compile that we will need to createa simple wrapper script in order to accomplish that task. There are many optionson how to accomplish this including, but not limited to, making a separatescript and using that as a separateSourceX directive and the option we’regoing to show in this example which is to create the file in-line in the SPECfile. The reason for showing the example option that we are is simply todemonstrate that the SPEC file itself is scriptable. What we’re going to do iscreate a small“wrapper script” which will execute thePython byte-compiled code by using ahere document. We will also need to actually install the byte-compiled file into a librarydirectory on the system such that it can be accessed.

Note
You will notice below that we are hard coding the library path. There arevarious methods to avoid needing to do this, many of which are addressed inAdvanced Topics, under theMore on Macros section, and are specificto the programming language in which the software that is being packaged waswritten in. In this example we hard code the path for simplicity as to not covertoo many topics simultaneously.

The%install section should look like the following after your edits:

%installmkdir -p %{buildroot}/%{_bindir}mkdir -p %{buildroot}/usr/lib/%{name}cat > %{buildroot}%{_bindir}/%{name} <<-EOF#!/bin/bash/usr/bin/python /usr/lib/%{name}/%{name}.pycEOFchmod 0755 %{buildroot}%{_bindir}/%{name}install -m 0644 %{name}.py* %{buildroot}/usr/lib/%{name}/

The%files section is where we provide the list of files that this RPMprovides and where it’s intended for them to live on the system that the RPM isinstalled upon. Note here that this isn’t relative to the%{buildroot} butthe full path for the files as they are expected to exist on the end systemafter installation. Therefore, the listing for thepello file we areinstalling will be%{_bindir}/pello. We will also need to provide a%dirlisting to define that this package “owns” the library directory we created aswell as all the files we placed in it.

Also, within this section you will sometimes need a built-in macro to providecontext on a file. This can be useful for Systems Administrators and end userswho might want to query the system withrpm about the resulting package. Thebuilt-in macro we will use here is%license which will tellrpmbuildthat this is a software license file in the package file manifest metadata.

The%files section should look like the following after your edits:

%files%license LICENSE%dir /usr/lib/%{name}/%{_bindir}/%{name}/usr/lib/%{name}/%{name}.py*

The last section,%changelog is a list of date-stamped entries thatcorrelate to a specific Version-Release of the package. This is not meant to bea log of what changed in the software from release to release, but specificallyto packaging changes. For example, if software in a package needed patching orthere was a change needed in the build procedure listed in the%buildsection that information would go here. Each change entry can contain multipleitems and each item should start on a new line and begin with a- character.Below is our example entry:

%changelog* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org> - 0.1.1-1- First pello package- Example second item in the changelog for version-release 0.1.1-1

Note the format above, the date-stamp will begin with a* character,followed by the calendar day of the week, the month, the day of the month, theyear, then the contact information for the RPM Packager. From there we have a- character before the Version-Release, which is an often used conventionbut not a requirement. Then finally the Version-Release.

That’s it! We’ve written an entire SPEC file forpello! In the next sectionwe will cover how to build the RPM!

The full SPEC file should now look like the following:

Name:           pelloVersion:        0.1.1Release:        1%{?dist}Summary:        Hello World example implemented in pythonLicense:        GPLv3+URL:            https://www.example.com/%{name}Source0:        https://www.example.com/%{name}/releases/%{name}-%{version}.tar.gzBuildRequires:  pythonRequires:       pythonRequires:       bashBuildArch:      noarch%descriptionThe long-tail description for our Hello World Example implemented inPython.%prep%setup -q%buildpython -m compileall %{name}.py%installmkdir -p %{buildroot}%{_bindir}mkdir -p %{buildroot}/usr/lib/%{name}cat > %{buildroot}%{_bindir}/%{name} <<-EOF#!/bin/bash/usr/bin/python /usr/lib/%{name}/%{name}.pycEOFchmod 0755 %{buildroot}%{_bindir}/%{name}install -m 0644 %{name}.py* %{buildroot}/usr/lib/%{name}/%files%license LICENSE%dir /usr/lib/%{name}/%{_bindir}/%{name}/usr/lib/%{name}/%{name}.py*%changelog* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org> - 0.1.1-1  - First pello package
cello

Our third SPEC file will be for our example written in theCprogramminglanguage that we created a simulated upstream release of previously (or youdownloaded) and placed its source code into~/rpmbuild/SOURCES/ earlier.

Let’s go ahead and open the file~/rpmbuild/SPECS/cello.spec and startfilling in some fields.

The following is the output template we were given fromrpmdev-newspec.

Name:           celloVersion:Release:        1%{?dist}Summary:License:URL:Source0:BuildRequires:Requires:%description%prep%setup -q%build%configuremake %{?_smp_mflags}%installrm -rf $RPM_BUILD_ROOT%make_install%files%doc%changelog* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org>-

Just as with the previous examples, let’s begin with the first set of directivesthatrpmdev-newspec has grouped together at the top of the file:Name,Version,Release,Summary. TheName is alreadyspecified because we provided that information to the command line forrpmdev-newspec.

Let’s set theVersion to match what the “upstream” release version of thecello source code is, which we can observe is1.0 as set by the examplecode we downloaded (or we created in thePreparing Software for Packagingsection).

TheRelease is already set to1%{?dist} for us, the numerical valuewhich is initially1 should be incremented every time the package is updatedfor any reason, such as including a new patch to fix an issue, but doesn’t havea new upstream releaseVersion. When a new upstream release happens (forexample, cello version2.0 were released) then theRelease number shouldbe reset to1. Thedisttag of%{?dist} should look familiar from theprevious section’s coverage ofRPM Macros.

TheSummary should be a short, one-line explanation of what this softwareis.

After your edits, the first section of the SPEC file should resemble thefollowing:

Name:           celloVersion:        1.0Release:        1%{?dist}Summary:        Hello World example implemented in C

Now, let’s move on to the second set of directives thatrpmdev-newspec hasgrouped together in our SPEC file:License,URL,Source0. However,we will add one to this grouping as it is closely related to theSource0 andthat is ourPatch0 which will list the first patch we need against oursoftware.

TheLicense field is theSoftware Licenseassociated with the source codefrom the upstream release. The exact format for how to label the License in yourSPEC file will vary depending on which specific RPM basedLinuxdistribution guidelines you are following, we will use the notation standards intheFedora License Guidelinesfor this document and as such this field will contain the textGPLv3+

TheURL field is the upstream software’s website, not the source codedownload link but the actual project, product, or company website where someonewould find more information about this particular piece of software. Since we’rejust using an example, we will call thishttps://example.com/cello. However,we will use the rpm macro variable of%{name} in its place for consistency.

TheSource0 field is where the upstream software’s source code should beable to be downloaded from. This URL should link directly to the specificversion of the source code release that this RPM Package is packaging. Onceagain, since this is an example we will use an example value:https://example.com/cello/releases/cello-1.0.tar.gz

We should note that this example URL has hard coded values in it that arepossible to change in the future and are potentially even likely to change suchas the release version1.0. We can simplify this by only needing to updateone field in the SPEC file and allowing it to be reused. we will use the valuehttps://example.com/%{name}/releases/%{name}-%{version}.tar.gz instead ofthe hard coded examples string previously listed.

The next item is to provide a listing for the.patch file we created earliersuch that we can apply it to the code later in the%prep section. We willneed a listing ofPatch0:         cello-output-first-patch.patch.

After your edits, the top portion of your spec file should look like thefollowing:

Name:           celloVersion:        1.0Release:        1%{?dist}Summary:        Hello World example implemented in CLicense:        GPLv3+URL:            https://example.com/%{name}Source0:        https://example.com/%{name}/release/%{name}-%{version}.tar.gzPatch0:         cello-output-first-patch.patch

Next up we haveBuildRequires andRequires, each of which definesomething that is required by the package. However,BuildRequires is to tellrpmbuild what is needed by your package atbuild time andRequiresis what is needed by your package atrun time.

In this example we will need thegcc andmake packages in order toperform the compilation build process. Runtime requirements are fortunatelyhandled for us byrpmbuild because this program does not require anythingoutside of the coreCstandard libraries and we therefore will not need todefine anything by hand as aRequires and can omit that directive.

After your edits, the top portion of your spec file should look like thefollowing:

Name:           celloVersion:        0.1Release:        1%{?dist}Summary:        Hello World example implemented in CLicense:        GPLv3+URL:            https://example.com/%{name}Source0:        https://example.com/%{name}/release/%{name}-%{version}.tar.gzBuildRequires:  gccBuildRequires:  make

The following directives can be thought of as “section headings” because theyare directives that can define multi-line, multi-instruction, or scripted tasksto occur. We will walk through them one by one just as we did with the previousitems.

The%description should be a longer, more full length description of thesoftware being packaged than what is found in theSummary directive. For thesake of our example, this isn’t really going to contain much content but thissection can be a full paragraph or more than one paragraph if desired.

The%prep section is where weprepare our build environment or workspacefor building. Most often what happens here is the expansion of compressedarchives of the source code, application of patches, and potentially parsing ofinformation provided in the source code that is necessary in a later portion ofthe SPEC. In this section we will simply use the provided macro%setup -q.

The%build section is where we tell the system how to actually build thesoftware we are packaging. Since wrote a simpleMakefile for ourCimplementation, we can simply use theGNU make command provided byrpmdev-newspec. However, we need to remove the call to%configurebecause we did not provide aconfigure script. The%build section of our SPEC file should look as follows.

%buildmake %{?_smp_mflags}

The%install section is where we instructrpmbuild how to install ourpreviously built software into theBUILDROOT which is effectively achrootbase directory with nothing in it and we will have to construct anypaths or directory hierarchies that we will need in order to install oursoftware here in their specific locations. However, our RPM Macros help usaccomplish this task without having to hardcode paths.

Once again, since we have a simpleMakefile the installation step can beaccomplished easily by leaving in place the%make_install macro that wasagain provided for us by therpmdev-newspec command.

The%install section should look like the following after your edits:

%install%make_install

The%files section is where we provide the list of files that this RPMprovides and where it’s intended for them to live on the system that the RPM isinstalled upon. Note here that this isn’t relative to the%{buildroot} butthe full path for the files as they are expected to exist on the end systemafter installation. Therefore, the listing for thecello file we areinstalling will be%{_bindir}/cello.

Also, within this section you will sometimes need a built-in macro to providecontext on a file. This can be useful for Systems Administrators and end userswho might want to query the system withrpm about the resulting package.The built-in macro we will use here is%license which will tellrpmbuildthat this is a software license file in the package file manifest metadata.

The%files section should look like the following after your edits:

%files%license LICENSE%{_bindir}/%{name}

The last section,%changelog is a list of date-stamped entries thatcorrelate to a specific Version-Release of the package. This is not meant to bea log of what changed in the software from release to release, but specificallyto packaging changes. For example, if software in a package needed patching orthere was a change needed in the build procedure listed in the%buildsection that information would go here. Each change entry can contain multipleitems and each item should start on a new line and begin with a- character.Below is our example entry:

%changelog* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org> - 0.1-1- First cello package

Note the format above, the date-stamp will begin with a* character,followed by the calendar day of the week, the month, the day of the month, theyear, then the contact information for the RPM Packager. From there we havea- character before the Version-Release, which is an often used conventionbut not a requirement. Then finally the Version-Release.

That’s it! We’ve written an entire SPEC file forcello! In the next sectionwe will cover how to build the RPM!

The full SPEC file should now look like the following:

Name:           celloVersion:        1.0Release:        1%{?dist}Summary:        Hello World example implemented in CLicense:        GPLv3+URL:            https://www.example.com/%{name}Source0:        https://www.example.com/%{name}/releases/%{name}-%{version}.tar.gzPatch0:         cello-output-first-patch.patchBuildRequires:  gccBuildRequires:  make%descriptionThe long-tail description for our Hello World Example implemented inC.%prep%setup -q%patch0%buildmake %{?_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

Therpmdevtools package provides a set of SPEC file templates for severalpopular languages in the/etc/rpmdevtools/ directory.

Building RPMS

RPMs are built with therpmbuild command. Different scenarios and desiredoutcomes require different combinations of arguments torpmbuild. Thissection describes the two prime scenarios:

  1. building a source RPM

  2. building a binary RPM

Therpmbuild command expects a certain directory and file structure. This isthe same structure as set up by therpmdev-setuptree utility. The previousinstructions also confirmed to the required structure.

Source RPMs

Why build a Source RPM (SRPM)?

  1. To preserve the exact source of a certain Name-Version-Release of the RPM thatwas deployed to an environment. This includes the exact SPEC file, the sourcecode, and all relevant patches. This is useful for looking back in history andfor debugging.

  2. To be able to build a binary RPM on a different hardware platform orarchitecture.

To create a SRPM:
$ rpmbuild -bs _SPECFILE_

SubstituteSPECFILE with the SPEC file. The-bs option stands for "buildsource".

Here we build SRPMs forbello,pello, andcello:

$ cd ~/rpmbuild/SPECS/$ rpmbuild -bs bello.specWrote: /home/admiller/rpmbuild/SRPMS/bello-0.1-1.el7.src.rpm$ rpmbuild -bs pello.specWrote: /home/admiller/rpmbuild/SRPMS/pello-0.1.1-1.el7.src.rpm$ rpmbuild -bs cello.specWrote: /home/admiller/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm

Note that SRPMs were placed into therpmbuild/SRPMS directory, which is partof the structure expected byrpmbuild.

This is all there is to building a SRPM.

Binary RPMS

There are two methods for building Binary RPMs:

  1. Rebuilding it from a SRPM using therpmbuild --rebuild command.

  2. Building it from a SPEC file using therpmbuild -bb command. The-bb option stands for "build binary".

Rebuilding from a Source RPM

To rebuildbello,pello, andcello from Source RPMs (SRPMs), run:

$ rpmbuild --rebuild ~/rpmbuild/SRPMS/bello-0.1-1.el7.src.rpm[output truncated]$ rpmbuild --rebuild ~/rpmbuild/SRPMS/pello-0.1.1-1.el7.src.rpm[output truncated]$ rpmbuild --rebuild ~/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm[output truncated]

Now you have built RPMs. A few notes:

  • The output generated when creating a binary RPM is verbose, which is helpfulfor debugging. The output varies for different examples and corresponds to theirSPEC files.

  • The resulting binary RPMs are in~/rpmbuild/RPMS/YOURARCH whereYOURARCHis yourarchitectureor in~/rpmbuild/RPMS/noarch/, if the package is not architecture-specific.

  • Invokingrpmbuild --rebuild involves:

    1. Installing the contents of the SRPM - the SPEC file and the source code - into the~/rpmbuild/ directory.

    2. Building using the installed contents.

    3. Removing the SPEC file and the source code.

    You can retain the SPEC file and the source code after building. For this, youhave two options:

    • When building, use the--recompile option instead of--rebuild.

    • Install the SRPMs using these commands:

    $ rpm -Uvh ~/rpmbuild/SRPMS/bello-0.1-1.el7.src.rpmUpdating / installing...   1:bello-0.1-1.el7                  ################################# [100%]$ rpm -Uvh ~/rpmbuild/SRPMS/pello-0.1.1-1.el7.src.rpmUpdating / installing...   1:pello-0.1.1-1.el7                ################################# [100%]$ rpm -Uvh ~/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpmUpdating / installing...   1:cello-1.0-1.el7                  ################################# [100%]

    For this tutorial, execute therpm -Uvh commands above to continueinteracting with the SPEC files and sources.

Building Binary from the SPEC file

To buildbello,pello, andcello from their SPEC files, run:

$ rpmbuild -bb ~/rpmbuild/SPECS/bello.spec$ rpmbuild -bb ~/rpmbuild/SPECS/pello.spec$ rpmbuild -bb ~/rpmbuild/SPECS/cello.spec

Now you have built RPMs from SPEC files.

Most of the information inRebuilding from a Source RPM apply here.

Checking RPMs For Sanity

After creating a package, it is good to check its quality. Quality of thepackage, not of the software delivered within it. The main tool for this isrpmlint. It improves RPMmaintainability and enables sanity and error checking by performing staticanalysis of the RPM. This utility can check Binary RPMs, Source RPMs (SRPMs),and SPEC files, so is useful for all stages of packaging, as shown in thefollowing examples.

Note thatrpmlint has very strict guidelines, and sometimes it is acceptableand necessary to skip some of its Errors and Warnings, as shown in the followingexamples.

Note
In the examples, we runrpmlint without any options, which producesnon-verbose output. For detailed explanations of each Error or Warning, runrpmlint -i instead.

Checking the bello SPEC File

This is the output of runningrpmlint on the SPEC file forbello:

$ rpmlint bello.specbello.spec: W: invalid-url Source0: https://www.example.com/bello/releases/bello-0.1.tar.gz HTTP Error 404: Not Found0 packages and 1 specfiles checked; 0 errors, 1 warnings.

Observations:

  • Forbello.spec there is only one warning. It says that the URL listed in theSource0 directive is unreachable. This is expected, because the specifiedexample.com URL does not exist. Presuming that we expect this URL to work inthe future, we can ignore this warning.

This is the output of runningrpmlint on the SRPM forbello:

$ rpmlint ~/rpmbuild/SRPMS/bello-0.1-1.el7.src.rpmbello.src: W: invalid-url URL: https://www.example.com/bello HTTP Error 404: Not Foundbello.src: W: invalid-url Source0: https://www.example.com/bello/releases/bello-0.1.tar.gz HTTP Error 404: Not Found1 packages and 0 specfiles checked; 0 errors, 2 warnings.

Observations:

  • For thebello SRPM there is a new warning, which says that the URLspecified in theURL directive is unreachable. Assuming the link will beworking in the future, we can ignore this warning.

Checking the bello Binary RPM

When checking Binary RPMs,rpmlint checks for more things, including:

  1. documentation

  2. manual pages

  3. consistent use of theFilesystem Hierarchy Standard

This is the output of runningrpmlint on the Binary RPM forbello:

$ rpmlint ~/rpmbuild/RPMS/noarch/bello-0.1-1.el7.noarch.rpmbello.noarch: W: invalid-url URL: https://www.example.com/bello HTTP Error 404: Not Foundbello.noarch: W: no-documentationbello.noarch: W: no-manual-page-for-binary bello1 packages and 0 specfiles checked; 0 errors, 3 warnings.

Observations:

  • Theno-documentation andno-manual-page-for-binary warnings say thatthe RPM has no documentation or manual pages, because we did not provide any.

Apart from the above warnings, our RPM is passingrpmlint checks.

Checking the pello SPEC File

This is the output of runningrpmlint on the SPEC file forpello:

$ rpmlint pello.specpello.spec:30: E: hardcoded-library-path in %{buildroot}/usr/lib/%{name}pello.spec:34: E: hardcoded-library-path in /usr/lib/%{name}/%{name}.pycpello.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.1.tar.gz HTTP Error 404: Not Found0 packages and 1 specfiles checked; 5 errors, 1 warnings.

Observations:

  • Theinvalid-url Source0 warning says that the URL listed in the Source0directive is unreachable. This is expected, because the specifiedexample.comURL does not exist. Presuming that we expect this URL to work in the future, wecan ignore this warning.

  • There are many errors, because we intentionally wrote this SPEC file to beuncomplicated and to show what errorsrpmlint can report.

  • Thehardcoded-library-path errors suggest to use the%{_libdir} macroinstead of hard-coding the library path. For the sake of this example, we ignorethese errors, but for packages going in production you need a good reason forignoring this error.

This is the output of runningrpmlint on the SRPM forpello:

$ rpmlint ~/rpmbuild/SRPMS/pello-0.1.1-1.el7.src.rpmpello.src: W: invalid-url URL: https://www.example.com/pello HTTP Error 404: Not Foundpello.src:30: E: hardcoded-library-path in %{buildroot}/usr/lib/%{name}pello.src:34: E: hardcoded-library-path in /usr/lib/%{name}/%{name}.pycpello.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.1.tar.gz HTTP Error 404: Not Found1 packages and 0 specfiles checked; 5 errors, 2 warnings.

Observations:

  • The newinvalid-url URL error here is about theURL directive, whichis unreachable. Assuming that we expect the URL to become valid in the future,we can ignore this error.

Checking the pello Binary RPM

When checking Binary RPMs,rpmlint checks for more things, including:

  1. documentation

  2. manual pages

  3. consistent use of the

  4. Filesystem Hierarchy Standard

This is the output of runningrpmlint on the Binary RPM forpello:

$ rpmlint ~/rpmbuild/RPMS/noarch/pello-0.1.1-1.el7.noarch.rpmpello.noarch: W: invalid-url URL: https://www.example.com/pello HTTP Error 404: Not Foundpello.noarch: W: only-non-binary-in-usr-libpello.noarch: W: no-documentationpello.noarch: E: non-executable-script /usr/lib/pello/pello.py 0644L /usr/bin/envpello.noarch: W: no-manual-page-for-binary pello1 packages and 0 specfiles checked; 1 errors, 4 warnings.

Observations:

  • Theno-documentation andno-manual-page-for-binary warnings say thatthe RPM has no documentation or manual pages, because we did not provide any.

  • Theonly-non-binary-in-usr-lib warning says that you provided onlynon-binary artifacts in/usr/lib/. This directory is normally reserved forshared object files, which are binary files. Therefore,rpmlint expects atleast one or more files in/usr/lib/ to be binary.

    This is an example of anrpmlint check for compliance withFilesystem Hierarchy Standard.

    Normally, use RPM macros to ensure the correct placement of files. For the sakeof this example, we can ignore this warning.

  • Thenon-executable-script error warns that the/usr/lib/pello/pello.pyfile has no execute permissions. Since this file contains theshebang,rpmlint expectsthe file to be executable. For the purpose of the example, leave this filewithout execute permissions and ignore this error.

Apart from the above warnings and errors, our RPM is passingrpmlint checks.

Checking the cello SPEC File

This is the output of runningrpmlint on the SPEC file forcello:

$ 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 Found0 packages and 1 specfiles checked; 0 errors, 1 warnings.

Observations:

  • The only warning forcello.spec says that the URL listed in theSource0 directive is unreachable. This is expected, because the specifiedexample.com URL does not exist. Presuming that we expect this URL to work inthe future, we can ignore this warning.

This is the output of runningrpmlint on the SRPM file forcello:

$ rpmlint ~/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpmcello.src: W: invalid-url URL: https://www.example.com/cello HTTP Error 404: Not Foundcello.src: W: invalid-url Source0: https://www.example.com/cello/releases/cello-1.0.tar.gz HTTP Error 404: Not Found1 packages and 0 specfiles checked; 0 errors, 2 warnings.

Observations:

  • For thecello SRPM there is a new warning, which says that the URLspecified in theURL directive is unreachable. Assuming the link will beworking in the future, we can ignore this warning.

Checking the cello Binary RPM

When checking Binary RPMs,rpmlint checks for more things, including:

  1. documentation

  2. manual pages

  3. consistent use of theFilesystem Hierarchy Standard.

This is the output of runningrpmlint on the Binary RPM forcello:

$ rpmlint ~/rpmbuild/RPMS/x86_64/cello-1.0-1.el7.x86_64.rpmcello.x86_64: W: invalid-url URL: https://www.example.com/cello HTTP Error 404: Not Foundcello.x86_64: W: no-documentationcello.x86_64: W: no-manual-page-for-binary cello1 packages and 0 specfiles checked; 0 errors, 3 warnings.

Observations:

  • Theno-documentation andno-manual-page-for-binary warnings say thatthe RPM has no documentation or manual pages, because we did not provide any.

Apart from the above warnings and errors, our RPM is passingrpmlint checks.

Our RPMs are now ready and checked withrpmlint. This concludes thetutorial. For more information on packaging RPMs, proceed toAdvanced Topics.

Advanced Topics

This chapter covers topics that are beyond the scope of the introductorytutorial but are often useful in real-world RPM packaging.

Signing Packages

Signing a package is a way to secure the package for an end user. Securetransport can be achieved with implementation of the HTTPS protocol, which canbe done when the package is downloaded just before installing. However, thepackages are often downloaded in advance and stored in local repositories beforethey are used. The packages are signed to make sure no third party can alter thecontent of a package.

There are three ways to sign a package:

Adding a Signature to a Package

In most cases packages are built without a signature. The signature is addedjust before the release of the package.

In order to add another signature to the package package, use the--addsignoption. Having more than one signature makes it possible to record the package’spath of ownership from the package builder to the end-user.

As an example, a division of a company creates a package and signs it with thedivision’s key. The company’s headquarters then checks the package’s signatureand adds the corporate signature to the package, stating that the signed packageis authentic.

With two signatures, the package makes its way to a retailer. The retailerchecks the signatures and, if they check out, adds their signature as well.

The package now makes its way to a company that wishes to deploy the package.After checking every signature on the package, they know that it is an authenticcopy, unchanged since it was first created. Depending on the deploying company’sinternal controls, they may choose to add their own signature, to reassure theiremployees that the package has received their corporate approval.

The output from the--addsign option:

$ rpm --addsign blather-7.9-1.i386.rpm            Enter pass phrase:Pass phrase is good.blather-7.9-1.i386.rpm:

To check the signatures of a package with multiple signatures:

$ rpm --checksig blather-7.9-1.i386.rpmblather-7.9-1.i386.rpm: size pgp pgp md5 OK

The twopgp strings in the output of therpm --checksig command show thatthe package has been signed twice.

RPM makes it possible to add the same signature multiple times. The--addsignoption does not check for multiple identical signatures.

$ rpm --addsig blather-7.9-1.i386.rpm              Enter pass phrase:Pass phrase is good.blather-7.9-1.i386.rpm:$ rpm --addsig blather-7.9-1.i386.rpm              Enter pass phrase:Pass phrase is good.blather-7.9-1.i386.rpm:$ rpm --addsig blather-7.9-1.i386.rpm              Enter pass phrase:Pass phrase is good.blather-7.9-1.i386.rpm:$ rpm --checksig blather-7.9-1.i386.rpmblather-7.9-1.i386.rpm: size pgp pgp pgp pgp md5 OK

The output of therpm --checksig command displays four signatures.

Replacing a Package Signature

To change the public key without having to rebuild each package, use the--resign option.

$ rpm --resign blather-7.9-1.i386.rpm            Enter pass phrase:Pass phrase is good.blather-7.9-1.i386.rpm:

To use the--resign option on multiple package files:

$ rpm --resign b*.rpm            Enter pass phrase:Pass phrase is good.blather-7.9-1.i386.rpm:bother-3.5-1.i386.rpm:

Build-time Signing

To sign a package at build-time, use therpmbuild command with the--signoption. This requires entering the PGP passphrase.

For example:

$ rpmbuild -ba --sign blather-7.9.spec            Enter pass phrase:Pass phrase is good.* Package: blather…Binary Packaging: blather-7.9-1Finding dependencies...…Generating signature: 1002Wrote: /usr/src/redhat/RPMS/i386/blather-7.9-1.i386.rpm…Source Packaging: blather-7.9-1…Generating signature: 1002Wrote: /usr/src/redhat/SRPMS/blather-7.9-1.src.rpm

The "Generating signature" message appears in both the binary and sourcepackaging sections. The number following the message indicates that thesignature added was created using PGP.

Note

When using the--sign option forrpmbuild, use only-bb or-ba optionsfor package building.-ba option mean build binaryand source packages.

To verify the signature of a package, use therpm command with--checksigoption. For example:

$ rpm --checksig blather-7.9-1.i386.rpmblather-7.9-1.i386.rpm: size pgp md5 OK
Building Multiple Packages

When building multiple packages, use the following syntax to avoid entering thePGP passphrase multiple times. For example when building theblather andbother packages, sign them by using the following:

$ rpmbuild -ba --sign b*.spec              Enter pass phrase:Pass phrase is good.* Package: blather…Binary Packaging: blather-7.9-1…Generating signature: 1002Wrote: /usr/src/redhat/RPMS/i386/blather-7.9-1.i386.rpm…Source Packaging: blather-7.9-1…Generating signature: 1002Wrote: /usr/src/redhat/SRPMS/blather-7.9-1.src.rpm…* Package: bother…Binary Packaging: bother-3.5-1…Generating signature: 1002Wrote: /usr/src/redhat/RPMS/i386/bother-3.5-1.i386.rpm…Source Packaging: bother-3.5-1…Generating signature: 1002Wrote: /usr/src/redhat/SRPMS/bother-3.5-1.src.rpm

Mock

Mock is a tool forbuilding packages. It can build packages for different architectures anddifferent Fedora or RHEL versions than the build host has. Mock creates chrootsand builds packages in them. Its only task is to reliably populate a chroot andattempt to build a package in that chroot.

Mock also offers a multi-package tool,mockchain, that can build chains ofpackages that depend on each other.

Mock is capable of building SRPMs from source configuration management if themock-scm package is present, then building the SRPM into RPMs. See –scm-enablein the documentation. (From the upstream documentation)

Note
In order to useMockon a RHEL or CentOS system, you will need to enable the “Extra Packages forEnterprise Linux” (EPEL) repository. Thisis a repository provided by theFedora community and hasmany useful tools for RPM Packagers, systems administrators, and developers.

One of the most common use cases RPM Packagers have forMock is to create what isknown as a “pristine build environment”. By using mock as a “pristine buildenvironment”, nothing about the current state of your system affects the RPMPackage itself. Mock uses different configurations to specify what the build“target” is, these are found on your system in the/etc/mock/ directory(once you’ve installed themock package). You can build for differentdistributions or releases just by specifying it on the command line. Somethingto keep in mind is that the configuration files that come withmock aretargeted at Fedora RPM Packagers, and as such RHEL and CentOS release versionsare labeled as “epel” because that is the “target” repository these RPMs wouldbe built for. You simply specify the configuration you want to use (minus the.cfg file extension). For example, you could build ourcello example forboth RHEL 7 and Fedora 23 using the following commands without ever having touse different machines.

$ mock -r epel-7-x86_64 ~/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm$ mock -r fedora-23-x86_64 ~/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm

One example of why you might want to usemock is if you were packaging RPMs onyour laptop and you had a package installed (we’ll call itfoo for thisexample) that was aBuildRequires of that package you were creating butforgot to actually make theBuildRequires: foo entry. The build wouldsucceed when you runrpmbuild becausefoo was needed to build and it wasfound on the system at build time. However, if you took the SRPM to anothersystem that lackedfoo it would fail, causing an unexpected side effect.Mock solves this by firstparsing the contents of the SRPM and installing theBuildRequires into itschroot which means that if you weremissing theBuildRequires entry, the build would fail becausemock wouldnot know to install it and it would therefore not be present in the buildroot.

Another example is the opposite scenario, let’s say you needgcc to build apackage but don’t have it installed on your system (which is unlikely as an RPMPackager, but just for the sake of the example let us pretend that is true).WithMock, you don’t haveto installgcc on your system because it will get installed in the chroot aspart ofmock’s process.

Below is an example of attempting to rebuild a package that has a dependencythat I’m missing on my system. The key thing to note is that whilegcc iscommonly on most RPM Packager’s systems, some RPM Packages can have over a dozenBuildRequires and this allows you to not need to clutter up your workstationwith otherwise un-needed or un-necessary packages.

$ rpmbuild --rebuild ~/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpmInstalling /home/admiller/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpmerror: Failed build dependencies: gcc is needed by cello-1.0-1.el7.x86_64$ mock -r epel-7-x86_64 ~/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpmINFO: mock.py version 1.2.17 starting (python version = 2.7.5)...Start: init pluginsINFO: selinux enabledFinish: init pluginsStart: runINFO: Start(/home/admiller/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm)  Config(epel-7-x86_64)Start: clean chrootFinish: clean chrootStart: chroot initINFO: calling preinit hooksINFO: enabled root cacheStart: unpacking root cacheFinish: unpacking root cacheINFO: enabled yum cacheStart: cleaning yum metadataFinish: cleaning yum metadataMock Version: 1.2.17INFO: Mock Version: 1.2.17Start: yum updatebase                                                                    | 3.6 kB  00:00:00epel                                                                    | 4.3 kB  00:00:00extras                                                                  | 3.4 kB  00:00:00updates                                                                 | 3.4 kB  00:00:00No packages marked for updateFinish: yum updateFinish: chroot initStart: build phase for cello-1.0-1.el7.src.rpmStart: build setup for cello-1.0-1.el7.src.rpmwarning: Could not canonicalize hostname: rhel7Building target platforms: x86_64Building for target x86_64Wrote: /builddir/build/SRPMS/cello-1.0-1.el7.centos.src.rpmGetting requirements for cello-1.0-1.el7.centos.src --> Already installed : gcc-4.8.5-4.el7.x86_64 --> Already installed : 1:make-3.82-21.el7.x86_64No uninstalled build requiresFinish: build setup for cello-1.0-1.el7.src.rpmStart: rpmbuild cello-1.0-1.el7.src.rpmBuilding target platforms: x86_64Building for target x86_64Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.v9rPOF+ umask 022+ cd /builddir/build/BUILD+ cd /builddir/build/BUILD+ rm -rf cello-1.0+ /usr/bin/gzip -dc /builddir/build/SOURCES/cello-1.0.tar.gz+ /usr/bin/tar -xf -+ STATUS=0+ '[' 0 -ne 0 ']'+ cd cello-1.0+ /usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .Patch #0 (cello-output-first-patch.patch):+ echo 'Patch #0 (cello-output-first-patch.patch):'+ /usr/bin/cat /builddir/build/SOURCES/cello-output-first-patch.patchpatching file cello.c+ /usr/bin/patch -p0 --fuzz=0+ exit 0Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.UxRVtI+ umask 022+ cd /builddir/build/BUILD+ cd cello-1.0+ make -j2gcc -g -o cello cello.c+ exit 0Executing(%install): /bin/sh -e /var/tmp/rpm-tmp.K3i2dL+ umask 022+ cd /builddir/build/BUILD+ '[' /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64 '!=' / ']'+ rm -rf /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64++ dirname /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64+ mkdir -p /builddir/build/BUILDROOT+ mkdir /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64+ cd cello-1.0+ /usr/bin/make install DESTDIR=/builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64mkdir -p /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64/usr/bininstall -m 0755 cello /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64/usr/bin/cello+ /usr/lib/rpm/find-debuginfo.sh --strict-build-id -m --run-dwz --dwz-low-mem-die-limit 10000000 --dwz-max-die-limit 110000000 /builddir/build/BUILD/cello-1.0extracting debug info from /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64/usr/bin/cellodwz: Too few files for multifile optimization/usr/lib/rpm/sepdebugcrcfix: Updated 0 CRC32s, 1 CRC32s did match.+ /usr/lib/rpm/check-buildroot+ /usr/lib/rpm/redhat/brp-compress+ /usr/lib/rpm/redhat/brp-strip-static-archive /usr/bin/strip+ /usr/lib/rpm/brp-python-bytecompile /usr/bin/python 1+ /usr/lib/rpm/redhat/brp-python-hardlink+ /usr/lib/rpm/redhat/brp-java-repack-jarsProcessing files: cello-1.0-1.el7.centos.x86_64Executing(%license): /bin/sh -e /var/tmp/rpm-tmp.vxtAuO+ umask 022+ cd /builddir/build/BUILD+ cd cello-1.0+ LICENSEDIR=/builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64/usr/share/licenses/cello-1.0+ export LICENSEDIR+ /usr/bin/mkdir -p /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64/usr/share/licenses/cello-1.0+ cp -pr LICENSE /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64/usr/share/licenses/cello-1.0+ exit 0Provides: cello = 1.0-1.el7.centos cello(x86-64) = 1.0-1.el7.centosRequires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1Requires: libc.so.6()(64bit) libc.so.6(GLIBC_2.2.5)(64bit) rtld(GNU_HASH)Processing files: cello-debuginfo-1.0-1.el7.centos.x86_64Provides: cello-debuginfo = 1.0-1.el7.centos cello-debuginfo(x86-64) = 1.0-1.el7.centosRequires(rpmlib): rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1 rpmlib(CompressedFileNames) <= 3.0.4-1Checking for unpackaged file(s): /usr/lib/rpm/check-files /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64Wrote: /builddir/build/RPMS/cello-1.0-1.el7.centos.x86_64.rpmwarning: Could not canonicalize hostname: rhel7Wrote: /builddir/build/RPMS/cello-debuginfo-1.0-1.el7.centos.x86_64.rpmExecuting(%clean): /bin/sh -e /var/tmp/rpm-tmp.JuPOtY+ umask 022+ cd /builddir/build/BUILD+ cd cello-1.0+ /usr/bin/rm -rf /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64+ exit 0Finish: rpmbuild cello-1.0-1.el7.src.rpmFinish: build phase for cello-1.0-1.el7.src.rpmINFO: Done(/home/admiller/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm) Config(epel-7-x86_64) 0 minutes 16 secondsINFO: Results and/or logs in: /var/lib/mock/epel-7-x86_64/resultFinish: run

As you can see,mock is a fairly verbose tool. You will also notice a lot ofyum ordnf output (depending on RHEL7,CentOS7, or Fedora mock target) that is not found in this output which wasomitted for brevity and is often omitted after you have done an--init on amock target, such asmock -r epel-7-x86_64 --init which will pre-downloadall the required packages, cache them, and pre-stage the build chroot.

For more information, please consult theMock upstreamdocumentation.

Version Control Systems

When working with RPMs, it is often desireable to utilize aVersion Control System (VCS) suchasgit for managing components of the software we arepackaging. Something to note is that storing binary files in a VCS is notfavorable because it will drastically inflate the size of the source repositoryas these tools are engineered to handle differentials in files (often optimizedfor text files) and this is not something that binary files lend themselves toso normally each whole binary file is stored. As a side effect of this there aresome clever utilities that are popular among upstream Open Source projects thatwork around this problem by either storing the SPEC file where the source codeis in a VCS (i.e. - it is not in a compressed archive for redistribution) orplace only the SPEC file and patches in the VCS and upload the compressedarchive of the upstream release source to what is called a “look aside cache”.

In this section we will cover two different options for using a VCS system,git, for managing the contents that will ultimately beturned into a RPM package. One is calledtitoand the other isdist-git.

Note
For the duration of this section you will need to install thegitpackage on you system in order to follow along.

tito

Tito is an utility that assumes all the source code for the software that isgoing to be packaged is already in agit source controlrepository. This is good for those practicing a DevOps workflow as it allows forthe team writing the software to maintain their normalBranchingWorkflow. Tito will then allow for the software to be incrementally packaged,built in an automated fashion, and still provide a native installationexperience forRPM based systems.

Note
Thetito package is available inFedora as well as in theEPEL repository for use on RHEL 7 andCentOS 7.

Tito operates based ongittags and will manage tags for you if you elect to allow it, but can optionallyoperate under whatever tagging scheme you prefer as this functionality isconfigurable.

Let’s explore a little bit about tito by looking at an upstream project alreadyusing it. We will actually be using the upstream git repository of the projectthat is our next section’s subject,dist-git. Since this project ispublicly hosted onGitHub, let’s go ahead and clone the gitrepo.

$ git clone https://github.com/release-engineering/dist-git.gitCloning into 'dist-git'...remote: Counting objects: 425, done.remote: Total 425 (delta 0), reused 0 (delta 0), pack-reused 425Receiving objects: 100% (425/425), 268.76 KiB | 0 bytes/s, done.Resolving deltas: 100% (184/184), done.Checking connectivity... done.$ cd dist-git/$ ls *.specdist-git.spec$ tree rel-eng/rel-eng/├── packages│   └── dist-git└── tito.props1 directory, 2 files

As we can see here, the spec file is at the root of the git repository and thereis arel-eng directory in the repository which is used by tito for generalbook keeping, configuration, and various advanced topics like custom titomodules. We can see in the directory layout that there is a sub-directoryentitledpackages which will store a file per package that tito manages inthe repository as you can have many RPMs in a single git repository and titowill handle that just fine. In this scenario however, we see only a singlepackage listing and it should be noted that it matches the name of our specfile. All of this is setup by the commandtito init when the developers ofdist-git first initializedtheir git repo to be managed by tito.

If we were to follow a common workflow of a DevOps Practitioner then we wouldlikely want to use this as part of aContinuous Integration(CI) orContinuous Delivery(CD) process. What we can do in that scenario is perform what is known as a“test build” to tito, we can even use mock to do this. We could then use theoutput as the installation point for some other component in the pipeline. Belowis a simple example of commands that could accomplish this and they could beadapted to other environments.

$ tito build --test --srpmBuilding package [dist-git-0.13-1]Wrote: /tmp/tito/dist-git-git-0.efa5ab8.tar.gzWrote: /tmp/tito/dist-git-0.13-1.git.0.efa5ab8.fc23.src.rpm$ tito build --builder=mock --arg mock=epel-7-x86_64 --test --rpmBuilding package [dist-git-0.13-1]Creating rpms for dist-git-git-0.efa5ab8 in mock: epel-7-x86_64Wrote: /tmp/tito/dist-git-git-0.efa5ab8.tar.gzWrote: /tmp/tito/dist-git-0.13-1.git.0.efa5ab8.fc23.src.rpmUsing srpm: /tmp/tito/dist-git-0.13-1.git.0.efa5ab8.fc23.src.rpmInitializing mock...Installing deps in mock...Building RPMs in mock...Wrote:  /tmp/tito/dist-git-selinux-0.13-1.git.0.efa5ab8.el7.centos.noarch.rpm  /tmp/tito/dist-git-0.13-1.git.0.efa5ab8.el7.centos.noarch.rpm$ sudo yum localinstall /tmp/tito/dist-git-*.noarch.rpmLoaded plugins: product-id, search-disabled-repos, subscription-managerExamining /tmp/tito/dist-git-0.13-1.git.0.efa5ab8.el7.centos.noarch.rpm: dist-git-0.13-1.git.0.efa5ab8.el7.centos.noarchMarking /tmp/tito/dist-git-0.13-1.git.0.efa5ab8.el7.centos.noarch.rpm to be installedExamining /tmp/tito/dist-git-selinux-0.13-1.git.0.efa5ab8.el7.centos.noarch.rpm: dist-git-selinux-0.13-1.git.0.efa5ab8.el7.centos.noarchMarking /tmp/tito/dist-git-selinux-0.13-1.git.0.efa5ab8.el7.centos.noarch.rpm to be installedResolving Dependencies--> Running transaction check---> Package dist-git.noarch 0:0.13-1.git.0.efa5ab8.el7.centos will be installed

Note that the final command would need to be run with either sudo or rootpermissions and that much of the output has been omitted for brevity as thedependency list is quite long.

This concludes our simple example of how to use tito but it has many amazingfeatures for traditional Systems Administrators, RPM Packagers, and DevOpsPractitioners alike. I would highly recommend consulting the upstreamdocumentation found at thetito GitHub site for more information on how toquickly get started using it for your project as well as various advancedfeatures it offers.

dist-git

Thedist-git utility takes aslightly different approach from that oftitosuch that instead of keeping the raw source code ingit itinstead will keep spec files and patches in a git repository and upload thecompressed archive of the source code to what is known as a “look-aside cache”.The “look-aside-cache”is a term that was coined by the use of RPM Build Systemsstoring large files like these “on the side”. A system like this is generallytied to a proper RPM Build System such asKoji.The build system is then configured to pull the items that are listed asSourceX entries in the spec files in from this look-aside-cache, while thespec and patches remain in a version control system. There is also a helpercommand line tool to assist in this.

In an effort to not duplicate documentation, for more information on how tosetup a system such as this please refer to the upstreamdist-git docs.

More on Macros

There are many built-in RPM Macros and we will cover a few in the followingsection, however an exhaustive list can be found at theRPM Official Documentation.

There are also macros that are provided by yourLinux Distribution, we will cover some ofthose provided byFedora,CentOS andRHEL in this section aswell as provide information on how to inspect your system to learn about othersthat we don’t cover or for discovering them on other RPM-based LinuxDistributions.

Defining Your Own Macros

You can define your own macros. Below is an excerpt from theRPM Official Documentation, which providesa comprehensive reference on macros capabilities.

To define a macro, use:

%global <name>[(opts)] <body>

All whitespace surrounding\ is removed. Name may be composed of alphanumericcharacters, and the character_ and must be at least 3 characters in length. Amacro without an(opts) field is “simple” in that only recursive macroexpansion is performed. A parameterized macro contains an(opts) field. Theopts (the string between parentheses) is passed exactly as is to getopt(3) forargc/argv processing at the beginning of a macro invocation.

Note

Older RPM SPEC files may use the%define <name> <body> macro pattern. Thedifferences between%define and%global macros are as follows:

  • %define has local scope, which means that it applies only to a specifiedpart of a SPEC file. In addition, the body of a%define macro is expanded whenused—​it is lazily evaluated.

  • %global has global scope, which means that it applies to an entire SPECfile. In addition, the body of a%global macro is expanded at definition time.

Examples:

%global githash 0ec4e58%global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")
Note
Macros are always evaluated, even in comments. Sometimes it is harmless.But in the second example, we are executing python command to get the content ofa macro. This command will be executed even when you comment out the macro. Orwhen you put the name of the macro into %changelog. To comment out macro, use%%. For example:%%global.

%setup

Macro%setup can be used to build the package with source code tarballs.Standard behavior of the%setup macro can be seen in therpmbuildoutput. At the beginning of each phase macro outputsExecuting(%something).For example:

Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.DhddsG

The shell output is set withset -x enabled. To see the content of/var/tmp/rpm-tmp.DhddsG use the--debug option, sincerpmbuild deletestemporary files after successful build. This displays the setup of environmentvariables, 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 $STATUSficd 'cello-1.0'/usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .

The%setup ensures that we are working in the right directory, removesresidues of previous builds, unpacks the source tarball, and sets up somedefault privileges. There are multiple options to adjust the behavior of the%setup macro.

%setup -q

Option-q limits verbosity of%setup macro. Onlytar -xof isexecuted instead oftar -xvvof. This option has to be used as first.

%setup -n

In some cases, the directory from expanded tarball has a different name thanexpected%{name}-%{version}. This can lead to an error of the%setupmacro. The name of a directory has to be specified by-n directory_nameoption.

For example, if the package name iscello, but the source code is archivedinhello-1.0.tgz and containedhello/ directory, the SPEC file contentneeds to be:

Name: celloSource0: https://example.com/%{name}/release/hello-%{version}.tar.gz…%prep%setup -n hello
%setup -c

The-c option can be used if the source code tarball does not contain anysubdirectories and after unpacking, files from an archive fill the currentdirectory. The-c option creates the directory and steps into the archiveexpansion. An illustrative example:

/usr/bin/mkdir -p cello-1.0cd 'cello-1.0'

The directory is not changed after archive expansion.

%setup -D and -T

-D option disables deleting of source code directory. This option is usefulif%setup macro is used several times. Essentially,-D option means thatfollowing lines are not used:

rm -rf 'cello-1.0'

The-T option disables expansion of the source code tarball by removing thefollowing line from the script:

/usr/bin/gzip -dc '/builddir/build/SOURCES/cello-1.0.tar.gz' | /usr/bin/tar -xvvof -
%setup -a and -b

Options-a and-b expand specific sources.

  • Option-b (which stands forbefore) expands specific sources beforeentering the working directory.

  • Option-a (which stands forafter) expands those sources after entering.Their arguments are source numbers from the spec file preamble.

For example, let’s say thecello-1.0.tar.gz archive contains emptyexamples directory, and the examples are shipped in separateexamples.tar.gz tarball and they expand into the directory of the same name.In this case use-a 1, as we want to expandSource1 after entering theworking directory:

Source0: https://example.com/%{name}/release/%{name}-%{version}.tar.gzSource1: examples.tar.gz…%prep%setup -a 1

But if the examples were in the separatecello-1.0-examples.tar.gz tarball,which expands intocello-1.0/examples, use-b 1 options, since theSource1 should be expanded before entering the working directory:

Source0: https://example.com/%{name}/release/%{name}-%{version}.tar.gzSource1: %{name}-%{version}-examples.tar.gz…%prep%setup -b 1

You can also use a combination of all these options.

%files

Common “advanced” RPM Macros needed in the%files section are as follows:

Macro

Definition

%license

This identifies the file listed as a LICENSE file and it will be installed and labeled as such by RPM. Example:%license LICENSE

%doc

This identifies the file listed as documentation and it will be installed and labeled as such by RPM. This is often used not only for documentation about the software being packaged but also code examples and various items that should accompany documentation. In the event code examples are included, care should be taken to remove executable mode from the file. Example:%doc README

%dir

Identifies that the path is a directory that should be owned by this RPM. This is important so that the RPM file manifest accurately knows what directories to clean up on uninstall. Example:%dir %{_libdir}/%{name}

%config(noreplace)

Specifies 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. In the event that there is a change, the file will be created with.rpmnew appended to the end of the filename upon upgrade or install so that the pre-existing or modified file on the target system is not modified. Example:%config(noreplace) %{_sysconfdir}/%{name}/%{name}.conf

Built-In Macros

Your system has many built-in RPM Macros and the fastest way to view them all isto simply run therpm --showrc command. Note that this will contain a lot ofoutput so it is often used in combination with a pipe togrep.

You can also find information about the RPMs macros that come directly with yoursystem’s version of RPM by looking at the output of therpm -ql rpm takingnote of the files titledmacros in the directory structure.

RPM Distribution Macros

Different distributions will supply different sets of recommended RPM Macrosbased on the language implementation of the software being packaged or thespecific guidelines of the distribution in question.

These are often provided as RPM Packages themselves and can be installed withthe distribution package manager, such asyum ordnf. The macro files themselvesonce installed can be found in/usr/lib/rpm/macros.d/ and will be includedin therpm --showrc output by default once installed.

One primary example of this is theFedora PackagingGuidelines section pertaining specifically toApplicationSpecific Guidelines which at the time of this writing has over 60 differentsets of guidelines along with associated RPM Macro sets for subject matterspecific RPM Packaging.

One example of this kind of RPMs would be forPythonversion 2.x and if we have thepython2-rpm-macros package installed(available in EPEL for RHEL 7 and CentOS 7), we have a number of python2specific macros available to us.

$ rpm -ql python2-rpm-macros/usr/lib/rpm/macros.d/macros.python2$ rpm --showrc | grep python2-14: __python2  /usr/bin/python2CFLAGS="%{optflags}" %{__python2} %{py_setup} %{?py_setup_args} build --executable="%{__python2} %{py2_shbang_opts}" %{?1}CFLAGS="%{optflags}" %{__python2} %{py_setup} %{?py_setup_args} install -O1 --skip-build --root %{buildroot} %{?1}-14: python2_sitearch   %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))")-14: python2_sitelib    %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")-14: python2_version    %(%{__python2} -c "import sys; sys.stdout.write('{0.major}.{0.minor}'.format(sys.version_info))")-14: python2_version_nodots     %(%{__python2} -c "import sys; sys.stdout.write('{0.major}{0.minor}'.format(sys.version_info))")

The above output displays the raw RPM Macro definitions, but we are likely moreinterested in what these will evaluate to which we can do withrpm --eval inorder to determine what they do as well as how they may be helpful to us whenpackaging RPMs.

$ rpm --eval %{__python2}/usr/bin/python2$ rpm --eval %{python2_sitearch}/usr/lib64/python2.7/site-packages$ rpm --eval %{python2_sitelib}/usr/lib/python2.7/site-packages$ rpm --eval %{python2_version}2.7$ rpm --eval %{python2_version_nodots}27

Custom Macros

You can override the distribution macros in the~/.rpmmacros file. Anychanges you make will affect every build on your machine.

There are several macros you can use to override:

%_topdir /opt/some/working/directory/rpmbuild

You can create thisdirectory, including all subdirectories using therpmdev-setuptree utility.The value of this macro is by default~/rpmbuild.

%_smp_mflags -l3

This macro is often used to pass to Makefile, for examplemake %{?_smp_mflags}, and to set a number of concurrent processes during thebuild phase. By default, it is set to-jX, whereX is a number of cores.If you alter the number of cores, you can speed up or slow down a build ofpackages.

While you can define any new macros in the~/.rpmmacros file, this isdiscouraged, because those macros would not be present on other machines, whereusers may want to try to rebuild your package.

Epoch, Scriptlets, and Triggers

There are various topics in the world of RPM SPEC files that are consideredadvanced because they have implications on not only the SPEC file, how thepackage is built, but also on the end machine that the resulting RPM isinstalled upon. In this section we will cover the most common of these such asEpoch, Scriptlets, and Triggers.

Epoch

First on the list isEpoch, epoch is a way to define weighted dependenciesbased on version numbers. It’s default value is 0 and this is assumed if anEpoch directive is not listed in the RPM SPEC file. This was not covered inthe SPEC File section of this guide because it is almost always a bad idea tointroduce an Epoch value as it will skew what you would normally otherwiseexpect RPM to do when comparing versions of packages.

For example if a packagefoobar withEpoch: 1 andVersion: 1.0 wasinstalled and someone else packagedfoobar withVersion: 2.0 but simplyomitted theEpoch directive either because they were unaware of itsnecessity or simply forgot, that new version would never be considered an updatebecause the Epoch version would win out over the traditionalName-Version-Release marker that signifies versioning for RPM Packages.

This approach is generally only used when absolutely necessary (as a lastresort) to resolve an upgrade ordering issue which can come up as a side effectof upstream software changing versioning number schemes or versionsincorporating alphabetical characters that can not always be compared reliablybased on encoding.

Scriptlets and Triggers

In RPM Packages, there are a series of directives that can be used to inflictnecessary or desired change on a system during install time of the RPM. Theseare calledscriptlets.

One primary example of when and why you’d want to do this is when a systemservice RPM is installed and it provides asystemdunit file.At install time we will need to notifysystemd that there is a new unitso that the system administrator can run a command similar tosystemctl startfoo.service after the fictional RPMfoo (which provides some servicedaemon in this example) has been installed. Similarly, we would need to inverseof this action upon uninstallation so that an administrator would not get errorsdue to the daemon’s binary no longer being installed but the unit file stillexisting in systemd’s running configuration.

There are a small handful of common scriptlet directives, they are similar tothe “section headers” like%build or%install in that they are definedby multi-line segments of code, often written as standardPOSIXshell script but can be a few different programming languages such that RPM forthe target machine’s distribution is configured to allow them. An exhaustivelist of these available languages can be found in theRPM OfficialDocumentation.

Scriptlet directives are as follows:

Directive

Definition

%pre

Scriptlet that is executed just before the package is installed on the target system.

%post

Scriptlet that is executed just after the package is installed on the target system.

%preun

Scriptlet that is executed just before the package is uninstalled from the target system.

%postun

Scriptlet that is executed just after the package is uninstalled from the target system.

It is also common for RPM Macros to exist for this function. In our previousexample we discussedsystemdneeding to be notified about a newunit file,this is easily handled by the systemd scriptlet macros as we can see from thebelow example output. More information on this can be found in theFedora systemd PackagingGuidelines.

$ 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_requiresRequires(post): systemdRequires(preun): systemdRequires(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_preunsystemd-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

Another item that provides even more fine grained control over the RPMTransaction as a whole is what is known astriggers. These are effectivelythe same thing as a scriptlet but are executed in a very specific order ofoperations during the RPM install or upgrade transaction allowing for a morefine grained control over the entire process.

The order in which each is executed and the details of which are provided below.

all-%pretrans...any-%triggerprein (%triggerprein from other packages set off by new install)new-%triggerpreinnew-%pre      for new version of package being installed...           (all new files are installed)new-%post     for new version of package being installedany-%triggerin (%triggerin from other packages set off by new install)new-%triggerinold-%triggerunany-%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 removedold-%triggerpostunany-%triggerpostun (%triggerpostun from other packages set off by old un            install)...all-%posttrans

The above items are from the included RPM documentation found in/usr/share/doc/rpm/triggers on Fedora systems and/usr/share/doc/rpm-4.*/triggers on RHEL 7 and CentOS 7 systems.

Using Non-Shell Scripts in SPEC File

A scriptlet option,-p, in a SPEC file allows to invoke a specific interpreterinstead of the default-p /bin/sh. An illustrative example is a script,which prints out a message after the installation ofpello.py.

  1. Open thepello.spec file.

  2. Find the following line:

    install -m 0644 %{name}.py* %{buildroot}/usr/lib/%{name}/

    Under this line, insert the following code:

    %post -p /usr/bin/python3print("This is {} code".format("python"))
  3. Build your package according to theBuilding RPMS chapter.

  4. Install your package:

    # dnf install /home/<username>/rpmbuild/RPMS/noarch/pello-0.1.1-1.fc27.noarch.rpm

    The output of this command is the following message after the installation:

    Installing       : pello-0.1.1-1.fc27.noarch                              1/1Running scriptlet: pello-0.1.1-1.fc27.noarch                              1/1This is python code
Note
  • To use a Python 3 script: Write a line%post -p /usr/bin/python3 underthe lineinstall -m in a SPEC file.

  • To use a Lua script: Write a line%post -p <lua> under the lineinstall -m in a SPEC file.

  • This way any interpreter can be specified in the SPEC file.

RPM Conditionals

RPM Conditionals enable the conditional inclusion of various sections of theSPEC file.

Most commonly, conditional inclusions deal with:

  • architecture-specific sections

  • operating system-specific sections

  • compatibility issues between various versions of operating systems

  • existence and definition of macros

RPM Conditionals Syntax

Ifexpression is true, then do some action:

%if expression...%endif

Ifexpression is true, then do some action, in other case, do another action:

%if expression...%else...%endif

RPM Conditionals Examples

The%if Conditional
%if 0%{?rhel} == 6sed -i '/AS_FUNCTION_DESCRIBE/ s/^/#/' configure.insed -i '/AS_FUNCTION_DESCRIBE/ s/^/#/' acinclude.m4%endif

This conditional handles compatibility between RHEL6 and other operating systemsin terms of support of the AS_FUNCTION_DESCRIBE macro. When the package is buildfor RHEL, the%rhel macro is defined and it is expanded to RHEL version. Ifits value is 6, meaning the package is build for RHEL 6, then the references toAS_FUNCTION_DESCRIBE, which is not supported by RHEL6, are deleted fromautoconfig scripts.

%if 0%{?el6}%global ruby_sitearch %(ruby -rrbconfig -e 'puts Config::CONFIG["sitearchdir"]')%endif

This conditional handles compatibility between Fedora version 17 and newer andRHEL6 in terms of support of the%ruby_sitearch macro. Fedora version 17 andnewer defines%ruby_sitearch by default, but RHEL6 does not support thismacro. The conditional checks whether the operating system is RHEL6. If it is,%ruby_sitearch is defined explicitly. Note that0%{?el6} has the samemeaning as0%{?rhel} == 6 from the previous example, and it tests whether apackage is built on RHEL6.

%if 0%{?fedora} >= 19%global with_rubypick 1%endif

This conditional handles support for the rubypick tool. If the operating systemis Fedora version 19 or newer, rubypick is supported.

%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 the macros. If the%milestone or the%revision macros are set, the%ruby_archive macro, which defines the name ofthe upstream tarball, is redefined.

Specialized variants of%if Conditional

The%ifarch conditional,%ifnarch conditional and%ifos conditional arespecialized variants of the%if conditionals. These variants are commonlyused, so they have their own macros.

The%ifarch Conditional

The%ifarch conditional is used to begin a block of the SPEC file that isarchitecture-specific. It is followed by one or more architecture specifiers,each separated by commas or whitespace.

%ifarch i386 sparc...%endif

All the contents of the SPEC file between%ifarch and%endif are processedonly 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.

%ifnarch alpha...%endif

All the contents of the SPEC file between%ifnarch and%endif are processedonly if not being done on a Digital Alpha/AXP-based system.

The%ifos Conditional

The%ifos conditional is used to control processing based on the operatingsystem of the build. It can be followed by one or more operating system names.

%ifos linux...%endif

All the contents of the SPEC file between%ifos and%endif are processedonly if the build was done on a Linux system.

Appendix A: New features of RPM in RHEL 7

This list documents most noticable changes in RPM packaging between Red HatEnterprise Linux 6 and 7.

  • A new command,rpmkeys, used for keyring import and signature verificationhas been added.

  • A new command,rpmspec, used for spec queries and parsed output has beenadded.

  • A new command,rpmsign, used for package signing has been added.

  • Theposix.exec() andos.exit() extensions embedded in%{lua:…​}scripts fail the script unless called from a child process created with theposix.fork() scriptlet.

  • The%pretrans scriptlet failure causes the package installation to beskipped.

  • Scriptlets can be macro-expanded and queryformat-expanded at runtime.

  • Pre-transaction and post-transaction scriptlet dependencies can now becorrectly expressed withRequires(pretrans) andRequires(posttrans)scriptlets.

  • TheOrderWithRequires tag for supplying additional ordering hints has beenadded. The tag followsRequires tag syntax, but does not generate actualdependencies. The ordering hints are treated as if they wereRequires whencalculating the transaction order, only if the involved packages are present inthe same transaction.

  • The%license flag can be used in the%files section. This flag can beused similar to the%doc flag to mark files as licenses, which need to beinstalled despite the--nodocs option.

  • The%autosetup macro for automating patch application, with optionaldistributed version control system integration has been added.

  • The automatic dependency generator has been rewritten into extensible andcustomizable rule based system with built-in filtering.

  • The OpenPGP V3 public keys are no longer supported.

Appendix B: References

Below are references to various topics of interest around RPMs, RPM packaging,and RPM building. Some of these are advanced and extend far beyond theintroductory material included in this guide.

Software Collections -SoftwareCollections.org is an open-source project for building and distributingcommunity-supported Software Collections (SCLs) for Red Hat Enterprise Linux,Fedora, CentOS, and Scientific Linux.

CreatingRPM package - Step-by-step guide for learning basics of RPM packaging.

Packaging software withRPM, Part 1,Part 2,Part 3 - IBM RPMpackaging guide.

RPM Documentation - The official RPMdocumentation.

Fedora PackagingGuidelines - The official packaging guidelines for Fedora, useful for allRPM-based distributions.

rpmfluff - python library for building RPM packages, and sabotaging them so they are broken in controlled ways.

Appendix C: Acknowledgements

Certain portions of this text first appeared in theRPM Packaging Guide. Copyright© 2023 Adam Miller and others. Licensed under aCreative CommonsAttribution-ShareAlike 3.0 Unported License.

Last updated 2023-02-20 15:04:03 UTC

[8]ページ先頭

©2009-2025 Movatter.jp