Creating RPMs


These notes give brief instructions on how to make your own RPMs - the package management system used by RedHat Linux and many other Linux distributions.  The system is not really hard to use, but there are some tricks that will help you get started.  These instructions aren't really intended as a complete tutorial - rather a set of hints to get you going.  For a more complete tutorial, I recommend the Mandrake RPM Howto.  There is also the RPM Howto, which has some useful information but is more roundabout and less specific.

How it works

Basically, RPM is a system for placing all the files for a software package in one object (the rpm file) with some information about where the files go, what type they are and what the package does.  Creating a RPM for your own software requires three basic steps:
  1. Making sure the rpm-build software is installed.
  2. Perhaps modifying your software install process to be rpm "aware".
  3. Writing a spec file.
This last step is the heart of the matter.  The "spec" file tells rpm-build everything that it needs to know to make the software and install it.

The rpm build process basically works as any software build works.  First it compiles the software, then it installs it.  The difference is that rpmbuild generally installs files in a subdirectories of a temporary directory - that way it doesn't interfere with software already installed and it is easy to identify the files that belong to the software package.  After the software is "installed", rpm-build compresses all the files in the subdirectory into one file, and the information in the spec file is included.  This way, when you go to really install the resulting binary rpm, the files will end up in the correct place and rpm knows which files are part of the package.

Creating Your Environment

 A few simple additions to your home directory can make building and testing RPMs a lot easier as a normal user and protects you from yourself.  It is tempting to just build RPMs as root since this is the account you will use to install and usually building as root is already configured on Redhat systems.  Nevertheless, building as root is very dangerous.  If the RPM spec file has intentional or unintentional errors, you risk deleting important files, destroying your system, compromising protected information, etc.  

That being said, building as the normal user you login to every day isn't much better.  Although your system is better protected, your precious home directory isn't - email, secret keys, etc.  A malicious software package downloaded off the web could try to gather important information from your account and forward it on to a waiting hacker via email, for example.

The best solution is to create a separate account solely for building and testing RPMs.  I created an "rpmbuild" account and use it for just this purpose.  This might seem a bit tedious, but it is a lot safer.  Below, when I refer to ~/ I am assuming you are logged into the "rpmbuild" account.

Now to configure your system.  First you will want to create the following directories:

$ mkdir ~/rpm ~/rpm/BUILD ~/rpm/RPMS ~/rpm/RPMS/i386 ~/rpm/RPMS/i686 ~/rpm/RPMS/noarch ~/rpm/SOURCES ~/rpm/SPECS ~/rpm/SRPMS ~/rpm/tmp

These are the directories where the build process will take place.  You might have to add more directories if your system architecture is different from i386 or i686.

Then you need to create a file ~/.rpmmacros with at least the following lines:

%_topdir               /home/rpmbuild/rpm
%_tmppath              /home/rpmbuild/rpm/tmp

Replace "rpmbuild" with the name of the account you will be using to build RPMs if different.  This file lets rpm-build know that you want to build locally by telling it to look for and create files in subdirectories of /home/rpmbuild/rpm.  There are many other things that could be added to this file, but this is a start.  

Modifying the install routines

Next you might need to modify the process by which the software normally builds and installs (ie. from a tarball or from CVS, without RPM).  Basically, you want to modify your Makefile or install script so that, when rpm-build is building your software, it knows to place the files in a subdirectory of the build root (ie. that temporary directory where the files will be placed).  If the Makefile or automake file is well written, you might not need to modify anything at all as rpmbuild relies on many standard names.  If the Makefile is poorly written, but the package installs relatively few files, it is sometimes easiest to simply install the files manually from inside the spec file into the appropriate directories for rpmbuild.

If not, you will have to make changes.  rpmbuild makes this easy.  When installing, it generally makes available a wide variety of variables that indicate where things should go.  Examples:

There are many more.  I don't intend to discuss them all.  The point is that Makefile's should expect and use these variables when deciding where to install things.  For example, at the beginning of one of my Makefile's, I put the following lines:
# These are typically taken from rpm, but, if not, defined here.
bindir=/usr/local/bin
libdir=/usr/local/lib
sysconfdir=/etc
mandir=/usr/local/man
ifdef RPM_DOC_DIR
sixdocdir = $(RPM_BUILD_ROOT)/$(RPM_DOC_DIR)/$(RPM_PACKAGE_NAME)-$(RPM_PACKAGE_VERSION)
else
sixdocdir = /usr/local/doc/sixpack
endif

The first few lines define default install locations.  If rpmbuild is building the software, these will be ignored and the values rpmbuild provides will be used.  The ifdef part looks for a variable which indicates if rpmbuild is present and uses it if so.  

Writing the spec file

Writing spec files is not something that I want to discuss at length.  That is explained in the Mandrake tutorial quite well.  I do have some suggestions to make the process as painless as possible:

Building the RPMs

Finally you are about ready to make an RPM.  The following commands should do the trick:
$ cp myspec.spec ~/rpm/SPECS/
$ tar -zcvf ~/rpm/SOURCES/mypackagename-myversionnumber.tar.gz mypackagename-myversionnumber
$ rpmbuild -ba ~/rpm/SPECS/myspec.spec

Notice that your source files should be in a directory with the appropriate name and version number before tar'ing.

Signing Your RPMs

Recently, I have begun to submit RPMs to the Fedora project.  They require all submissions to be digitally signed for security.  As I had never used gpg or any of the other tools for signing packages, it was quite difficult to get started.  Here are some basic instructions on how to do it.  For more information, consult the GPG Mini-Howto.

First you will want to do all signing in your normal user account or in a special signing account, but NOT the build account.  This is for security reasons.

As your normal user, you generate a secret key with:
$ gpg --gen-key
This will ask you some questions regarding the name of the key you are generating, often called the USERID, UID or KEYID.  Then you will enter a password.  This will generate a key and it will be stored in some keyring somewhere.
$ gpg --list-sigs
will produce a list of your signatures like:
/home/USERNAME/.gnupg/key_ring_file.gpg
----------------------------
pub  1024D/xxxxxxxx 2003-10-15 David M. Kaplan (...) <email_address>
sig       xxxxxxxx 2003-10-15   David M. Kaplan (...) <email_address>
The "David M. Kaplan (...) <email_address>" part is the USERID.  

Now you need to generate a public key to give to other people and publish it so Fedora users can get the key.
$ gpg --armor --export "USERID" > my.key.file.asc
$ gpg --keyserver pgp.mit.edu --send-key "USERID"

The public key should be spread around as much as possible.  It is used by others to verify that you signed your packages.  It can also be used to send encrypted messages to you (which you open with the private companion to the public key).

Now create a ~/.rpmmacros file in the account you will use for signing.  Place the following lines in the file:
%_signature gpg
%_gpg_name USERID
%_gpgbin /usr/bin/gpg
Now you can sign packages at will with:
$ rpm --addsign name_of_rpm_package

Fedora Hints

Submitting packages to Fedora isn't hard, but requires some time.  Here are a few suggestions/links to point you in the right direction:

Good luck.