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:
- Making sure the rpm-build
software is installed.
- Perhaps modifying your software install process to be rpm
"aware".
- 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:
- $RPM_BUILD_ROOT - where files will go. Generally
different for every RPM spec file. Typically
something like ~/rpm/tmp/nameofpackage-root, assuming you created
~/.rpmmacros as described above.
- $RPM_DOC_DIR - standard place for documentation files from
RPMs. Perhaps /usr/share/doc.
- %{_bindir} (or $bindir inside the Makefile) - this variable
will be defined as
the standard place to put binaries, relative to build root.
Something like ${RPM_BUILD_ROOT}/usr/bin.
- %{_libdir} - similar to bindir.
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:
- Use emacs. When you initially create a spec file with
emacs, it gives you a template that has most of the sections you will
ultimately want. Perhaps other text editors will do this, but I
don't know of any.
- Download source RPMs from the net, install them (using rpm
-ivh) as the build user and
look
at the results in ~/rpm/specs and ~/rpm/SOURCES. You can learn
more looking at other people's specs than reading a tutorial.
- In a spec file, anything that begins with a % is a macro.
rpmbuild will expand these. These macros are typically
defined in /usr/lib/rpm/macros and nearby files.
- Some macros are particularly important as they break the
installation process into sections. For example, %build tells
rpmbuild to begin generating a simple bash script that will be run to
build the software package.
- If the build process ends in an error, you can take a look
at the bash scripts in ~/rpm/tmp/rpm-tmp.*. Looking at these
scripts will tell you exactly what variables are being defined and what
is being done. Often times you can fix an error in the spec file
just by looking at the scripts.
- These scripts will be deleted upon termination of a section,
preventing you from looking at them. You can cheat and get a look
at them by forcing an error in that section. Just add "exit 1" to
your spec file in the appropriate section. Another useful thing
is
to "set; exit 1" in a spec file. This will exit and spit out a
list of defined environment variables. Finally, you can place
"bash -i" in a spec file to stop rpmbuild and drop to a shell where you
can take a look around.
- Use %config(noreplace) - this will save configuration files
from being replaced or over-written on update.
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:
- Read and follow the guidelines
for Fedora packages. This is the ultimate source.
Unfortunately, I find it a bit unorganized. That is why I
have my hints here.
- Use macros and RPM environment variables as much as possible
in your SPEC files. This makes them easier to maintain and more
portable. There is a useful list of macros.
- Follow the somewhat unusual SPEC file specifications
for Fedora packages.
- Be specific about the packages required to build and run the
package you wish to submit.
- If it is an application you are submitting, add a desktop
entry with desktop-file-install.
- GPG sign your packages.
- Submit a bug report to bugzilla.fedora.us
in the Fedora Meta section. It sometimes takes a while to
respond,
but once they did, they were very helpful.
Good luck.