Rpmbuild | Create rpm package | Build rpm from source code


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


If you do not have an active repository then download rpms with all the list of dependencies using which then you can create an offline repository.
[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/
├── RPMS

5 directories, 0 files


We will cover two scenarios to create rpm package

  1. Build rpm from source code
  2. 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>
* Fri Jun 21 2019 Deepak Prasad <deepak.prasad@nokia.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 -

<Output Trimmed>

Checking for unpackaged file(s): /usr/lib/rpm/check-files /root/rpmbuild/BUILDROOT/httpd-2.4.6-90.el7.x86_64
Wrote: /root/rpmbuild/SRPMS/httpd-2.4.6-90.el7.src.rpm
Wrote: /root/rpmbuild/RPMS/x86_64/httpd-2.4.6-90.el7.x86_64.rpm
Wrote: /root/rpmbuild/RPMS/x86_64/httpd-devel-2.4.6-90.el7.x86_64.rpm
Wrote: /root/rpmbuild/RPMS/noarch/httpd-manual-2.4.6-90.el7.noarch.rpm
Wrote: /root/rpmbuild/RPMS/x86_64/httpd-tools-2.4.6-90.el7.x86_64.rpm
Wrote: /root/rpmbuild/RPMS/x86_64/mod_ssl-2.4.6-90.el7.x86_64.rpm
Wrote: /root/rpmbuild/RPMS/x86_64/mod_proxy_html-2.4.6-90.el7.x86_64.rpm
Wrote: /root/rpmbuild/RPMS/x86_64/mod_ldap-2.4.6-90.el7.x86_64.rpm
Wrote: /root/rpmbuild/RPMS/x86_64/mod_session-2.4.6-90.el7.x86_64.rpm
Wrote: /root/rpmbuild/RPMS/x86_64/httpd-debuginfo-2.4.6-90.el7.x86_64.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.5JJBBV
+ umask 022
+ cd /root/rpmbuild/BUILD
+ cd httpd-2.4.6
+ rm -rf /root/rpmbuild/BUILDROOT/httpd-2.4.6-90.el7.x86_64
+ exit 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

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

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 rpmbuild directory 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 rpmbuild structure
[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/
├── RPMS

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

we are learning how to create rpm package in Linux

%setup -q


rm -rf %{buildroot}
mkdir -p %{buildroot}
cp -a * %{buildroot}

rm -rf %{buildroot}


* 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 scriptlets we can define %pre, %post and many other sections of rpm spec file
  • 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 scriptlets to 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 SOURCES will 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 spec file
  • We do not keep individual files in SOURCES, instead we must bundle the content and then place the bundle in SOURCES directory

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 -
+ '[' 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

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



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.


If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can either use the comments section or contact me form.

Thank You for your support!!

Leave a Comment