I have already written article to build a signed rpm from scratch by building a source archive using Red Hat Linux, now in this article I will share the steps to rebuild rpm using source rpm. The source rpm can be collected from official page of respective distribution. In this article I will use httpd source rpm to rebuild the httpd rpm
Install rpmdevtools
We need rpmdev-setuptree to setup rpmbuild directory structure to create rpm package. The tool is provided by rpmdevtools in RHEL/CentOS 7 Linux
[root@centos-7 ~]# yum install rpmdevtools
There are some other pre-requisite rpms which are required to create rpm package. Some of them are:
[root@centos-7 ~]# yum install gcc gcc-c++ make automake autoconf rpm-build
Now later when we build rpm from source code, it is possible we may get any more dependency error, so we will install any additional rpms based on the requirement.
Create rpmbuild structure
To create rpm package using rpmbuild, we need a proper directory structure. rpmbuild will look for files from respective directories to build rpm from source code
Run the rpmdev-setuptree utility from the location where you wish to setup the rpmbuild structure to create rpm package. I will execute under root's home folder
[root@centos-7 ~]# rpmdev-setuptree
As you can see a directory structure is created by rpmdev-setuptree under /root/rpmbuild
[root@centos-7 ~]# tree ~/rpmbuild/
/root/rpmbuild/
├── BUILD
├── RPMS
├── SOURCES
├── SPECS
└── SRPMS
5 directories, 0 files
We will cover two scenarios to create rpm package
- Build rpm from source code
- Create rpm package from scratch
Build RPM from source code
In this example, we already have an rpm, but we would like to modify the rpm content and build rpm from the source code with our modifications.
Step 1: Download source rpm
First we must access the source rpm which we have to modify to create rpm package. You can get the source rpm depending upon your environment and vendor.
I will take a basic example of httpd rpm, and the src rpm was available at CentOS project repository
Step 2: Extract source rpm content
Next we will extract the source code from the rpm. I have created a temporary directory /tmp/customrpm to extract the content of httpd rpm.
[root@centos-7 ~]# mkdir /tmp/customrpm
Copy the httpd source rpm to this temporary directory
[root@centos-7 ~]# cp httpd-2.4.6-89.el7_6.src.rpm /tmp/customrpm/
Extract the source code using rpm2cpio
[root@centos-7 ~]# cd /tmp/customrpm [root@centos-7 customrpm]# rpm2cpio httpd-2.4.6-89.el7_6.src.rpm | cpio -idm 11022 blocks
Next extract the httpd-2.4.6.tar.bz2 archive so that we can modify the content of httpd. This contains all the source code for httpd package.
[root@centos-7 customrpm]# tar -xjvf httpd-2.4.6.tar.bz2
Step 3: Modify the Source Code
If you observe we have multiple patch and other configuration files available inside our temporary directory.
[root@centos-7 customrpm]# ls
00-base.conf httpd-2.4.6-CVE-2017-3169.patch httpd-2.4.6-r1651658.patch
00-dav.conf httpd-2.4.6-CVE-2017-7668.patch httpd-2.4.6-r1662640.patch
00-lua.conf httpd-2.4.6-CVE-2017-7679.patch httpd-2.4.6-r1663647.patch
00-mpm.conf httpd-2.4.6-CVE-2017-9788.patch httpd-2.4.6-r1664565.patch
00-proxy.conf httpd-2.4.6-CVE-2017-9798.patch httpd-2.4.6-r1668532.patch
00-proxyhtml.conf httpd-2.4.6-default-port-worker.patch httpd-2.4.6-r1674222.patch
00-ssl.conf httpd-2.4.6-dhparams-free.patch httpd-2.4.6-r1681107.patch
00-systemd.conf httpd-2.4.6-full-release.patch httpd-2.4.6-r1681114.patch
01-cgi.conf httpd-2.4.6-http-protocol-options-define.patch httpd-2.4.6-r1681289.patch
01-ldap.conf httpd-2.4.6-ldaprefer.patch httpd-2.4.6-r1683112.patch
01-session.conf httpd-2.4.6-mod_authz_dbd-missing-query.patch httpd-2.4.6-r1684462.patch
action-configtest.sh httpd-2.4.6-mpm-segfault.patch httpd-2.4.6-r1688399.patch
{Output trimmed}
Now based on your source rpm, the content of the rpm may vary. I will go ahead and put some dummy data in one of the files under httpd and later we will use the new changes to build rpm from source code.
[root@centos-7 customrpm]# cd httpd-2.4.6
[root@centos-7 httpd-2.4.6]# echo "TEST Deepak" >> README
Step 4: Create RPM Content Archive
Next I will create a new archive under /tmp with the updated source code.
[root@centos-7 customrpm]# tar -cjvf ../httpd-2.4.6.tar.bz2 httpd-2.4.6/*
In the later steps we will transfer this archive to SOURCE directory of the rpmbuild structure to build rpm from source code
So now since I have created a new archive, I will remove the extra content from my temporary directory.
[root@centos-7 customrpm]# rm -rf httpd-2.4.6 httpd-2.4.6.tar.bz2 httpd-2.4.6-89.el7_6.src.rpm
It will help me arrange the files required to create rpm package later.
Step 5: Update SOURCES
Next we will copy the new archive to SOURCES directory
[root@centos-7 customrpm]# cp /tmp/httpd-2.4.6.tar.bz2 /root/rpmbuild/SOURCES/
Step 6: Update rpm spec File
A spec file is always part of any rpm. So when we extracted our src rpm, we also got httpd.spec file, we will copy the httpd.spec file to SPEC directory. We will need this to create rpm package with a new version.
[root@centos-7 customrpm]# cp httpd.spec /root/rpmbuild/SPECS/
It is always a good idea to modify the version of rpm and provide the list of changes performed in the source code to track the changes
[root@centos-7 customrpm]# cd /root/rpmbuild/SPECS/ [root@centos-7 SPECS]# vim httpd.spec <Updated the Release Number> Summary: Apache HTTP Server Name: httpd Version: 2.4.6 Release: 90%{?dist} <Added change log> %changelog * Fri Jun 21 2019 Deepak Prasad <admin@golinuxcloud.com> - 2.4.6-90 - Test Changes
Step 7: Build rpm using source code
If you check the spec file, you will find BuildRequires section. This contains pre-requisite which are required by httpd rpm for installation
BuildRequires: autoconf, perl, pkgconfig, findutils, xmlto BuildRequires: zlib-devel, libselinux-devel, lua-devel BuildRequires: apr-devel >= 1.4.0, apr-util-devel >= 1.2.0, pcre-devel >= 5.0 BuildRequires: systemd-devel Requires: /etc/mime.types, system-logos >= 7.92.1-1 Requires(pre): /usr/sbin/useradd Requires(pre): /usr/sbin/groupadd Requires(preun): systemd-units Requires(postun): systemd-units Requires(post): systemd-units
So before we start to build rpm from source code, we have to make sure the build machine has these rpms installed and those files available on your host.
To get the complete list of dependencies you can initiate rpmbuild and it will throw you the dependencies list. Here -ba means Build binary and source packages (after doing the %prep, %build, and %install stages).
[root@centos-7 SPECS]# rpmbuild -ba httpd.spec
error: Failed build dependencies:
autoconf is needed by httpd-2.4.6-90.el7.x86_64
xmlto is needed by httpd-2.4.6-90.el7.x86_64
zlib-devel is needed by httpd-2.4.6-90.el7.x86_64
libselinux-devel is needed by httpd-2.4.6-90.el7.x86_64
lua-devel is needed by httpd-2.4.6-90.el7.x86_64
apr-devel >= 1.4.0 is needed by httpd-2.4.6-90.el7.x86_64
apr-util-devel >= 1.2.0 is needed by httpd-2.4.6-90.el7.x86_64
pcre-devel >= 5.0 is needed by httpd-2.4.6-90.el7.x86_64
systemd-devel is needed by httpd-2.4.6-90.el7.x86_64
openssl-devel >= 1:1.0.1e-37 is needed by httpd-2.4.6-90.el7.x86_64
libxml2-devel is needed by httpd-2.4.6-90.el7.x86_64
The rpmbuild will throw an error will the dependency error. So we must manually install these dependency rpms before we can create rpm package:
[root@centos-7 SPECS]# yum -y install autoconf xmlto zlib-devel libselinux-devel lua-devel apr-devel apr-util-devel pcre-devel systemd-devel openssl-devel libxml2-devel
Once all the dependency rpms are installed, you can again trigger rpmbuild to build rpm from source code.
[root@centos-7 SPECS]# rpmbuild -ba httpd.spec Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.u33j1b + umask 022 + cd /root/rpmbuild/BUILD + cd /root/rpmbuild/BUILD + rm -rf httpd-2.4.6 + /usr/bin/bzip2 -dc /root/rpmbuild/SOURCES/httpd-2.4.6.tar.bz2 + /usr/bin/tar -xf - + STATUS=0
So our create rpm package was successful. At the end of execution you can see the path of the new rpm files under RPMS directory of the rpmbuild structure.
Step 8: Install and Validate RPM
I will install the new rpm to verify the changes which I had done to the README file of our httpd rpm.
[root@centos-7 SPECS]# rpm -Uvh /root/rpmbuild/RPMS/x86_64/httpd-2.4.6-90.el7.x86_64.rpm /root/rpmbuild/RPMS/x86_64/httpd-tools-2.4.6-90.el7.x86_64.rpm Preparing... ################################# [100%] Updating / installing... 1:httpd-tools-2.4.6-90.el7 ################################# [ 50%] 2:httpd-2.4.6-90.el7 ################################# [100%]
Verify if the rpm is successfully installed
[root@centos-7 SPECS]# rpm -qa | grep httpd
httpd-tools-2.4.6-90.el7.x86_64
httpd-2.4.6-90.el7.x86_64
Get the path of README file where we did the changes of our source rpm:
[root@centos-7 SPECS]# rpm -ql httpd-2.4.6-90.el7.x86_64 | grep README /etc/httpd/conf.d/README /usr/share/doc/httpd-2.4.6/README /usr/share/httpd/error/README /usr/share/httpd/icons/README /usr/share/httpd/icons/README.html
So looks like our changes are present as expected:
[root@centos-7 SPECS]# grep TEST /usr/share/doc/httpd-2.4.6/README
TEST Deepak
Create rpm package from scratch
- It is possible you want to create a completely new rpm package for your development and testing tasks.
- We will need similar
rpmbuilddirectory structure - It is important that you create separate rpmbuild structure for individual rpm
- I will create a new rpm by my name "
deepak" - So I will create a new folder to create
rpmbuildstructure
[root@centos-7 ~]# mkdir /root/deepak [root@centos-7 ~]# cd /root/deepak
Execute rpmdev-setuptree to create rpmbuild structure to create rpm package
[root@centos-7 ~]# rpmdev-setuptree
This will create similar directory structure as we created earlier to build rpm from source code
[root@centos-7 ~]# tree /root/deepak/rpmbuild/
/root/deepak/rpmbuild/
├── BUILD
├── RPMS
├── SOURCES
├── SPECS
└── SRPMS
5 directories, 0 files
Step 1: Create rpm spec file
Since we plan to create rpm package from scratch, we would need a spec file. You can refer fedora: create rpm packages to get a sample spec file
Or you can use below spec file template for your rpm
[root@centos-7 ~]# vim /tmp/deepak/rpmbuild/SPECS/deepak.spec
Name: deepak
Summary: Test Rpm
Version: 1.0.0
Release: 1
Group: GoLinuxCloud
License: Not Applicable
URL: https://www.golinuxcloud.com
SOURCE0: %{name}-%{version}-%{release}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
%description
we are learning how to create rpm package in Linux
%prep
%setup -q
%build
#Empty
%install
rm -rf %{buildroot}
mkdir -p %{buildroot}
cp -a * %{buildroot}
%clean
rm -rf %{buildroot}
%files
%defattr(-,root,root,-)
/test1/file1
/test2/file2
%changelog
* Wed May 06 2020 Deepak Prasad - 1.0.0-1
- Created first draft
- One of the most important part of spec file are
scriptlets - Using
scriptletswe can define%pre,%postand many other sections of rpmspecfile - So you can use respective section to call a custom script or function
- For example, you have a requirement to setup an environment before installing the rpm, so you can choose respective
scriptletsto add your changes. - I have written separate article to cover all the scriptlets with detailed examples
Step 2: Create RPM content
- All the content under
SOURCESwill be considered as rpm content - These will be placed on the server after rpm is installed
- The location of these content will be defined using rpm
specfile - We do not keep individual files in
SOURCES, instead we must bundle the content and then place the bundle inSOURCESdirectory
So I will create a temporary directory under /tmp .Below will be our source directory with base release number of 1.0.0
[root@centos-7 ~]# cd /tmp [root@centos-7 ~]# mkdir deepak-1.0.0
Next lets create some dummy files and directories which we have mentioned in the spec file under %files
[root@centos-7 ~]# mkdir -p deepak-1.0.0/test1/file1 [root@centos-7 ~]# mkdir -p deepak-1.0.0/test2/file2
Step 3: Update SOURCES
Next we will create an archive using the temporary file and directory we created above. The archive must use proper release and version number
For example my spec file contains below
Version: 1.0.0 Release: 1
So my archive name would be deepak-1.0.0-1.tar.gz
[root@centos-7 ~]# cd /tmp
[root@centos-7 ~]# tar -czvf /root/deepak/rpmbuild/SOURCES/deepak-1.0.0-1.tar.gz deepak-1.0.0/*
Step 4: Create RPM package
So our rpmbuild environment setup is complete. Next we will create rpm package using rpmbuild with the spec file
[root@centos-7 ~]# rpmbuild -ba /root/deepak/rpmbuild/SPECS/deepak.spec Executing(%prep): /bin/sh -e /root/deepak/rpmbuild/tmp/rpm-tmp.p63FqV + umask 022 + cd /root/deepak/rpmbuild/BUILD + cd /root/deepak/rpmbuild/BUILD + rm -rf deepak-1.0.0 + /usr/bin/gzip -dc /root/deepak/rpmbuild/SOURCES/deepak-1.0.0-1.tar.gz + /usr/bin/tar -xf - + STATUS=0 + '[' 0 -ne 0 ']' + cd deepak-1.0.0 + /usr/bin/chmod -Rf a+rX,u+w,g-w,o-w . + exit 0 Executing(%build): /bin/sh -e /root/deepak/rpmbuild/tmp/rpm-tmp.Oekwnu + umask 022 + cd /root/deepak/rpmbuild/BUILD + cd deepak-1.0.0 + exit 0 Executing(%install): /bin/sh -e /root/deepak/rpmbuild/tmp/rpm-tmp.My9Zk3 + umask 022 + cd /root/deepak/rpmbuild/BUILD + '[' /root/deepak/rpmbuild/BUILDROOT/deepak-1.0.0-1.x86_64 '!=' / ']' + rm -rf /root/deepak/rpmbuild/BUILDROOT/deepak-1.0.0-1.x86_64 ++ dirname /root/deepak/rpmbuild/BUILDROOT/deepak-1.0.0-1.x86_64 + mkdir -p /root/deepak/rpmbuild/BUILDROOT + mkdir /root/deepak/rpmbuild/BUILDROOT/deepak-1.0.0-1.x86_64 + cd deepak-1.0.0 + rm -rf /root/deepak/rpmbuild/BUILDROOT/deepak-1.0.0-1.x86_64 + mkdir -p /root/deepak/rpmbuild/BUILDROOT/deepak-1.0.0-1.x86_64 + cp -a test1 test2 /root/deepak/rpmbuild/BUILDROOT/deepak-1.0.0-1.x86_64 + /usr/lib/rpm/find-debuginfo.sh --strict-build-id -m --run-dwz --dwz-low-mem-die-limit 10000000 --dwz-max-die-limit 110000000 /root/deepak/rpmbuild/BUILD/deepak-1.0.0 /usr/lib/rpm/sepdebugcrcfix: Updated 0 CRC32s, 0 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-jars Processing files: deepak-1.0.0-1.x86_64 Provides: deepak = 1.0.0-1 deepak(x86-64) = 1.0.0-1 Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1 Processing files: deepak-debuginfo-1.0.0-1.x86_64 Provides: deepak-debuginfo = 1.0.0-1 deepak-debuginfo(x86-64) = 1.0.0-1 Requires(rpmlib): rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1 rpmlib(CompressedFileNames) <= 3.0.4-1 Checking for unpackaged file(s): /usr/lib/rpm/check-files /root/deepak/rpmbuild/BUILDROOT/deepak-1.0.0-1.x86_64 Wrote: /root/deepak/rpmbuild/SRPMS/deepak-1.0.0-1.src.rpm Wrote: /root/deepak/rpmbuild/RPMS/x86_64/deepak-1.0.0-1.x86_64.rpm Wrote: /root/deepak/rpmbuild/RPMS/x86_64/deepak-debuginfo-1.0.0-1.x86_64.rpm Executing(%clean): /bin/sh -e /root/deepak/rpmbuild/tmp/rpm-tmp.OipO2Y + umask 022 + cd /root/deepak/rpmbuild/BUILD + cd deepak-1.0.0 + rm -rf /root/deepak/rpmbuild/BUILDROOT/deepak-1.0.0-1.x86_64 + exit 0
Our rpmbuild execution is successful.
The new rpm is placed under /root/deepak/rpmbuild/RPMS/x86_64/deepak-1.0.0-1.x86_64.rpm
Step 5: Install and Verify rpm content
Let us check the content of our rpm
[root@centos-7 ~]# rpm -qlp /tmp/rpmbuild/RPMS/x86_64/deepak-1.0.0-1.x86_64.rpm /test1/file1 /test2/file2
Install and validate the rpm
[root@centos-7 ~]# rpm -ivh /tmp/rpmbuild/RPMS/x86_64/deepak-1.0.0-1.x86_64.rpm Preparing... ################################# [100%] Updating / installing... 1:deepak-1.0.0-1 ################################# [100%]
The rpm installation was successful and the content of the rpm remains the same
[root@centos-7 ~]# rpm -ql deepak /test1/file1 /test2/file2
Lastly I hope the steps from the article to create rpm package and build rpm from source code using rpmbuild on Red Hat Linux was helpful. So, let me know your suggestions and feedback using the comment section.

