You are on page 1of 6

Thanks to Jrummy on the write up.

---
Here is a HOWTO on getting an rpmbuild environment set up in order to inspect an
d modify sources and build RPM packages.
---
1) The first step is finding a system on which to build RPMs. If a customer is
going to be installing this package, it needs to
be built against the right libraries and possibly against a specific kernel. I
choose to keep a Xen VM for every variety of system
I will need, namely i386 and x86_64 for RHEL 3, 4, and 5. This way there is an
environment for testing and building no matter what the
customer is running (unless you are unlucky and get someone with a different arc
h :) Brew is your friend for when you need to build
multiple arches or arches you don't have access to. Taking the Engineering cour
se in SABA for Brew is very helpful in this area.
If you have no intention of ever providing a customer with a test or custom pack
age then having special RPM building systems
is not necessary. You can build a RHEL 4 source RPM on Fedora 10 and the source
will look the same. If you do intend on
providing customers with packages, make sure you know what you are doing and you
have thoroughly tested the package first. If
you're not sure then leave it up to L2 or SEG to make the decision. ALWAYS info
rm the customer that it is a test package that
has not undergone any QA and it is not intended for production use.
2) The next step is creating a user for building RPMs. We do not want to build
RPMs as root because of the possibility of
overwriting critical OS files if something goes wrong.
# useradd rpmbuild
# passwd rpmbuild
# su - rpmbuild
3) Now we want to set up our ~/.rpmmacros file with some settings that will make
our environment much cleaner:
%packager Robin Price II <rpricer@redhat.com>
%dist .el5_2
# no debug RPM's, please...
%debug_package %{nil}
%_topdir /home/rpmbuild/redhat
%_tmppath /home/rpmbuild/packages/redhat/tmp
%_rpmtopdir %{_topdir}/%{name}-%{version}
%_builddir %{_rpmtopdir}
%_rpmdir %{_rpmtopdir}
%_sourcedir %{_rpmtopdir}
%_specdir %{_rpmtopdir}
%_srcrpmdir %{_rpmtopdir}
%_prefix /usr
%_exec_prefix %{_prefix}
%_bindir %{_exec_prefix}/bin
%_sbindir %{_exec_prefix}/sbin
%_libexecdir %{_exec_prefix}/libexec
%_datadir %{_prefix}/share
%_sysconfdir /etc
%_sharedstatedir %{_prefix}/com
%_localstatedir /var
%_lib lib
%_libdir %{_exec_prefix}/%{_lib}
%_includedir %{_prefix}/include
%_oldincludedir /usr/include
%_infodir %{_datadir}/info
%_mandir %{_datadir}/man
These settings tell rpm to install source packages to /home/rpmbuild/redhat/<pac
kage>-<version>/. This is much easier to use than
the default method of just dropping everything into BUILD,SOURCES,SPECS,RPMS,SRP
MS. Try finding a file you need in SOURCES after
installing a dozen packages and a kernel all in the same directory and you will
see what I mean. Notice the
%dist variable is defining what OS level I am at. You'll notice a lot of packag
es named like cman-2.0.84-2.el5_2.3, which indicates
its the 3rd release of this 5.2 version of the package. Keep this dist tag upda
ted if you want to follow the same naming scheme.
Now we need to make the directory for rpmbuilding
# mkdir /home/rpmbuild/redhat
3) It is usually beneficial to have engarchive and curly mounted on your buildin
g system so you have quick access to every version
of a package. Add these lines to fstab:
curly.devel.redhat.com:/vol/engineering/devarchive /mnt/curly nfs ro,nosuid,no
dev,noatime,intr,tcp,rsize=8192,wsize=8192 0 0
engarchive.rdu.redhat.com:/engineering/archives2 /mnt/engarchive nfs ro,nosuid
,nodev,noatime,intr,tcp,rsize=8192,wsize=8192 0 0
And mount them
# su -c 'mkdir /mnt/curly /mnt/engarchive'
# su -c 'mount -a'
If you just want a GA release of a package, use engarchive (/mnt/engarchive/rele
ased/RHEL-5-Server/U2/source/SRPMS/*). However if you
need a Z-stream package or something that hasn't been released yet, you'll find
it in curly
(/mnt/curly/redhat/brewroot/packages/<package>/<version>/<release>/source/*).
You are done configuring your environment. Now its time for an example.
################
You are ready to start installing and building source RPMs. Lets say we need to
apply a test patch to the 5.2 device-mapper-multipath
in order to see if it resolves a customer's issue on x86_64. Here's how I'd do
it:
I log in to my 64-bit machine as rpmbuild and install the version of the RPM tha
t the customer is running
# ssh rpmbuild@machine1
# rpm -ivh /mnt/curly/redhat/brewroot/packages/device-mapper-multipath/0.4.7/1
7.el5/src/device-mapper-multipath-0.4.7-17.el5.src.rpm
1:device-mapper-multipathwarning: user brewbuilder does not exist - using root
warning: group brewbuilder does not exist - using root
warning: user brewbuilder does not exist - using root
warning: group brewbuilder does not exist - using root
warning: user brewbuilder does not exist - using root
warning: group brewbuilder does not exist - using root
warning: user brewbuilder does not exist - using root
warning: group brewbuilder does not exist - using root
########################################### [100%]
(Those messages are normal. This source RPM was created as the brewbuilder user
via the brew system). Now we should have the contents
of the SRPM available
# cd ~/redhat/device-mapper-multipath-0.4.7/
# ls
355961_better_fix.patch device-mapper-multipath.spec multipath-tools-0.4.7.
rhel5.10.tgz only_static.patch
Notice we have a spec file, the source tarball, and a few patches. Now we could
just unpack the tarball and look at the source, but then
the patches don't get applied so we aren't actually looking at the same code as
the customer is running. So we want to 'prep' the source:
# rpmbuild -bp device-mapper-multipath.spec

Note that you may get dependency errors here. If you are only inspecting the so
urce then you can use the --nodeps option since you don't
care about dependencies. However if you are going to roll a new RPM then these
dependencies need to be installed.
Now we should have an unpacked and patched source tree.
# ls multipath-tools-0.4.7.rhel5.10/
AUTHOR COPYING kpartx Makefile multipath.conf.annotated
multipathd path_priority
cciss_id devmap_name libcheckers Makefile.inc multipath.conf.defaults
multipath-tools.spec.in README
ChangeLog FAQ libmultipath multipath multipath.conf.synthetic
Multipath-usage.txt TODO
Let's say that we need to apply a simple patch to to the hwtable to no longer us
e the queue_if_no_path option for NETAPP devices. (Note that
this is a stupid change for us to make as a patch in the RPM since we could just
change the multipath.conf file. Its just as an example). Maybe
we found this patch upstream or on a mailing list somewhere and we think it reso
lves our customer's issue. The problem is the patch we found
was written against a different version of the source, so the line numbers don't
match up. Now we have to create our own patch that applies to
the current RHEL 5 version. If we had gotten this patch from Bugzilla or somewh
ere internal to Red Hat, we may not have had to recreate this patch
since it might have already had the right lines.
First we locate the file we are patching and make a copy of the original:
# cd multipath-tools-0.4.7.rhel5.10/libmultipath/
# cp hwtable.c hwtable.c.orig
# vi hwtable.c
Now we modify hwtable.c to have the fixes we need. Namely we change this line:
.features = "1 queue_if_no_path",
to this:
.features = "0",
Now we save the file and exit. We need to get back to the build directory:
# cd ~/redhat/device-mapper-multipath-0.4.7/
Its time to build the patch. We can use the gendiff command to traverse the sou
rce and search for any file with a corresponding .orig file. For
any one it finds, it compares the two and creates a diff patch.
# gendiff multipath-tools-0.4.7.rhel5.10/ .orig > netapp-no_queuing.patch

Let's take a look at this patch. Note that sometimes engineers may accidentally
leave .orig files laying around in the source directory so gendiff
may have spit out several patches you don't need (and won't work since they were
already applied). To get around this you can do any one of:
a) Change the .orig to something else and run gendiff again
b) Extract the one patch you need from the ones spit out from gendiff and discar
d the rest
c) Manually run 'diff -pruN' against the original and new file
The first two lines just show us what the old and new source files are:
--- multipath-tools-0.4.7.rhel5.10/libmultipath/hwtable.c.orig 2008-12-20 12:
29:37.000000000 -0500
+++ multipath-tools-0.4.7.rhel5.10/libmultipath/hwtable.c 2008-12-20 12:33:18
.000000000 -0500
Note the next line:
@@ -357,7 +357,7 @@
This tells us the block of code we're about to see starts at line 357. The next
few lines are reference lines:
.product = "2145",
.getuid = DEFAULT_GETUID,
.getprio = "/sbin/mpath_prio_alua /dev/%n",
Lines that do not have a + or - before them mean that those lines will remain th
e same after the patch. If these lines can't be found at build time
then the patch will fail.
- .features = "1 queue_if_no_path",
+ .features = "0",
We see that we are removing the queue_if_no_path line and replacing it with one
specifying features = "0".
.hwhandler = DEFAULT_HWHANDLER,
.selector = DEFAULT_SELECTOR,
.pgpolicy = GROUP_BY_PRIO,
Again these are just reference lines.
Now that we have our patch ready, we need to tell RPM how to apply it. Let's op
en up the spec file:
# vi device-mapper-multipath.spec
We see where there are already a few patches defined so we add ours
Patch0: only_static.patch
Patch1: 355961_better_fix.patch
Patch2: netapp-no_queuing.patch <- Line we added
We also need to add it to the %prep section so rpmbuild will apply that patch th
e next time it preps the source:
%prep
%setup -q -n multipath-tools-0.4.7.rhel5.10
%patch0 -p1
%patch1 -p1
%patch2 -p1 <- Line we added
Note the significance of the -p1. Remember the paths we used in the patch:
--- multipath-tools-0.4.7.rhel5.10/libmultipath/hwtable.c.orig 2008-12-20 12:
29:37.000000000 -0500
+++ multipath-tools-0.4.7.rhel5.10/libmultipath/hwtable.c 2008-12-20 12:33:18
.000000000 -0500
The -p1 tells us to strip off multipath-tools-0.4.7.rhel5.10/ from the pathname
and apply the patch from there. If our patch had specified something like this:
--- old-source/multipath-tools-0.4.7.rhel5.10/libmultipath/hwtable.c.orig 200
8-12-20 12:29:37.000000000 -0500
+++ new-source/multipath-tools-0.4.7.rhel5.10/libmultipath/hwtable.c 2008-12-
20 12:33:18.000000000 -0500
We would want to use -p2 to tell it to strip 2 slashes off the front.
Now we are almost done. Still in the spec file, let's change the release name o
f this package so that when someone goes to install it, rpm
recognizes the new version is higher. We also want to make sure that anyone tha
t comes across it in the future knows where it came from so we use a
descriptive name:
Release: 17%{?dist}_crm1234567
Time to build our RPM! Save the file and exit then do
# rpmbuild -bb --target=x86_64 device-mapper-multipath.spec
The -bb tells it to only build the binary. We could've done -ba to build everyt
hing or -bs to only build a source rpm (which we could then upload to brew, hint
hint...). If you can read really fast as the output flies by you should see you
r patch get applied hopefully without errors. Once it's done we should have our
finished binary
# ls x86_64/
device-mapper-multipath-0.4.7-17.el5_2.crm1234567.x86_64.rpm
You'll want to install this on an x86_64 system to make sure there are no obviou
s errors. If possible, its best to do some more extensive testing in the area o
f
focus for the patch just to make sure you didn't break anything less obvious tha
n installation.

You might also like