Skip to content
reubenhwk edited this page Jan 7, 2011 · 4 revisions

Summary

$ rpmbuild -ts radvd-1.7.tar.gz
$ rpm --addsign rpm/SRPMS/radvd-1.7-1.src.rpm
$ rpm -qip rpm/SRPMS/radvd-1.7-1.src.rpm

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:

* $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 (...) 
sig       xxxxxxxx 2003-10-15   David M. Kaplan (...) 

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.