Level: Introductory Dan Poirier (poirier@us.ibm.com), Software engineer, IBM
01 Dec 2001 RPM is a widely used tool for delivering software for Linux; users can easily install an RPM-packaged product. In this article, the second in a series, Dan explains how to package software without running as root, how to handle software that won't build on Linux without changes, and how to distribute your work. 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/rpmThis 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. Caveats
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.
Patching software
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.
Learning by example
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:
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.
Resources
About the author  | |  | 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.
|
Rate this page
|