RPM packager's cheat sheet
Published on ; updated on
These are some random notes on creating RPM packages that I wrote some time ago for my own use.
The RPM build tree
On Fedora, the default rpm build tree is ~/rpmbuild.
ls ~/rpmbuild
BUILD/
BUILDROOT/
RPMS/
SOURCES/
SPECS/
SRPMS/
You can create this directory structure with
rpmdev-setuptree from the rpmdevtools
package.
The purpose of each directory is:
SPECS: spec files live here. They contain package metainformation and build instructions. This is what you’ll be writing when creating packages.SOURCES: download the source tarballs into this directory.SRPMS:.src.rpmbundles together a spec file and the sources for that spec. You can create one withrpmbuild -bs, or download withdnf download --source, and then build a binary package from it withrpmbuild -rb.BUILD: sources are unpacked into this directory and get compiled here.BUILDROOT: during the install phase, files are copied here.RPMS: the final rpms are created here.
To change the location of the build tree, put this into
~/.rpmmacros:
%_topdir %{getenv:HOME}/myrpmbuild
Build stages
The -b option selects the stage at which to stop. Except
for -bs, each option runs its stage and all preceding
stages.
-bp: %prep
Unpack the source tarballs and apply patches. If you have any sources
to unpack, try to find a %setup invocation that does the
trick. The flags to %setup are described elsewhere.
-bc: %build
Run %prep and then build the software, usually with
make or another build tool.
-bi: %install
and %check
Run %prep and %build, install the built
files into %{buildroot}, and run the test suite, if there
is one. Do not install files directly into the system.
-bl: %files
Run all preceding stages, expand the %files section, and
verify that every listed file exists.
-bb: binary package
assembly
Run all preceding stages and create the binary RPMs.
-bs: source package
assembly
Create the source RPM without running any of the build stages.
-ba: source
and binary package assembly
Run all stages and create both the source and binary RPMs.
SRPMs
To customize a package that already exists in Fedora (for instance, to update to a newer upstream version), it is convenient to download and install an srpm (also known as src.rpm).
First, download an srpm from dl.fedoraproject.org
(substitute your release number) to ~/rpmbuild/SRPMS.
Alternatively, use the program yumdownloader from the
dnf-utils package:
yumdownloader --destdir ~/rpmbuild/SRPMS --source packagename
Next, install it into your build tree:
rpm -i ~/rpmbuild/SRPMS/packagename.src.rpm
Now the spec file should appear under ~/rpmbuild/SPECS
and sources under ~/rpmbuild/SOURCES.
Extract dependencies from a spec file and install them
dnf builddep package.spec
If you’re getting an error
Unknown argument "builddep" for command "dnf5", install the
dnf5-plugins package.
RPM macros
Evaluate an RPM macro expression:
rpm -E '%_topdir'
Show all macro definitions:
rpm --showrc
It will also show where the definitions are located: look for
something like
Macro path: /usr/lib/rpm/macros:/usr/lib/rpm/macros.d/macros.*:/usr/lib/rpm/platform/%{_target}/macros:/usr/lib/rpm/fileattrs/*.attr:/usr/lib/rpm/redhat/macros:/etc/rpm/macros.*:/etc/rpm/macros:/etc/rpm/%{_target}/macros:~/.rpmmacros.
Unfortunately, it doesn’t seem to show where each particular macro comes from.
Defining your own
It’s often useful to define variables in the SPEC file.
This can be done with either %define or
%global, and %global
is preferred:
%define commit a0a746442daf8dfca01859f1efcc8663b47b3705
%global commit a0a746442daf8dfca01859f1efcc8663b47b3705
Minimal RPM spec file
I usually start with the minimal spec file:
Name: minimal
Version: 1
Release: 1%{?dist}
Summary: Minimal package
License: MIT
%description
...
%prep
%build
%install
%files
/*
RPM has many defaults and convenient macros suited for traditional
./configure && make && make install
tarballs, but nowadays those are actually rare.
Sources
If you have any source files, they should be declared (usually above
%description) like this:
URL: ftp://ftp.ncbi.nlm.nih.gov/entrez/entrezdirect/edirect.zip
Source0: edirect.zip
You can then download the sources automatically using
spectool:
spectool -C ~/rpmbuild/SOURCES -g package.spec
Sometimes I don’t declare any sources and instead use a tool (such as
pypi or gem) to download and install the
software. This is not reproducible, and therefore unacceptable when
creating public packages, but is usually fine for personal use.
GitHub sources
Here’s an example of using a specific commit from a github repo.
See also the pages on SourceURL for git and versioning snapshots.
%global commit a0a746442daf8dfca01859f1efcc8663b47b3705
%global shortcommit %(c=%{commit}; echo ${c:0:7})
Release: 1.%{shortcommit}
Source0: https://github.com/lwindolf/%{name}/archive/%{commit}/%{name}-%{commit}.tar.gz
%prep
%setup -n %{name}-%{commit}
Auto-provides
A likely reason why your shared library is not detected by the “automatic provides” mechanism is that it is not executable.
Add something like this to your %install section:
find %buildroot -type f \( -name '*.so' -o -name '*.so.*' \) -exec chmod 755 {} +
Disable debuginfo
Put this in the spec file:
%global debug_package %{nil}
rpmbuild invocation
tips
Incremental builds
--build-in-place skips %prep and builds the
sources in the current working tree. --noclean retains
build artifacts after the build. Together, they make iterative local
builds faster:
rpmbuild -bb --build-in-place --noclean package.spec
Use a clean build without these options before publishing a package.
Limiting parallelism
%make_build uses %_smp_build_ncpus to
decide how many jobs to run. To cap parallelism for one build, override
it on the command line:
rpmbuild -bb --define '_smp_build_ncpus 2' package.spec
To apply the cap to every build of a package, add this to its spec file:
%global _smp_build_ncpus 2