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:
also has a source package found nearby:
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.
- 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
- 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.