Building Custom Red Hat Linux RPMs from Source Code

Posted by John

Sep 30, 2010 12:00:00 AM Linux, Provisioning, RPM

Using rpms is a good way to keep track of changes and dependencies. If you are using Red Hat Satellite, it can be much more efficient to push out a modified package as an rpm rather than building it locally on each machine. And it can be rolled back or updated easily as well. Previously I discussed using rpms to distribute Java related software. In those cases, the software was in binary form and did not need any compiling or patching, just packaging and installation. Much Linux software is written in C and to be built and packaged requires additional steps than those for Java programs. This is an example of how to set up and do these steps for the package “sed”, into which we will introduce a simple modification. Many packages already have a lot of flexibility just by modfying the “configure” command parameters when they are built. That will also be covered here. First, we need to prepare a machine for rpm development. It is recommended not to build as root. As a regular user, we do the following: create a gpg key
$ gpg --gen-key   (interactive, create gpg key)
$ gpg --export --armor  > jholland.pub  # to be imported on
$ # machines that will verify the rpm's signature
$ gpg --list-keys
$ gpg --list-keys
/home/jholland/.gnupg/pubring.gpg
---------------------------------
pub   1024D/4E73A6C2 2010-09-29
uid                  John Holland
sub   2048g/439B3988 2010-09-29
This will show the keys, the gpg_name macro in .rpmmacros below will need to be the second half of the second column of the “pub” line above our email (or are rpm builder userid’s email). A file is created, ~/.rpmmacros which contains configuration options for rpm building. The “topdir” is the directory where we are building the rpms. The “gpgname” is the value in the list of keys above.
$ cat > ~/.rpmmacros
%_signature gpg
%_gpg_name 4E73A6C2
%_topdir /home/jholland/rpmbuild
^D
We need to create the directory structure where the rpms will be built. The directory should match the “topdir” in the .rpmmacros above.
$ mkdir -p ~/rpmbuild
$ cd ~/rpmbuild
$ mkdir BUILD RPMS SOURCES SPECS SRPMS
$ mkdir RPMS/{i386,i486,i586,i686,noarch,athlon}
As root, we install the packages needed to build rpms and develop software.
# yum install rpm-build    #(run as root)
# yum install yum-utils #(run as root)
# yum groupinstall "Development Tools"   #(run as root)
# yum groupinstall "Development Libraries"  #(run as root)
====================================================== Here we will install packages needed to build “sed” (run as root): (download the source rpm for sed)
$ yumdownloader --source sed   #(run as you)
(install the needed dependencies)
 
$ su -c 'yum-builddep ~jholland/sed-4.1.5-5.fc6.src.rpm'   #(run as you)
Now we will install the sources and spec for sed in the directory structure existing in "topdir" directory chosen in .rpmmacros:
$ rpm -ivh sed-4.1.5-5.fc6.src.rpm   #(run as you)
(this will fail if the directory specified in .rpmmacros (%_topdir) does not exist) At this point the source code and patches for “sed” are in ~/rpmbuild/SOURCES and the .spec file is in ~/rpmbuild/SPECS. The .spec file in SPECS controls the build. Now we actually will build the RPM from the source packages, patches and spec file. the RPM will be in (%_topdir)/RPMS when it is done.
$  cd ~/rpmbuild/SPECS
$ rpmbuild -ba --target=`uname -m` sed.spec  #(run as you)
This produces a sed-4.1.5-5.x86_64.rpm in the ~rpmbuild/RPMS directory. Why go to all this trouble? So far all we have done is rebuild an rpm available from Red Hat. The answer is that we can make our own changes to it, and have those changes recorded to be reproducible, and able to be distributed quickly in binary format. In some cases all that is needed is to change the configure script command. Typically, building a package by hand on Linux involves
./configure; make; make install
In rpm building these steps are done for us by the rpmbuild command. The SPEC directory contains .spec files for the rpms we are building. In those .spec files, there is a section with the configure command and all its options. By editing that spec file we can cause the rpm to be built with different options. It would be a good idea in this scenario to give the package a different name or version or release so that when installing or querying it it is clear it is a modified build. More significant changes to a package would probably be code changes. To capture these, we can create a patch file using the diff command. Many packages already have numerous patches in the distributed source from Red Hat; you can see them in the SOURCES directory. When your source rpm is built, it creates a directory in the BUILD subdirectory of your topdir containing the source of the package. You can create a copy of that directory, modify it as you see fit, and then create the patch file by using diff. This would look something like the following:
$ cd ~/rpmbuild/BUILD
$ cp -r sed-4.1.5/ sed-4.1.5jh
$  cd sed-4.1.5jh/
$  cd sed
$  vi sed.c   # make trivial change
$  cd ..
$  cd ..
$  diff -uNrp sed-4.1.5 sed-4.1.5jh/ > ../SOURCES/sed-4.1.5jhpatch.patch
Our patchfile should have a unique name to distinguish it from the other patches for this package. Then we have to modify the .spec file (or a copy with a different name) to use our patch. There are two sections of it that we need to add a line to:
Source0: <a href="ftp://ftp.gnu.org/pub/gnu/sed/sed-%">ftp://ftp.gnu.org/pub/gnu/sed/sed-%</a>{version}.tar.gz
Source1: <a href="http://sed.sourceforge.net/sedfaq.txt
Patch0:">http://sed.sourceforge.net/sedfaq.txt
Patch0:</a> sed-4.1.5-utf8performance.patch
Patch1: sed-4.1.5-bz185374.patch
Patch2: sed-4.1.5-relsymlink.patch
Patch3: sed-4.1.5jhpatch.patch
(in this case we added our patch as the last line)
%prep
%setup -q
%patch0 -p1
%patch1 -p1
%patch2 -p1
%patch3 -p1
(ditto) We then build our modified package with a command like
$ rpmbuild -bb --target=`uname -m` sedjh.spec
In this case a sed-4.1.5jh-5 package is built (an rpm) We can then copy it over to a clean machine, and run
# rpm -ivh --force sed-4.1.5jh-5.rpm  #(as root)
on that machine to install it ( the –force is necessary, otherwise it rejects it as not being a new update) We can see by running it that our trivial modification was installed by the new rpm. - See more at: http://vizuri.com/insights/blog/2010/09/building-custom-red-hat-linux-rpms#sthash.BmCBo6i9.dpuf

Posted by John

    

Posts by Topic

see all
Request a Complimentary Docker Consultation

An Open View

Vizuri Blog

Subscribing to our blog is a great way to stay up to date with the latest information from Vizuri, as well as our strategic partners. We focus on providing a range of content that is practically useful and relevant from both a technical and business perspective.

We promise to respect your privacy.

×