Skip to main content

Packaging software with RPM, Part 2

Building without root, patching software, and distributing RPMs

Dan Poirier (poirier@us.ibm.com), Software engineer, IBM, Software Group
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.

Summary:  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.

Date:  01 Dec 2001
Level:  Introductory
Activity:  10919 views

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:

  1. RPM installs the software during the packaging process, and normally only the root user can write to the install directories.
  2. 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.

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:

  1. Figure out what changes to make to the source code to get the software to work.
  2. Create a patch file capturing your changes.
  3. 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.

  1. 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.

  2. 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
  3. 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:

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.


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.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Linux, Open source
ArticleID=11177
ArticleTitle=Packaging software with RPM, Part 2
publish-date=12012001
author1-email=poirier@us.ibm.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers