RPM Packaging Guide
This tutorial explains packaging RPMs for the Red Hat family of Linux distributions, primarily:
These distributions use the RPM Packaging Format.
While these distributions are the target environment, this guide is mostly applicable to all RPM based distributions. However, the instructions need to be adapted for distribution-specific features, such as prerequisite installation items, guidelines, or macros.
This tutorial assumes no previous knowledge about packaging software for any Operating 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 of Linux and Package Managers. |
RPM Packages
This section covers the basics of the RPM packaging format. See Advanced Topics for more advanced information.
What is an RPM?
An RPM package is simply a file containing other files and information about
them needed by the system. Specifically, an RPM package consists of the
cpio
archive, which contains the files, and the RPM header, which contains metadata
about the package. The rpm package manager uses this metadata to determine
dependencies, 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 different contents and serve different purposes. An SRPM contains source code, optionally patches to it, and a SPEC file, which describes how to build the source code into a binary RPM. A binary RPM contains the binaries built from the sources and patches.
RPM Packaging Tools
The rpmdevtools package, installed in Prerequisites, provides several
utilities 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 help dialogs.
RPM Packaging Workspace
To set up a directory layout that is the RPM packaging workspace, use the
rpmdev-setuptree utility:
$ rpmdev-setuptree
$ tree ~/rpmbuild/
/home/user/rpmbuild/
|-- BUILD
|-- RPMS
|-- SOURCES
|-- SPECS
`-- SRPMS
5 directories, 0 files
The created directories serve these purposes:
Directory |
Purpose |
BUILD |
When packages are built, various |
RPMS |
Binary RPMs are created here, in subdirectories for different architectures, for example in subdirectories |
SOURCES |
Here, the packager puts compressed source code archives and patches. The |
SPECS |
The packager puts SPEC files here. |
SRPMS |
When |
What is a SPEC File?
A SPEC file can be thought of as the "recipe" that the rpmbuild utility uses
to actually build an RPM. It tells the build system what to do by defining
instructions in a series of sections. The sections are defined in the
Preamble and the Body. The Preamble contains a series of metadata
items that are used in the Body. The Body contains the main part of the
instructions.
Preamble Items
This table lists the items used in the Preamble section of the RPM SPEC file:
SPEC Directive |
Definition |
|
The base name of the package, which should match the SPEC file name. |
|
The upstream version number of the software. |
|
The number of times this version of the software was released. Normally, set the initial value to 1%{?dist}, and increment it with each new release of the package. Reset to 1 when a new |
|
A brief, one-line summary of the package. |
|
The license of the software being packaged. For packages distributed in community distributions such as Fedora this must be an open source license abiding by the specific distribution’s licensing guidelines. |
|
The full URL for more information about the program. Most often this is the upstream project website for the software being packaged. |
|
Path or URL to the compressed archive of the upstream source code (unpatched, patches are handled elsewhere). This should point to an accessible and reliable storage of the archive, for example, the upstream page and not the packager’s local storage. If needed, more SourceX directives can be added, incrementing the number each time, for example: Source1, Source2, Source3, and so on. |
|
The name of the first patch to apply to the source code if necessary. If needed, more PatchX directives can be added, incrementing the number each time, for example: Patch1, Patch2, Patch3, and so on. |
|
If the package is not architecture dependent, for example, if written entirely in an interpreted programming language, set this to |
|
A comma- or whitespace-separated list of packages required for building the program written in a compiled language. There can be multiple entries of |
|
A comma- or whitespace-separated list of packages required by the software to run once installed. There can be multiple entries of |
|
If a piece of software can not operate on a specific processor architecture, you can exclude that architecture here. |
The Name, Version, and Release directives comprise the file name of
the RPM package. RPM Package Maintainers and Systems Administrators often call
these three directives N-V-R or NVR, because RPM package filenames have
the NAME-VERSION-RELEASE format.
You can get an example of an NAME-VERSION-RELEASE by querying using rpm
for a specific package:
$ rpm -q python
python-2.7.5-34.el7.x86_64
Here, python is the Package Name, 2.7.5 is the Version, and 34.el7
is the Release. The final marker is x86_64, which signals the architecture.
Unlike the NVR, the architecture marker is not under direct control of the
RPM packager, but is defined by the rpmbuild build environment. The
exception to this is the architecture-independent noarch package.
Body Items
This table lists the items used in the Body section of the RPM SPEC file:
SPEC Directive |
Definition |
|
A full description of the software packaged in the RPM. This description can span multiple lines and can be broken into paragraphs. |
|
Command or series of commands to prepare the software to be built, for example, unpacking the archive in |
|
Command or series of commands for actually building the software into machine code (for compiled languages) or byte code (for some interpreted languages). |
|
Command or series of commands for copying the desired build artifacts from the |
|
Command or series of commands to test the software. This normally includes things such as unit tests. |
|
The list of files that will be installed in the end user’s system. |
|
A record of changes that have happened to the package between different |
Advanced items
The SPEC file can also contain advanced items. For example, a SPEC file can have scriptlets and triggers. They take effect at different points during the installation process on the end user’s system (not the build process).
See the Scriptlets and Triggers for advanced topics.
BuildRoots
In the context of RPM packaging, "buildroot" is a chroot environment. This means that the build artifacts are placed here 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 artifacts should comply with the filesystem hierarchy standard of the end user’s system.
The files in "buildroot" are later put into a cpio archive, which becomes the main part of the RPM. When RPM is installed on the end user’s system, these files are extracted in the root directory, preserving the correct hierarchy.
|
Note |
Starting from Red Hat Enterprise Linux 6 release, the |
RPM Macros
An rpm macro is a straight text substitution that can be conditionally assigned based on the optional evaluation of a statement when certain built-in functionality is used. What this means is that you can have RPM perform text substitutions for you so that you don’t have to.
This is useful when, for example, referencing the packaged software Version
multiple times in the SPEC file. You define Version only once - in the
%{version} macro. Then use %{version} throughout the SPEC file. Every
occurrence will be automatically substituted by Version you defined
previously.
|
Note |
If you see an unfamiliar macro, you can evaluate it with: For example:
|
A common macro is %{?dist}, which signifies the “distribution tag”. It
signals 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, see More on Macros.
Working with SPEC files
A big part of packaging software into RPMs is editing the SPEC file. In this section we discuss how to create and modify a spec file.
To package new software, you need to create a new SPEC file. Instead of writing
it manually from scratch, use the rpmdev-newspec utility. It creates an
unpopulated SPEC file, and you fill in the necessary directives and fields.
For this tutorial, we use the three example implementations of the 'Hello World!' program created in Preparing 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 file
with their own SPEC template. The rpmdev-newspec provides an editor-agnostic
method, which is why it is used in this guide.
|
$ cd ~/rpmbuild/SPECS
$ rpmdev-newspec bello
bello.spec created; type minimal, rpm version >= 4.11.
$ rpmdev-newspec cello
cello.spec created; type minimal, rpm version >= 4.11.
$ rpmdev-newspec pello
pello.spec created; type minimal, rpm version >= 4.11.
The ~/rpmbuild/SPECS/ directory now contains three SPEC files named
bello.spec, cello.spec, and pello.spec.
Examine the files. The directives in them represent the ones described in the What is a SPEC File? section. In the following sections, you will populate these SPEC files.
|
Note |
The
|
There are three examples below. Each one is fully described, so you can go to a specific one if it matches your packaging needs. Or, read them all to fully explore 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 the bello bash shell script from
Preparing Software for Packaging.
Ensure that you have:
-
Placed
bellosource code into~/rpmbuild/SOURCES/. See Working with SPEC files. -
Created the unpopulated SPEC file
~/rpmbuild/SPECS/bello.spec. The file has these contents:Name: bello Version: Release: 1%{?dist} Summary: License: URL: Source0: BuildRequires: Requires: %description %prep %setup -q %build %configure make %{?_smp_mflags} %install rm -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 creating bello RPMs:
-
Populate the
Name,Version,Release, andSummarydirectives:-
The
Namewas already specified as an argument torpmdev-newspec. -
Set the
Versionto match the “upstream” release version of thebellosource code,0.1. -
The
Releaseis automatically set to1%{?dist}, which is initially1. Increment the initial value whenever updating the package without a change in the upstream releaseVersion- such as when including a patch. ResetReleaseto1when a new upstream release happens, for example, if bello version0.2is released. The disttag macro is covered in RPM Macros. -
The
Summaryis a short, one-line explanation of what this software is.After your edits, the first section of the SPEC file should resemble:
Name: bello Version: 0.1 Release: 1%{?dist} Summary: Hello World example implemented in bash script
-
-
Populate the
License,URL, andSource0directives:-
The
Licensefield is the Software License associated with the source code from the upstream release.For example, use
GPLv3+. -
The
URLfield 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}. -
The
Source0field provides URL to the upstream software source code. It should link directly to the version of software that is being packaged. In this example, we can usehttps://example.com/bello/releases/bello-0.1.tar.gz. Instead, use the %{name} macro. Also, use the %{version} macro to accommodate for changes 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: bello Version: 0.1 Release: 1%{?dist} Summary: Hello World example implemented in bash script License: GPLv3+ URL: https://example.com/%{name} Source0: https://example.com/%{name}/release/%{name}-%{version}.tar.gz
-
-
Populate the
BuildRequiresandRequiresdirectives and include theBuildArchdirective:-
BuildRequiresspecifies build-time dependencies for the package. There is no building step forbello, because bash is a raw interpreted programming language, and the files are simply installed to their location on the system. Just delete this directive. -
Requiresspecifies run-time dependencies for the package. Thebelloscript requires only thebashshell environment to execute, so specifybashin this directive. -
Since this is software written in an interpreted programming language with no natively compiled extensions, add the
BuildArchdirective with thenoarchvalue. This tells RPM that this package does not need to be bound to the processor architecture on which it is built.After your edits, the first section of the SPEC file should resemble:
Name: bello Version: 0.1 Release: 1%{?dist} Summary: Hello World example implemented in bash script License: GPLv3+ URL: https://example.com/%{name} Source0: https://example.com/%{name}/release/%{name}-%{version}.tar.gz Requires: bash BuildArch: noarch
-
-
Populate the
%description,%prep,%build,%install,%files, and%licensedirectives. These directives can be thought of as “section headings”, because they are directives that can define multi-line, multi-instruction, or scripted tasks to occur.-
The
%descriptionis a longer, fuller description of the software thanSummary, containing one or more paragraphs. In our example we will use only a short description. -
The
%prepsection specifies how to prepare the build environment. This usually involves expansion of compressed archives of the source code, application of patches, and, potentially, parsing of information provided in the source code for use in a later portion of the SPEC. In this section we simply use the built-in macro%setup -q. -
The
%buildsection specifies how to actually build the software we are packaging. However, since abashdoes not need to be built, simply remove what was provided by the template and leave this section blank. -
The
%installsection contains instructions forrpmbuildon how to install the software, once it has been built, into theBUILDROOTdirectory. This directory is an empty chroot base directory, which resembles the end user’s root directory. Here we should create any directories that will contain the installed files.Since for installing
bellowe only need to create the destination directory and install the executablebashscript file there, we will use theinstallcommand. RPM macros allow us to do this without hardcoding paths.The
%installsection should look like the following after your edits:%install mkdir -p %{buildroot}/%{_bindir} install -m 0755 %{name} %{buildroot}%{_bindir}/%{name} -
The
%filessection specifies the list of files provided by this RPM and their full path location on the end user’s system. Therefore, the listing for thebellofile 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-in macros. This is useful for querying the package file manifest metadata using the
rpmcommand. For example, to indicate that the LICENSE file is a software license file, we use the %license macro.After your edits, the
%filessection looks like this:%files %license LICENSE %{_bindir}/%{name}
-
-
The last section,
%changelog, is a list of datestamped entries for each Version-Release of the package. They log packaging changes, not software changes. Examples of packaging changes: adding a patch, changing the build procedure in%build.Follow this format for the first line:
* Day-of-Week Month Day Year Name Surname <email> - Version-ReleaseFollow this format for the actual change entry:
-
Each change entry can contain multiple items - one for each change
-
Each item starts on a new line.
-
Each item begins with a
-character.
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 for bello. The full SPEC file for bello now resembles:
Name: bello
Version: 0.1
Release: 1%{?dist}
Summary: Hello World example implemented in bash script
License: GPLv3+
URL: https://www.example.com/%{name}
Source0: https://www.example.com/%{name}/releases/%{name}-%{version}.tar.gz
Requires: bash
BuildArch: noarch
%description
The long-tail description for our Hello World Example implemented in
bash script.
%prep
%setup -q
%build
%install
mkdir -p %{buildroot}/%{_bindir}
install -m 0755 %{name} %{buildroot}%{_bindir}/%{name}
%files
%license LICENSE
%{_bindir}/%{name}
%changelog
* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org> - 0.1-1
- First bello package
- Example second item in the changelog for version-release 0.1-1
The next section covers how to build the RPM.
pello
Our second SPEC file will be for our example written in the Python
programming language that you downloaded (or you created a simulated upstream
release in the Preparing Software for Packaging section) and placed its
source code into ~/rpmbuild/SOURCES/ earlier. Let’s go ahead and open the
file ~/rpmbuild/SPECS/pello.spec and start filling in some fields.
Before we start down this path, we need to address something somewhat unique about byte-compiled interpreted software. Since we will be byte-compiling this program, the shebang is no longer applicable because the resulting file will not contain the entry. It is common practice to either have a non-byte-compiled shell script that will call the executable or have a small bit of the Python code that isn’t byte-compiled as the “entry point” into the program’s execution. This might seem silly for our small example but for large software projects with many thousands of lines of code, the performance increase of pre-byte-compiled code is sizeable.
|
Note |
The creation of a script to call the byte-compiled code or having a non-byte-compiled entry point into the software is something that upstream software developers most often address before doing a release of their software to the world, however this is not always the case and this exercise is meant to help address what to do in those situations. For more information on how Python code is normally released and distributed please reference the Software Packaging and Distribution documentation. |
We will make a small shell script to call our byte compiled code to be the entry
point into our software. We will do this as a part of our SPEC file itself in
order to demonstrate how you can script actions inside the SPEC file. We will
cover the specifics of this in the %install section later.
Let’s go ahead and open the file ~/rpmbuild/SPECS/pello.spec and start
filling in some fields.
The following is the output template we were given from rpmdev-newspec.
Name: pello
Version:
Release: 1%{?dist}
Summary:
License:
URL:
Source0:
BuildRequires:
Requires:
%description
%prep
%setup -q
%build
%configure
make %{?_smp_mflags}
%install
rm -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 directives
that rpmdev-newspec has grouped together at the top of the file: Name,
Version, Release, Summary. The Name is already specified because
we provided that information to the command line for rpmdev-newspec.
Let’s set the Version to match what the “upstream” release version of the
pello source code is, which we can observe is 0.1.1 as set by the
example code we downloaded (or we created in the
Preparing Software for Packaging section).
The Release is already set to 1%{?dist} for us, the numerical value
which is initially 1 should be incremented every time the package is updated
for any reason, such as including a new patch to fix an issue, but doesn’t have
a new upstream release Version. When a new upstream release happens (for
example, pello version 0.1.2 were released) then the Release number
should be reset to 1. The disttag of %{?dist} should look familiar
from the previous section’s coverage of RPM Macros.
The Summary should be a short, one-line explanation of what this software
is.
After your edits, the first section of the SPEC file should resemble the following:
Name: pello
Version: 0.1.1
Release: 1%{?dist}
Summary: Hello World example implemented in Python
Now, let’s move on to the second set of directives that rpmdev-newspec has
grouped together in our SPEC file: License, URL, Source0.
The License field is the
Software License associated with
the source code from the upstream release. The exact format for how to label the
License in your SPEC file will vary depending on which specific RPM based
Linux distribution guidelines you are
following, we will use the notation standards in the
Fedora License Guidelines for
this document and as such this field will contain the text GPLv3+
The URL field is the upstream software’s website, not the source code
download link but the actual project, product, or company website where someone
would find more information about this particular piece of software. Since we’re
just using an example, we will call this https://example.com/pello. However,
we will use the RPM macro variable of %{name} in its place for consistency.
The Source0 field is where the upstream software’s source code should be
able to be downloaded from. This URL should link directly to the specific
version of the source code release that this RPM Package is packaging. Once
again, 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 are
possible to change in the future and are potentially even likely to change such
as the release version 0.1.1. We can simplify this by only needing to update
one field in the SPEC file and allowing it to be reused. we will use the value
https://example.com/%{name}/releases/%{name}-%{version}.tar.gz instead of
the hard coded examples string previously listed.
After your edits, the top portion of your spec file should look like the following:
Name: pello
Version: 0.1.1
Release: 1%{?dist}
Summary: Hello World example implemented in Python
License: GPLv3+
URL: https://example.com/%{name}
Source0: https://example.com/%{name}/release/%{name}-%{version}.tar.gz
Next up we have BuildRequires and Requires, each of which define
something that is required by the package. However, BuildRequires is to tell
rpmbuild what is needed by your package at build time and Requires
is what is needed by your package at run time.
In this example we will need the python package in order to perform the
byte-compile build process. We will also need the python package in order to
execute the byte-compiled code at runtime and therefore need to define
python as a requirement using the Requires directive. We will also need
the bash package in order to execute the small entry-point script we will
use here.
Something we need to add here since this is software written in an interpreted
programming language with no natively compiled extensions is a BuildArch
entry that is set to noarch in order to tell RPM that this package does not
need 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 the following:
Name: pello
Version: 0.1.1
Release: 1%{?dist}
Summary: Hello World example implemented in Python
License: GPLv3+
URL: https://example.com/%{name}
Source0: https://example.com/%{name}/release/%{name}-%{version}.tar.gz
BuildRequires: python
Requires: python
Requires: bash
BuildArch: noarch
The following directives can be thought of as “section headings” because they are directives that can define multi-line, multi-instruction, or scripted tasks to occur. We will walk through them one by one just as we did with the previous items.
The %description should be a longer, more full length description of the
software being packaged than what is found in the Summary directive. For the
sake of our example, this isn’t really going to contain much content but this
section can be a full paragraph or more than one paragraph if desired.
The %prep section is where we prepare our build environment or workspace
for building. Most often what happens here is the expansion of compressed
archives of the source code, application of patches, and potentially parsing of
information provided in the source code that is necessary in a later portion of
the 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 the
software we are packaging. Here we will perform a byte-compilation of our
software. For those who read the Preparing Software for Packaging
section, this portion of the example should look familiar.
The %build section of our SPEC file should look as follows.
%build
python -m compileall pello.py
The %install section is where we instruct rpmbuild how to install our
previously built software into the BUILDROOT which is effectively a
chroot
base directory with nothing in it and we will have to
construct any paths or directory hierarchies that we will need in order to
install our software here in their specific locations. However, our RPM Macros
help us accomplish this task without having to hardcode paths.
We had previously discussed that since we will lose the context of a file with the
shebang
line in it when we byte compile that we will need to create
a simple wrapper script in order to accomplish that task. There are many options
on how to accomplish this including, but not limited to, making a separate
script and using that as a separate SourceX directive and the option we’re
going to show in this example which is to create the file in-line in the SPEC
file. The reason for showing the example option that we are is simply to
demonstrate that the SPEC file itself is scriptable. What we’re going to do is
create a small“wrapper script” which will execute the
Python byte-compiled code by using a
here document
. We will also need to actually install the byte-compiled file into a library
directory on the system such that it can be accessed.
|
Note |
You will notice below that we are hard coding the library path. There are various methods to avoid needing to do this, many of which are addressed in Advanced Topics, under the More on Macros section, and are specific to the programming language in which the software that is being packaged was written in. In this example we hard code the path for simplicity as to not cover too many topics simultaneously. |
The %install section should look like the following after your edits:
%install
mkdir -p %{buildroot}/%{_bindir}
mkdir -p %{buildroot}/usr/lib/%{name}
cat > %{buildroot}%{_bindir}/%{name} <<-EOF
#!/bin/bash
/usr/bin/python /usr/lib/%{name}/%{name}.pyc
EOF
chmod 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 RPM
provides and where it’s intended for them to live on the system that the RPM is
installed upon. Note here that this isn’t relative to the %{buildroot} but
the full path for the files as they are expected to exist on the end system
after installation. Therefore, the listing for the pello file we are
installing will be %{_bindir}/pello. We will also need to provide a %dir
listing to define that this package “owns” the library directory we created as
well as all the files we placed in it.
Also, within this section you will sometimes need a built-in macro to provide
context on a file. This can be useful for Systems Administrators and end users
who might want to query the system with rpm about the resulting package. The
built-in macro we will use here is %license which will tell rpmbuild
that 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 that
correlate to a specific Version-Release of the package. This is not meant to be
a log of what changed in the software from release to release, but specifically
to packaging changes. For example, if software in a package needed patching or
there was a change needed in the build procedure listed in the %build
section that information would go here. Each change entry can contain multiple
items 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, the
year, then the contact information for the RPM Packager. From there we have a
- character before the Version-Release, which is an often used convention
but not a requirement. Then finally the Version-Release.
That’s it! We’ve written an entire SPEC file for pello! In the next section we will cover how to build the RPM!
The full SPEC file should now look like the following:
Name: pello
Version: 0.1.1
Release: 1%{?dist}
Summary: Hello World example implemented in python
License: GPLv3+
URL: https://www.example.com/%{name}
Source0: https://www.example.com/%{name}/releases/%{name}-%{version}.tar.gz
BuildRequires: python
Requires: python
Requires: bash
BuildArch: noarch
%description
The long-tail description for our Hello World Example implemented in
Python.
%prep
%setup -q
%build
python -m compileall %{name}.py
%install
mkdir -p %{buildroot}%{_bindir}
mkdir -p %{buildroot}/usr/lib/%{name}
cat > %{buildroot}%{_bindir}/%{name} <<-EOF
#!/bin/bash
/usr/bin/python /usr/lib/%{name}/%{name}.pyc
EOF
chmod 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 the
C
programming
language that we created a simulated upstream release of previously (or you
downloaded) and placed its source code into ~/rpmbuild/SOURCES/ earlier.
Let’s go ahead and open the file ~/rpmbuild/SPECS/cello.spec and start
filling in some fields.
The following is the output template we were given from rpmdev-newspec.
Name: cello
Version:
Release: 1%{?dist}
Summary:
License:
URL:
Source0:
BuildRequires:
Requires:
%description
%prep
%setup -q
%build
%configure
make %{?_smp_mflags}
%install
rm -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 directives
that rpmdev-newspec has grouped together at the top of the file:
Name, Version, Release, Summary. The Name is already
specified because we provided that information to the command line for
rpmdev-newspec.
Let’s set the Version to match what the “upstream” release version of the
cello source code is, which we can observe is 1.0 as set by the example
code we downloaded (or we created in the Preparing Software for Packaging
section).
The Release is already set to 1%{?dist} for us, the numerical value
which is initially 1 should be incremented every time the package is updated
for any reason, such as including a new patch to fix an issue, but doesn’t have
a new upstream release Version. When a new upstream release happens (for
example, cello version 2.0 were released) then the Release number should
be reset to 1. The disttag of %{?dist} should look familiar from the
previous section’s coverage of RPM Macros.
The Summary should be a short, one-line explanation of what this software
is.
After your edits, the first section of the SPEC file should resemble the following:
Name: cello
Version: 1.0
Release: 1%{?dist}
Summary: Hello World example implemented in C
Now, let’s move on to the second set of directives that rpmdev-newspec has
grouped together in our SPEC file: License, URL, Source0. However,
we will add one to this grouping as it is closely related to the Source0 and
that is our Patch0 which will list the first patch we need against our
software.
The License field is the
Software License
associated with the source code
from the upstream release. The exact format for how to label the License in your
SPEC file will vary depending on which specific RPM based
Linux
distribution guidelines you are following, we will use the notation standards in
the Fedora License Guidelines
for this document and as such this field will contain the text GPLv3+
The URL field is the upstream software’s website, not the source code
download link but the actual project, product, or company website where someone
would find more information about this particular piece of software. Since we’re
just using an example, we will call this https://example.com/cello. However,
we will use the rpm macro variable of %{name} in its place for consistency.
The Source0 field is where the upstream software’s source code should be
able to be downloaded from. This URL should link directly to the specific
version of the source code release that this RPM Package is packaging. Once
again, 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 are
possible to change in the future and are potentially even likely to change such
as the release version 1.0. We can simplify this by only needing to update
one field in the SPEC file and allowing it to be reused. we will use the value
https://example.com/%{name}/releases/%{name}-%{version}.tar.gz instead of
the hard coded examples string previously listed.
The next item is to provide a listing for the .patch file we created earlier
such that we can apply it to the code later in the %prep section. We will
need a listing of Patch0: cello-output-first-patch.patch.
After your edits, the top portion of your spec file should look like the following:
Name: cello
Version: 1.0
Release: 1%{?dist}
Summary: Hello World example implemented in C
License: GPLv3+
URL: https://example.com/%{name}
Source0: https://example.com/%{name}/release/%{name}-%{version}.tar.gz
Patch0: cello-output-first-patch.patch
Next up we have BuildRequires and Requires, each of which define
something that is required by the package. However, BuildRequires is to tell
rpmbuild what is needed by your package at build time and Requires
is what is needed by your package at run time.
In this example we will need the gcc and make packages in order to
perform the compilation build process. Runtime requirements are fortunately
handled for us by rpmbuild because this program does not require anything
outside of the core
C
standard libraries and we therefore will not need to
define anything by hand as a Requires and can omit that directive.
After your edits, the top portion of your spec file should look like the following:
Name: cello
Version: 0.1
Release: 1%{?dist}
Summary: Hello World example implemented in C
License: GPLv3+
URL: https://example.com/%{name}
Source0: https://example.com/%{name}/release/%{name}-%{version}.tar.gz
BuildRequires: gcc
BuildRequires: make
The following directives can be thought of as “section headings” because they are directives that can define multi-line, multi-instruction, or scripted tasks to occur. We will walk through them one by one just as we did with the previous items.
The %description should be a longer, more full length description of the
software being packaged than what is found in the Summary directive. For the
sake of our example, this isn’t really going to contain much content but this
section can be a full paragraph or more than one paragraph if desired.
The %prep section is where we prepare our build environment or workspace
for building. Most often what happens here is the expansion of compressed
archives of the source code, application of patches, and potentially parsing of
information provided in the source code that is necessary in a later portion of
the 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 the
software we are packaging. Since wrote a simple Makefile for our
C
implementation, we can simply use the GNU make command provided by
rpmdev-newspec. However, we need to remove the call to %configure
because we did not provide a
configure script
. The %build section of our SPEC file should look as follows.
%build
make %{?_smp_mflags}
The %install section is where we instruct rpmbuild how to install our
previously built software into the BUILDROOT which is effectively a
chroot
base directory with nothing in it and we will have to construct any
paths or directory hierarchies that we will need in order to install our
software here in their specific locations. However, our RPM Macros help us
accomplish this task without having to hardcode paths.
Once again, since we have a simple Makefile the installation step can be
accomplished easily by leaving in place the %make_install macro that was
again provided for us by the rpmdev-newspec command.
The %install section should look like the following after your edits:
The %files section is where we provide the list of files that this RPM
provides and where it’s intended for them to live on the system that the RPM is
installed upon. Note here that this isn’t relative to the %{buildroot} but
the full path for the files as they are expected to exist on the end system
after installation. Therefore, the listing for the cello file we are
installing will be %{_bindir}/cello.
Also, within this section you will sometimes need a built-in macro to provide
context on a file. This can be useful for Systems Administrators and end users
who might want to query the system with rpm about the resulting package.
The built-in macro we will use here is %license which will tell rpmbuild
that 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 that
correlate to a specific Version-Release of the package. This is not meant to be
a log of what changed in the software from release to release, but specifically
to packaging changes. For example, if software in a package needed patching or
there was a change needed in the build procedure listed in the %build
section that information would go here. Each change entry can contain multiple
items 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, the
year, then the contact information for the RPM Packager. From there we have
a - character before the Version-Release, which is an often used convention
but not a requirement. Then finally the Version-Release.
That’s it! We’ve written an entire SPEC file for cello! In the next section we will cover how to build the RPM!
The full SPEC file should now look like the following:
Name: cello
Version: 1.0
Release: 1%{?dist}
Summary: Hello World example implemented in C
License: GPLv3+
URL: https://www.example.com/%{name}
Source0: https://www.example.com/%{name}/releases/%{name}-%{version}.tar.gz
Patch0: cello-output-first-patch.patch
BuildRequires: gcc
BuildRequires: make
%description
The long-tail description for our Hello World Example implemented in
C.
%prep
%setup -q
%patch0
%build
make %{?_smp_mflags}
%install
%make_install
%files
%license LICENSE
%{_bindir}/%{name}
%changelog
* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org> - 1.0-1
- First cello package
The rpmdevtools package provides a set of SPEC file templates for several
popular languages in the /etc/rpmdevtools/ directory.
Building RPMS
RPMs are built with the rpmbuild command. Different scenarios and desired
outcomes require different combinations of arguments to rpmbuild. This
section describes the two prime scenarios:
-
building a source RPM
-
building a binary RPM
The rpmbuild command expects a certain directory and file structure. This is
the same structure as set up by the rpmdev-setuptree utility. The previous
instructions also confirmed to the required structure.
Source RPMs
Why build a Source RPM (SRPM)?
-
To preserve the exact source of a certain Name-Version-Release of the RPM that was deployed to an environment. This includes the exact SPEC file, the source code, and all relevant patches. This is useful for looking back in history and for debugging.
-
To be able to build a binary RPM on a different hardware platform or architecture.
To create a SRPM:
$ rpmbuild -bs _SPECFILE_
Substitute SPECFILE with the SPEC file. The -bs option stands for "build
source".
Here we build SRPMs for bello, pello, and cello:
$ cd ~/rpmbuild/SPECS/
$ rpmbuild -bs bello.spec
Wrote: /home/admiller/rpmbuild/SRPMS/bello-0.1-1.el7.src.rpm
$ rpmbuild -bs pello.spec
Wrote: /home/admiller/rpmbuild/SRPMS/pello-0.1.1-1.el7.src.rpm
$ rpmbuild -bs cello.spec
Wrote: /home/admiller/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm
Note that SRPMs were placed into the rpmbuild/SRPMS directory, which is part
of the structure expected by rpmbuild.
This is all there is to building a SRPM.
Binary RPMS
There are two methods for building Binary RPMs:
-
Rebuilding it from a SRPM using the
rpmbuild --rebuildcommand. -
Building it from a SPEC file using the
rpmbuild -bbcommand. The-bboption stands for "build binary".
Rebuilding from a Source RPM
To rebuild bello, pello, and cello 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 helpful for debugging. The output varies for different examples and corresponds to their SPEC files.
-
The resulting binary RPMs are in
~/rpmbuild/RPMS/YOURARCHwhereYOURARCHis your architecture or in~/rpmbuild/RPMS/noarch/, if the package is not architecture-specific. -
Invoking
rpmbuild --rebuildinvolves:-
Installing the contents of the SRPM - the SPEC file and the source code - into the
~/rpmbuild/directory. -
Building using the installed contents.
-
Removing the SPEC file and the source code.
You can retain the SPEC file and the source code after building. For this, you have two options:
-
When building, use the
--recompileoption instead of--rebuild. -
Install the SRPMs using these commands:
$ rpm -Uvh ~/rpmbuild/SRPMS/bello-0.1-1.el7.src.rpm Updating / installing... 1:bello-0.1-1.el7 ################################# [100%] $ rpm -Uvh ~/rpmbuild/SRPMS/pello-0.1.1-1.el7.src.rpm Updating / installing... 1:pello-0.1.1-1.el7 ################################# [100%] $ rpm -Uvh ~/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm Updating / installing... 1:cello-1.0-1.el7 ################################# [100%]For this tutorial, execute the
rpm -Uvhcommands above to continue interacting with the SPEC files and sources. -
Building Binary from the SPEC file
To build bello, pello, and cello 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 in Rebuilding from a Source RPM apply here.
Checking RPMs For Sanity
After creating a package, it is good to check its quality. Quality of the package, not of the software delivered within it. The main tool for this is rpmlint. It improves RPM maintainability and enables sanity and error checking by performing static analysis 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 the following examples.
Note that rpmlint has very strict guidelines, and sometimes it is acceptable
and necessary to skip some of its Errors and Warnings, as shown in the following
examples.
|
Note |
In the examples, we run rpmlint without any options, which produces
non-verbose output. For detailed explanations of each Error or Warning, run
rpmlint -i instead.
|
Checking the bello SPEC File
This is the output of running rpmlint on the SPEC file for bello:
$ rpmlint bello.spec
bello.spec: W: invalid-url Source0: https://www.example.com/bello/releases/bello-0.1.tar.gz HTTP Error 404: Not Found
0 packages and 1 specfiles checked; 0 errors, 1 warnings.
Observations:
-
For
bello.specthere is only one warning. It says that the URL listed in theSource0directive is unreachable. This is expected, because the specifiedexample.comURL does not exist. Presuming that we expect this URL to work in the future, we can ignore this warning.
This is the output of running rpmlint on the SRPM for bello:
$ rpmlint ~/rpmbuild/SRPMS/bello-0.1-1.el7.src.rpm
bello.src: W: invalid-url URL: https://www.example.com/bello HTTP Error 404: Not Found
bello.src: W: invalid-url Source0: https://www.example.com/bello/releases/bello-0.1.tar.gz HTTP Error 404: Not Found
1 packages and 0 specfiles checked; 0 errors, 2 warnings.
Observations:
-
For the
belloSRPM there is a new warning, which says that the URL specified in theURLdirective is unreachable. Assuming the link will be working in the future, we can ignore this warning.
Checking the bello Binary RPM
When checking Binary RPMs, rpmlint checks for more things, including:
-
documentation
-
consistent use of the Filesystem Hierarchy Standard
This is the output of running rpmlint on the Binary RPM for bello:
$ rpmlint ~/rpmbuild/RPMS/noarch/bello-0.1-1.el7.noarch.rpm
bello.noarch: W: invalid-url URL: https://www.example.com/bello HTTP Error 404: Not Found
bello.noarch: W: no-documentation
bello.noarch: W: no-manual-page-for-binary bello
1 packages and 0 specfiles checked; 0 errors, 3 warnings.
Observations:
-
The
no-documentationandno-manual-page-for-binarywarnings say that the RPM has no documentation or manual pages, because we did not provide any.
Apart from the above warnings, our RPM is passing rpmlint checks.
Checking the pello SPEC File
This is the output of running rpmlint on the SPEC file for pello:
$ rpmlint pello.spec
pello.spec:30: E: hardcoded-library-path in %{buildroot}/usr/lib/%{name}
pello.spec:34: E: hardcoded-library-path in /usr/lib/%{name}/%{name}.pyc
pello.spec:39: E: hardcoded-library-path in %{buildroot}/usr/lib/%{name}/
pello.spec:43: E: hardcoded-library-path in /usr/lib/%{name}/
pello.spec:45: E: hardcoded-library-path in /usr/lib/%{name}/%{name}.py*
pello.spec: W: invalid-url Source0: https://www.example.com/pello/releases/pello-0.1.1.tar.gz HTTP Error 404: Not Found
0 packages and 1 specfiles checked; 5 errors, 1 warnings.
Observations:
-
The
invalid-url Source0warning says that the URL listed in the Source0 directive is unreachable. This is expected, because the specifiedexample.comURL does not exist. Presuming that we expect this URL to work in the future, we can ignore this warning. -
There are many errors, because we intentionally wrote this SPEC file to be uncomplicated and to show what errors
rpmlintcan report. -
The
hardcoded-library-patherrors suggest to use the%{_libdir}macro instead of hard-coding the library path. For the sake of this example, we ignore these errors, but for packages going in production you need a good reason for ignoring this error.
This is the output of running rpmlint on the SRPM for pello:
$ rpmlint ~/rpmbuild/SRPMS/pello-0.1.1-1.el7.src.rpm
pello.src: W: invalid-url URL: https://www.example.com/pello HTTP Error 404: Not Found
pello.src:30: E: hardcoded-library-path in %{buildroot}/usr/lib/%{name}
pello.src:34: E: hardcoded-library-path in /usr/lib/%{name}/%{name}.pyc
pello.src:39: E: hardcoded-library-path in %{buildroot}/usr/lib/%{name}/
pello.src:43: E: hardcoded-library-path in /usr/lib/%{name}/
pello.src:45: E: hardcoded-library-path in /usr/lib/%{name}/%{name}.py*
pello.src: W: invalid-url Source0: https://www.example.com/pello/releases/pello-0.1.1.tar.gz HTTP Error 404: Not Found
1 packages and 0 specfiles checked; 5 errors, 2 warnings.
Observations:
-
The new
invalid-url URLerror here is about theURLdirective, which is 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:
-
documentation
-
consistent use of the
This is the output of running rpmlint on the Binary RPM for pello:
$ rpmlint ~/rpmbuild/RPMS/noarch/pello-0.1.1-1.el7.noarch.rpm
pello.noarch: W: invalid-url URL: https://www.example.com/pello HTTP Error 404: Not Found
pello.noarch: W: only-non-binary-in-usr-lib
pello.noarch: W: no-documentation
pello.noarch: E: non-executable-script /usr/lib/pello/pello.py 0644L /usr/bin/env
pello.noarch: W: no-manual-page-for-binary pello
1 packages and 0 specfiles checked; 1 errors, 4 warnings.
Observations:
-
The
no-documentationandno-manual-page-for-binarywarnings say that the RPM has no documentation or manual pages, because we did not provide any. -
The
only-non-binary-in-usr-libwarning says that you provided only non-binary artifacts in/usr/lib/. This directory is normally reserved for shared object files, which are binary files. Therefore,rpmlintexpects at least one or more files in/usr/lib/to be binary.Normally, use RPM macros to ensure the correct placement of files. For the sake of this example, we can ignore this warning.
-
The
non-executable-scripterror warns that the/usr/lib/pello/pello.pyfile has no execute permissions. Since this file contains the shebang ,rpmlintexpects the file to be executable. For the purpose of the example, leave this file without execute permissions and ignore this error.
Apart from the above warnings and errors, our RPM is passing rpmlint checks.
Checking the cello SPEC File
This is the output of running rpmlint on the SPEC file for cello:
$ rpmlint ~/rpmbuild/SPECS/cello.spec
/home/admiller/rpmbuild/SPECS/cello.spec: W: invalid-url Source0: https://www.example.com/cello/releases/cello-1.0.tar.gz HTTP Error 404: Not Found
0 packages and 1 specfiles checked; 0 errors, 1 warnings.
Observations:
-
The only warning for
cello.specsays that the URL listed in theSource0directive is unreachable. This is expected, because the specifiedexample.comURL does not exist. Presuming that we expect this URL to work in the future, we can ignore this warning.
This is the output of running rpmlint on the SRPM file for cello:
$ rpmlint ~/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm
cello.src: W: invalid-url URL: https://www.example.com/cello HTTP Error 404: Not Found
cello.src: W: invalid-url Source0: https://www.example.com/cello/releases/cello-1.0.tar.gz HTTP Error 404: Not Found
1 packages and 0 specfiles checked; 0 errors, 2 warnings.
Observations:
-
For the
celloSRPM there is a new warning, which says that the URL specified in theURLdirective is unreachable. Assuming the link will be working in the future, we can ignore this warning.
Checking the cello Binary RPM
When checking Binary RPMs, rpmlint checks for more things, including:
-
documentation
-
consistent use of the Filesystem Hierarchy Standard .
This is the output of running rpmlint on the Binary RPM for cello:
$ rpmlint ~/rpmbuild/RPMS/x86_64/cello-1.0-1.el7.x86_64.rpm
cello.x86_64: W: invalid-url URL: https://www.example.com/cello HTTP Error 404: Not Found
cello.x86_64: W: no-documentation
cello.x86_64: W: no-manual-page-for-binary cello
1 packages and 0 specfiles checked; 0 errors, 3 warnings.
Observations:
-
The
no-documentationandno-manual-page-for-binarywarnings say that the RPM has no documentation or manual pages, because we did not provide any.
Apart from the above warnings and errors, our RPM is passing rpmlint checks.
Our RPMs are now ready and checked with rpmlint. This concludes the
tutorial. For more information on packaging RPMs, proceed to
Advanced Topics.