The fight, my first attempt to make a readable rpm package building introduction.
(or how I learned to stop worrying and love the packages)
Note : This document is not intended to be a reference, not even a "howto". It will hopefully just do what I intended it for : Show the basics of the rpm build process for people already familiar with software compiling and rpm packages from a user point of view.
The four steps I will guide you through :
1) Know your enemy : The source!
In most cases, source code is a good thing. Here, we'll find out that quite often it's a real pain to deal with.
2) Prepare your weapon : The spec file!
This is the mandatory defensive weapon to stand a chance. Reusability will save us lots of time here.
3) Fight against the beast : The duel!
Now that we saw what the beast was up to, and that we are all set, it's time to strike back!
4) Happy end : The winner!
If you're as determined as I am, you will now enjoy a millisecond of glory, and many people will benefit from your fight.
The basic rules to never forget :
- Don't EVER build any packages as root, use your regular user account instead.
- Don't EVER modify any of the pristine sources, create patches or quick tweaks instead.
- If the software can be installed by hand, it can ALWAYS be packaged as an rpm (even if your brain does get damaged during the process).
- If you've done something completely new or useful (a new spec file, a patch, an init script...), ALWAYS take the time to offer it to the program's author(s) and the previous packager if there was one.
1) Know your enemy : The source!
So, here we go. Everything usually starts with a tarball of the program we want to package. The very first step is to analyse it to see how it needs to be compiled. Most of the time, the usual "./configure ; make ; make install" is given in the INSTALL or README file, but sometimes you may want to pass extra arguments to the configure script, or the installation needs to be done in a special way.
Here's how I proceed with the common "./configure ; make ; make install" case :
- Execute the ./configure script manually and try to see if it checks for some particular librairies to be able to later report them as build dependencies and most of the time as package dependencies too.
- Execute "./configure --help" to see if there are any special options we may want to turn on or off in our build.
- Execute "make" once the configure is done to be sure that everything compiles cleanly.
- Execute "make install prefix=/var/tmp/packagename" to see if it is possible to install the package in a temporary location.
For the last step (make install), there are various ways of getting things to work, and most of the time you will have to take a look at the Makefile if the "prefix=" way doesn't work. Very often you just need to execute "make install DESTDIR=/var/tmp/packagename" (quite common) or "make install ROOT=/var/tmp/packagename" (less common). Getting things to install at this point isn't vital, but keep in mind that we will need to install all the files we want contained in our package to a temporary "build root", which is a location from which they will appear as their final destination : If our package is to contain /bin/ls, it should have been installed to /var/tmp/packagename/bin/ls by our last command.
Now you should know the steps to build and install the package from sources with the desired options, and you should have prepared any needed patches (I won't get into details here since patches are rarely required, but it does happen, unfortunately...).
2) Prepare your weapon : The spec file!
Now, mighty knights, we need to get prepared : Let's start the spec file! This file is the soul of every rpm package, and will be the magic potion based on many ingredients (the rpm macros) that will allow us to change the ugly tarball into a handsome... rpm package :-)
Here's (quickly) how I usually structure my spec files :
- First the required usual informations (headers) in my usual arbitrary order
- The package description (%description)
- The build preparation (%prep)
- The sources build (%build)
- The sources install (%install)
- The post-build cleaning (%clean)
- The package pre/post(un)install scripts (%pre, %post, %preun, %postun)
- The package file list (%files)
- The package changelog (%changelog)
You can order most of these sections as you like, but the above is the most widely used way of doing it.
Here's what my simple skeleton spec file looks like :
Summary: . Name: Version: Release: License: GPL Group: Source0: Source1: Patch0: Patch1: URL: BuildRoot: %{_tmppath}/%{name}-root Requires: /sbin/ldconfig %description %prep %setup -q %build %configure make %{_smp_mflags} %install rm -rf %{buildroot} %makeinstall %find_lang %{name} %clean rm -rf %{buildroot} %post -p /sbin/ldconfig %postun -p /sbin/ldconfig %files -f %{name}.lang %defattr(-, root, root) %doc AUTHORS COPYING ChangeLog NEWS README TODO %{_bindir}/* %{_libdir}/*.so.* %{_datadir}/%{name} %{_mandir}/man8/* %files devel %defattr(-, root, root) %doc HACKING %{_libdir}/*.a %{_libdir}/*.la %{_libdir}/*.so %{_mandir}/man3/* %changelog * Fri Feb 14 2003 Matthias Saou <matthias.saou@est.une.marmotte.net> - Initial RPM release.
As you can see, it's fairly simple. I should even say *amazingly* simple if you have any programming notions. I personnaly don't know much about programming, but it didn't take me long to understand the structure of a spec file. I think the only two things you need are to already have installed tarballs yourself and a basic rpm knowledge, then you're all set! How's that for simplicity?
Now as you can see, many macros are used here. To see exactly what they do, take a look at the /usr/lib/rpm/macros file on your system... yes, all the ingredients needed to prepare the magic potion are right there. Basically, you could guess what most of the path macros (the %{_*} ones) are : %{_bindir} defaults to /usr/bin, %{_datadir} to /usr/share, %{_sysconfdir} to /etc and so on. They're all called the same as the options you usually feed the configure scripts (and "make install" too), simple eh? Well there's even better! The %configure macro executes the ./configure script with all those paths set (./configure --prefix=%{_prefix} --bindir=%{_bindir} ...) and even sets the CFLAGS environment variables (the compiler optimization flags) to the ones you gave rpm! Same for the %makeinstall macro that will prepend the build root (make install prefix=%{buildroot}%{_prefix} bindir=%{buildroot}%{_bindir} ...).
Starting with RedHat 7.0, there's even more magic performed : The binaries and libraries are automagically stripped and all the manpages gzipped!
That's it for the build part. If only life was as simple :-) Luckily it's not, and actually rpm package building isn't either... you often encounter "interesting" problems. <grin>
Now we need to know what files to include in the package. All of them will need to be referenced in the %files section, with a path relative to the build root, which is in fact the absolute path they will have in the final RPM. You need to be sure to not miss any (the package would be incomplete) or not to include too many (the build would fail). Since in Red Hat Linux 8.0, the default is to complain if some files exist in the build root but aren't referenced. I personally think it's a good thing.
By now you're probably wondering what the battle area looks like, right? Well, it's just like the files containing the marcos, it's been right under your nose forever : It's the /usr/src/redhat directory and all it's subdirectories. Now remember the rule saying NEVER EVER to build rpm packages as root? Well, that's why we won't touch those directories, so let's just copy them to our home (cp -a /usr/src/redhat ~/), or keep reading for a per-package structure instead. Now we need to change our rpm preferences to use this "~/redhat" directory instead of the original (and default) one, by editing the ~/.rpmmacros file (create it if necessary, and yes, ~/.rpmrc is depreceated now, so ignore anything you see that still relates to it). Here's mine :
%packager Matthias Saou %distribution Psyche Freshrpms %vendor Freshrpms.net %_signature gpg %_gpg_name Matthias Saou (Thias) %_topdir %(echo $HOME)/redhat %_tmppath %{_topdir}/tmp %_builddir %{_tmppath} %_rpmtopdir %{_topdir}/%{name} %_sourcedir %{_rpmtopdir} %_specdir %{_rpmtopdir} %_rpmdir %{_topdir}/RPMS %_srcrpmdir %{_topdir}/RPMS %_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm
You need to remove that last big block out if you intend to use the same structure as the one under /usr/src/redhat. If you use the lines above, you will get all files related to a build (sources, spec) in ~/redhat/%{name} and built packages under ~/redhat/RPMS/ which I prefer. So the %_topdir is the directory we're after to put our stuff in, and %_tmppath is the one where the build roots will go (see the BuildRoot: from the spec file). The first few options are simply default values that I've set so that I don't have to insert them into every spec file I write, as well as my GnuPG signature options.
Now here's how the subdirectories of this "top directory" will be used if you don't define the %_rpmtopdir and following options :
- SPECS/ : Where all the spec files go (we put)
- SOURCES/: Where all the tarballs and patches go (we put)
- RPMS/<arch>/ : Where all the binary RPMS we build will be written (we take)
- SRPMS/ : Where all the source RPMS we build will be written (we take)
- BUILD/ : Where all the preparation and build will be done (we cleanup, we use to debug)
If you do, here is what it would look like :
- <package name>/ : Where all the files for a given package go
- tmp/ : Where all the preparation and build will be done
- RPMS/ : Where all the binary *and* source RPMS will be written
So now our last move before the duel is to put our spec file, tarballs and patches in the right place. Usually, the spec file is called "packagename.spec" (e.g. rpm.spec) and does not contain any version number, it saves some time when updating the package since you don't need to rename it.
3) Fight against the beast : The duel!
Ready? Fight!
Oops! I forgot to explain how to use the weapon :-) It's pretty straightforward : Just execute "rpmbuild -ba yourfile.spec". This is the first move, you may need to do it over, over and over again fixing your spec file each time anything fails. I won't go into the details here, just a few pointers : The %_builddir (~/redhat/BUILD/ or ~/redhat/tmp/ depending on the setup you chose earlier) and "BuildRoot:" (%{_tmppath}/%{name}-root usually) directories are your allies, so are the rpm* files created under %{_tmppath} and the "rpmbuild --help" command (-ba is build all : binary + source, -bb is build binary, -bi --short-citcuit may be useful too sometimes).
4) Happy end : The winner!
You got your rpm packages to build at last? Great!
Well, now you can run the usual queries (rpm -qpi, rpm -qpl, rpm -qp --scripts) on your packages to see it they're correct, but you probably know them off by heart by now, so it won't teach you much. I bet you've never felt you knew so well a program you haven't made yourself. Who ever said rpms were for lazy people?
Last step, install your new binary rpm package on a system somewhere and see if the program works... if not, you've lost the battle... but if it does, you've made it! You are now a "knight or the rpm package" :-)
The end.
What?? you've only read this page and you haven't compiled anything yet?
Then I suggest you read this mini for-this-page-only faq while you're at it :
- What's the difference in between binary and source rpm packages?
- The binary is the program ready to be installed on a specific architecture, whereas the source is a totally different package containing the spec file, the sources and patches, and can be used to rebuild binary rpms in no time! Both were created during the build process.
- Where can I find detailed documentation on the spec file syntax?
- Read the very good "Maximum RPM" docs, they were written in 1997 (hey, I didn't even have a PC back then!), haven't been much changed since, but it's the absolute reference.
- Isn't there an easier way to learn how to build rpms than trying to make one from scratch right away?
- Sure! If all this scared you (just a bit I hope), simply set up the "battle field" as described above and install source rpms. You will then have all the tarballs, patches and spec files ready to be examined and rebuilt. This is how I learned many interesting tweaking methods, it's probably far from being the worst way to learn rpm package building :-)
- In the %files section, why not just put "/" since all the files I want to include and *only* them are in the build root?
- Simple question, simple answer : All directories referenced as being part of the RPM will be "rmdir"ed when it is erased, which means you will get an error if you included "/bin" in your package and there are still files in that directory when you remove the package from the system (which is very likely!). You can easily recognize rpms built by non-experts when you get such messages (for /usr/share most of the time). The %files section is not as simple as it looks, so be careful!
- How come you know rpms so well? And why the hell do you build so many?
- Well, I know rpms quite well because I've built many :-) (and some tricky ones, pheeeew!). I build them first of all because I have three home computers, two laptops, and two other computers at work... and yes, they all run Red Hat Linux at the moment (some have a multiboot... with FreeBSD). It's so much easier to perform a simple "rpm -Uvh" command rather than having to recompile the same program over and over and it's so much "cleaner" too... and the other reason is because I like to feel useful to others, that's why I make them all available from my website.
- You haven't said much about dependencies, why?
- Look at some existing rpms and you'll know how to deal with them. For instance, if the package you are building requires gtk, you will need to put "Requires: gtk+" in your spec file or you can even include a version, let's say "Requires: gtk+ >= 1.2.0". In these cases, you should also add a "BuildRequires: gtk+-devel >= 1.2.0" to be nice with the people trying to rebuild your source package so it will tell them right away if they don'y have that development package installed instead of failing later in the configure script.
- You haven't said much about the changelog, why?
- OK, I suppose looking at existing spec files *is* the best way to learn then... just have a look at a few and everything should be crystal clear in no time.
I hope you found this page useful, please send me any suggestions or corrections if you have any.