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
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/
/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
scriptlets
we can define%pre
,%post
and many other sections of rpmspec
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 inSOURCES
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 - + 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.