Skip to main content

Rebuild a RPM Package

·715 words·4 mins
System-Administration Linux Packaging Rpm Fedora Redhat Suse
Table of Contents

Preparation
#

Acquire tools
#

Install the required tools by acquiring the rpm-build package.

RHEL / Fedora
#

$ dnf install rpm-build

SUSE
#

$ zypper install rpm-build

Setup directory structure
#

Then create a directory to perform the work, and ensure it has several subdirectories, like so:

$ mkdir ~/rpmbuild/
$ cd ~/rpmbuild/
$ mkdir BUILD RPMS SOURCES SPECS SRPMS

with a resulting layout:

/home/<user>/rpmbuild/
├── BUILD
├── RPMS
├── SOURCES
├── SPECS
└── SRPMS

Configure RPM variables
#

Create a .rpmmacros file to tell the RPM build tools where to perform it’s work. As with most environment-setting files, place it in the user’s home directory at ~/.rpmmacros.

# Root working directory
%_topdir /home/<user>/rpmbuild

Get source package
#

RPM source packages have the .src.rpm file extension. Usually any package found in a distribution will also have the source package available.

From package manager
#

RHEL / Fedora
#

# Enable 'dnf download' command
$ dnf install 'dnf-command(download)'
# Get source package
$ dnf download --source package

SUSE
#

$ zypper si package

NOTE: If you can’t find the source package file, it may have downloaded to the default location of /usr/src/packages/.

From online
#

For example, on Fedora 36, the meson binary package found here:

https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/36/Everything/aarch64/os/Packages/m/meson-0.60.3-2.fc36.noarch.rpm

also has a source package found nearby:

https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/36/Everything/source/tree/Packages/m/meson-0.60.3-2.fc36.src.rpm

Rebuilding package as-is
#

If the only thing to do is to rebuild a package as it already is:

$ rpmbuild --rebuild package-0.0.0.src.rpm

And if successful, the resulting package RPM will be in %_topdir/RPMS/<arch>/.

Rebuilding a package with changes
#

Extracting sources from the source RPM
#

If changes are to be made, then what first needs to happen is to ‘install’ the source into the RPM working directories, and this is done via:

$ rpm -ivh package-0.0.0.src.rpm

and this will place the spec specification file into the SPECS directory, and the original source code tarball and any patches into the SOURCES directory, like so:

/home/<user>/rpmbuild
├── BUILD
├── RPMS
├── SOURCES
│   ├── 0.patch
│   ├── package-0.0.0.tar.gz
├── SPECS
│   └── package.spec
└── SRPMS

Patching source code
#

During builds, RPM will destroy any content in the BUILD directory and re-extract and re-patch the source freshly everytime. Thus, if changes to the source code need to be made, then a new patch will have to be created and added instead.

Thus, extract the package-0.0.0.tar.gz to a two temporary working directory, then apply any patches that will still be applied in the order specified in the .spec file. Then make a copy of this directory and it contents, so you have two identical directories like package-0.0.0/ and package-0.0.0-patched/.

Then, in the package-0.0.0-patched/ directory, make the desired changes and when done, generate a patch, diff’ing the two directories to determine the changes that were made:

$ diff -uNr package-0.0.0/ package-0.0.0-patched/ > /home/<user>/rpmbuild/SOURCES/my-patch.patch

Unfortunately, the diff will have the different root directories when marking files, like so:

--- package-0.0.0/file          2021-10-09 12:48:58 +0000
+++ package-0.0.0-patched/file  2021-10-09 13:49:13 +0000

However, when it comes time to patch it can be told to strip the first N items in a file’s path via the -pN argument, in this case -p1.

Finally is to tell the .spec file to actually apply the patch when building the package, and this is done in two places.

  1. In the top section where the name, source, etc are defined, add the patch file name, with a number that helps specify the order the patches should be applied in like Patch#: <file>:
Patch0: my-patch.patch
  1. Next, tell the spec file to actually apply the patch, typically in the prep or setup stages:
%prep
%setup ...
%patch0 -p1

If this change is in any way meant to be redistributed beyond personal use, it is always best practice to then update the changelog at the end of the .spec file, noting what changed and why.

Building the modified package
#

$ rpmbuild -ba SPECS/package.spec

This will build both the binary package in the RPMS/<arch> directory, and an updated source package with the new changes in the SOURCES/ directory:

/home/<user>/rpmbuild/
├── BUILD
├── BUILDROOT
├── RPMS
│   └── noarch
│       └── package-0.0.0-1.noarch.rpm
├── SOURCES
│   ├── my-patch.patch
│   └── package-0.0.0.tar.gz
├── SPECS
│   └── meson.spec
└── SRPMS
    └── package-0.0.0-1.src.rpm

To just build the binary, use the -bb argument, or for just a source package, -bs argument.