If you haven't read the Part 1 in this series, you will want to read that first before proceeding with this article.
Building RPM packages without being root
As you saw in Part 1, building an RPM package normally requires you to be logged in as root. There are a few reasons for this:
- RPM installs the software during the packaging process, and normally only the root user can write to the install directories.
- RPM expects to read and write in the directories under /usr/src/redhat, which ordinary users cannot modify.
We looked at how to solve the first problem in Part 1 by using an RPM build root.
To solve the second problem, you can tell RPM to look for and create
files in a different set of directories, by changing its
%_topdir setting. Create a file in your
home directory called .rpmmacros as follows:
%_topdir /home/your_userid/rpm
This tells RPM that all the directories it formerly
looked for under /usr/src/redhat should instead
be under /home/your_userid/rpm.
You should now
create this entire directory tree:
~/rpm
~/rpm/SOURCES
~/rpm/SPECS
~/rpm/BUILD
~/rpm/RPMS
~/rpm/RPMS/i386
~/rpm/SRPMS
(If you like, you can put any of these directories anywhere you
want, by redefining other macros in RPM. Some macros you
might consider changing include
%_sourcedir,
%_specdir,
%_srcrpmdir,
%_builddir, and
%_rpmdir.
Look at /usr/lib/rpm/macros for the default values.
For this example, we'll just leave them all under
~/rpm.)
Now copy the indent-2.2.6.tar.gz file (see Resources later in this article) to
~/rpm/SOURCES, and without logging in as root,
run rpm -ba indent-2.spec (these files are the same
ones from Part 1). RPM will build indent
under ~/rpm/BUILD, and put the binary RPM package
in ~/rpm/RPMS/i386 and the source package in
~/rpm/SRPMS.
By contrast, try using indent-1.spec, the spec file
without a build root. RPM will fail while trying to install indent
in /usr/local/bin.
Using a build root and setting RPM's i%_topdir
will let you build a large number of software packages without
having to run as root, but it's not always this easy.
First, some packages aren't as easy to install into a
build root as indent. With any package that
isn't developed using GNU autoconf, you'll have to look closely
to see if there's a way to do the install into a different directory,
and maybe modify the Makefile to force it. In the next
section, I'll show you how to build modified programs
using RPM.
Second, a very few packages will attempt to do things during their normal install that only root can do, such as:
- Creating special files (pipes, device files, etc.)
- Modifying system configuration files
You'll have to handle these case-by-case. Often you can do the necessary work in a post-install script (a script that runs after installing the RPM). I'll cover those in a later article, but briefly, you can add a "%post" section to your spec file and put some Linux commands there to run after the RPM is installed.
Suppose you've got some software that you want to package as an RPM, but it won't build on Linux without some changes. You don't own the software, so you can't make official changes to it.
What you need to do is patch or modify the official version of the software. But distributing modified versions of other people's software is generally considered impolite, so you'd like to make your changes available too. That way, anyone using your package can see what you've done and decide if your changes are acceptable.
This is not an uncommon situation, and RPM provides some help. You can set up an RPM package so that the binary RPM file contains your modified version of the program, and the source RPM contains, not just the original sources, but your changes and all the details of how they're applied and built.
Your steps will be the following:
- Figure out what changes to make to the source code to get the software to work.
- Create a patch file capturing your changes.
- Add the patch to your RPM spec file.
Step 1. Figure out what changes to make to the source code
As usual, the first thing to do is to get the software to compile and run without RPM. Keep track of which files you have to change. If necessary, you can create new files or remove files that came with original source.
For this example, I extracted the code to a directory indent-2.2.6-working. I modified indent.c to print a friendly message when the program starts, then verified that the program still built and worked.
Step 2. Create a patch file capturing your changes
Now you want to create a patch file that captures just your changes. Here's one way to do it. It's a bit tedious, but ensures that you capture all your changes.
- Do a clean extract of the software into a new directory,
and then copy your versions of the files you changed over the
ones you just extracted. This is so that you don't have any
additional files in the directory that might have been created
while you were building and testing the software before.
Similary, copy any new files you created, and delete any
files you deleted before.
For this example, I did a clean extract into a directory indent-2.2.6-my, and copied over the file indent.c.
-
Do another clean extract into another directory. This
provides a copy of the original software to compare yours
to. For this example, I did an extract into indent-2.2.6.
I now had three directories:
- indent-2.2.6-working
- where I got things to work
- indent-2.2.6-my
- the software plus my changes
- indent-2.2.6
- the unchanged software
-
Generate the patch file with a command like this from the
parent directory of these three directories:
diff -uNr indent-2.2.6 indent-2.2.6-my >indent-2.2.6.patch
Note that we use the options -uNr to diff.
-u creates a patch in unified format, which is a
bit more compact than the default. -N ensures that
our patch file will correctly handle the cases where we have created
or deleted files. -r compares all the files in all the
subdirectories of the two directories named on the command line.
Note also that the names of the directories do not matter, as long as you keep them straight. The directory names will appear in the patch file, but we'll be instructing the patch program to ignore them.
Now look at the patch file, indent-2.2.6.patch. Here's what mine looks like:
Listing 1. indent-2.2.6.patch
diff -uNr indent-2.2.6/indent.c indent-2.2.6-my/indent.c
--- indent-2.2.6/indent.c Thu Nov 16 22:01:04 2000
+++ indent-2.2.6-my/indent.c Wed Sep 26 14:33:11 2001
@@ -1864,6 +1864,8 @@
int using_stdin = false;
enum exit_values exit_status;
+ printf("Hello from Dan
");
+
#ifdef _WIN32
/* wildcard expansion of commandline arguments, see wildexp.c */
extern void wildexp (int *argc, char ***argv); |
Sometimes you'll notice that diff has picked up changes
you hadn't intended to make. You might want to go back, clean up your
code, and generate the patch again, until you get a nice clean patch file.
Once you have the patch the way you want it, it's nice to add a comment explaining what you did. You can add text at the beginning or end of the patch file without breaking anything.
Listing 2. indent-2.2.6.patch -- plus comment
Dan Poirier - 2001-09-26 - added a friendly greeting as indent starts.
This is just an example.
diff -uNr indent-2.2.6/indent.c indent-2.2.6-my/indent.c
--- indent-2.2.6/indent.c Thu Nov 16 22:01:04 2000
+++ indent-2.2.6-my/indent.c Wed Sep 26 14:33:11 2001
@@ -1864,6 +1864,8 @@
int using_stdin = false;
enum exit_values exit_status;
+ printf("Hello from Dan
");
+
#ifdef _WIN32
/* wildcard expansion of commandline arguments, see wildexp.c */
extern void wildexp (int *argc, char ***argv); |
Step 3. Add the patch to your RPM spec file
Now it's time to get RPM to use your patch. Copy it to your SOURCES directory, probably ~/rpm/SOURCES if you followed the advice earlier, then make the following changes to your spec file:
Listing 3. indent-3.spec: using indent-2.2.6.patch
Summary: GNU indent
Name: indent
Version: 2.2.6
Release: 3
Source0: %{name}-%{version}.tar.gz
Patch0: %{name}-2.2.6.patch
License: GPL
Group: Development/Tools
BuildRoot: %{_builddir}/%{name}-root
%description
The GNU indent program reformats C code to any of a variety of
formatting standards, or you can define your own.
%prep
%setup -q
%patch -p1
%build
./configure
make
%install
rm -rf $RPM_BUILD_ROOT
make DESTDIR=$RPM_BUILD_ROOT install
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root)
/usr/local/bin/indent
%doc /usr/local/info/indent.info
%doc %attr(0444,root,root) /usr/local/man/man1/indent.1
%doc COPYING AUTHORS README NEWS
|
Now build your package with rpm -ba indent-3.spec.
If you watch closely, you'll see RPM apply your patch during the
build.
The line %Patch0: %{name}-2.2.6.patch tells
RPM the name of the first patch file. You could add
%Patch1, %Patch2, etc. if necessary.
The %patch -p1 line in the %prep
section is an RPM macro that will run the patch program on your
system, in the build directory, with your first patch file as input.
You need to pass -p1 to the patch program to tell it
to strip one level of directories from the paths in the patch file,
since the patch includes the indent-2.2.6 directory name
but RPM will run the patch from inside that directory.
Now that you understand the basics of how RPM packages are built, you can learn a lot by looking at examples. One of the best sources of examples is your own Linux distribution. RedHat, for example, comes with an entire CD of source RPM packages. Here's how to use them.
A source RPM package contains:
- A .spec file
- One or more source files
- Any patch files that were used
You can install a source RPM package similarly to binary RPM packages,
using rpm -i filename.rpm. After installing,
the .spec file will be in your %_specdir
directory, and the
source files and patch files will be in your %_sourcedir
directory. That will be ~/rpm/SPECS
and ~/rpm/SOURCES if you've created the .rpmmacros
file described above.
Now you can read the .spec files for the packages that Red Hat
distributes themselves. You can try building them with
rpm -ba foo.spec and watch what happens, and fiddle
with the spec files to try new things.
A good one to start with is Red Hat's source RPM package for the GNU indent program. See if you can figure out why their .spec file is different from the one in this article.
Portability of binary RPM packages
Unfortunately, binary RPM packages are not very portable. In most cases, an RPM built on one Linux distribution won't work on another. It might not even work on another version of the same distribution!
There are many reasons for this, including differences in base kernel versions, library versions, and directory structures.
This is unfortunate, and efforts like the Linux Standard Base (see Resources) are attempting to reach consensus among distributions on the issues that break portability. Maybe one day, any RPM built on a major Linux distribution will install and run on any other major Linux distribution running on the same processor.
For now, you should plan to build your RPMs on as many distributions as you want them to run on, or find volunteers to do it for you.
Distributing your work: tar files and source RPM packages
In order to get others to build your software on as many distributions as possible, you'll want to make your .spec file and patch files available.
The best approach is to make changes directly to the software if necessary so it will build on Linux, and include the .spec file in the distribution. If the .spec file is in the tarball (.tar.gz file) with the source, a user can simply run:
rpm -tb foo.tar.gz |
and build the binary RPM for the package -- no need to even unpack the tar file!
If you can't get your .spec file included in the software, then you can distribute a source RPM package. With that, a user can run:
rpm --rebuild foo.src.rpm |
and build a binary RPM on their system.
- The previous article in this series
introduces the process for building software packages with RPM.
- Source for files described in this article:
- The RPM Web site
has pointers to many useful resources. The RPM e-mail list is a good place to ask questions.
- Maximum RPM
is a book about using RPM. It has become quite outdated,
but an effort is underway to update it.
- The RPM HOWTO
is also getting somewhat dated. It covers some of the same ground
as this article.
- Eric S. Raymond's
Software Release Practice HOWTO document
is not specific to RPM or Linux. But it does give many good tips on releasing software
in a way that makes it easy for users to use and programmers to
contribute fixes and improvements.
- Read about the Linux Standard Base (LSB), whose goal is to "develop and promote a set of standards that will increase compatibility among Linux distributions and enable software applications to run on any compliant Linux system."
- The Free Software Foundation
is the source for GNU Indent and many other useful software packages.
- Get the IBM Developer Kit for Linux, Java 2 edition, version 1.3 or the IBM Software Evaluation Kit for Linux.
- Browse more Linux resources on developerWorks.
- Browse more Open source resources on developerWorks.
Dan Poirier is an Advisory Software Engineer at IBM. He currently works in Research Triangle Park, North Carolina on network appliances that run Linux. Contact Dan at poirier@us.ibm.com.





