head 1.1; branch 1.1.1; access ; symbols MAXIMUM_RPM_1_0:1.1.1.1 VENDOR:1.1.1; locks ; strict; comment @# @; 1.1 date 2001.08.28.12.07.09; author rse; state Exp; branches 1.1.1.1; next ; 1.1.1.1 date 2001.08.28.12.07.09; author rse; state Exp; branches ; next ; desc @@ 1.1 log @Initial revision @ text @ 16. Making a Relocatable Package Subsections

   
16. Making a Relocatable Package

RPM has the ability to give users some latitude in deciding where packages are to be installed on their systems. However, package builders must first design their packages to give users this freedom.

That's all well and good, but why would the ability to ``relocate'' a package be all that important?

   
16.1 Why relocatable packages?

One of the many problems that plague a system administrator's life is disk space. Usually, there's not enough of it, and if there is enough, chances are it's in the wrong place. Here's a hypothetical example:

It doesn't have to be this way. RPM has the ability to make packages that can be installed with a user-specified prefix that dictates where the software will actually be placed. By making packages relocatable, the package builder can make life easier for sysadmins everywhere. But what exactly is a relocatable package?

A relocatable package is a package that is standard in every way, save one. The difference lies in the prefix tag. When this tag is added to a spec file, RPM will attempt to build a relocatable package.

Note the word ``attempt''. There are a few conditions that must be met before a relocatable package can be built successfully, and this chapter will cover them all. But first, let's look at exactly how RPM can relocate a package. And the one component at the heart of package relocation is the prefix tag.  

16.2 The prefix tag: Relocation Central

  

The best way to explain how the prefix tag is used is to step through an example. Here's a sample prefix tag:

Prefix: /opt

In this example, the prefix path is defined as /opt. This means that, by default, the package will install its files under /opt. Let's assume the spec file contains the following line in its %files list:

/opt/bin/baz

If the package is installed without any relocation, this file will be installed in /opt/bin. This is identical to how a non-relocatable package is installed.

However, if the package is to be relocated on installation, the path of every file in the %files list is modified according to the following steps:

  1. The part of the file's path that corresponds to the path specified on the prefix tag line is removed.

  2. The user-specified relocation prefix is prepended to the file's path.

Using our /opt/bin/baz file as an example, let's assume that the user installing the package wishes to override the default prefix (/opt), with a new prefix, say, /usr/local/opt. Following the steps above, we first remove the original prefix from the file's path:

/opt/bin/baz becomes /bin/baz

Next, we add the user-specified prefix to the front of the remaining part of the filename:

/usr/local/opt + /bin/baz = /usr/local/opt/bin/baz

Now that the file's new path has been created, RPM installs the file normally. This part of it seems simple enough, and it is. But as we mentioned above, there are a few things the package builder needs to consider before getting on the relocatable package bandwagon.   

   
16.3 Relocatable Wrinkles: Things to Consider

While it's certainly no problem to add a prefix tag line to a spec file, it's necessary to consider a few other issues:

Let's cover each of these issues, starting with the %files list.

16.3.1 %files List Restrictions

 

As mentioned above, each file in the %files list must start with the relocation prefix. If this isn't done, the build will fail:


# rpm -ba cdplayer-1.0.spec

* Package: cdplayer
+ umask 022
+ echo Executing: %prep
...
Binary Packaging: cdplayer-1.0-1
Package Prefix = usr/local
File doesn't match prefix (usr/local): /usr/doc/cdplayer-1.0-1
File not found: /usr/doc/cdplayer-1.0-1
Build failed.
#

In our example, the build proceeded normally until the time came to create the binary package file. At that point RPM detected the problem. The error message says it all: The prefix line in the spec file (/usr/local) was not present in the first part of the file's (/usr/doc/cdplayer-1.0-1) path. This stopped the build in its tracks.

The fact that every file in a relocatable package must be installed under the directory specified in the prefix line, raises some issues. For example, what about a program that reads a configuration file normally kept in /etc?  This question leads right into our next section.  

   
16.3.2 Relocatable Packages Must Contain Relocatable Software

While this section's title seems pretty obvious, it's not always easy to tell if a particular piece of software can be relocated. Let's take a look at the question raised at the end of the previous section. If a program has been written to read its configuration file from /etc, there are three possible approaches to making that program relocatable:

  1. Set the prefix to /etc and package everything under /etc.

  2. Package everything somewhere other than /etc and leave out the config file entirely.  

  3. Modify the program.

The first approach would certainly work from a purely technical standpoint, but not many people would be happy with a program that installed itself in /etc. So this approach isn't viable.

The second approach might be more appropriate, but it forces users to complete the install by having them create the config file themselves. If RPM's goal is to make software easier to install and remove, this is not a viable approach, either!

The final approach might be the best. Once the program is installed, when the rewritten software is first run, it could see that no configuration file existed in /etc, and create one.  However, even though this would work, when the time came to erase the package, the config file would be left behind. RPM had never installed it, so RPM couldn't get rid of it. There's also the fact that this approach is probably more labor intensive than most package builders would like.

None of these approaches are very appealing, are they? Some software just doesn't relocate very well. In general, any of the following things are warning signs that relocation is going to be a problem:

If these kinds of issues crop up, then making the software relocatable is going to be tough. And there's still one issue left to consider.

16.3.3 The Relocatable Software Is Referenced By Other Software

Even assuming the software is written so that it can be put in a relocatable package, there still might be a problem. And that problem centers not on the relocatable software itself, but on other programs that reference the relocatable software.

For example, there are times when a package needs to execute other programs. This might include backup software that needs to send mail, or a communications program that needs to compress files. If these underlying programs were relocatable, and not installed where other packages expect them, then they would be of little use.

Granted, this isn't a common problem, but it can happen. And for the package builder interested in building relocatable packages, it's an issue that needs to be explored. Unfortunately, this type of problem can be the hardest to find.

If, however, a software product has been found to be relocatable, the mechanics of actually building a relocatable package are pretty straightforward. Let's give it a try.   

   
16.4 Building a Relocatable Package

For this example, we'll use our tried-and-true cdplayer application. Let's start by reviewing the spec file for possible problems:

#
# Example spec file for cdplayer app...
#
Summary:A CD player app that rocks!
Name: cdplayer
...
%files
%doc README
/usr/local/bin/cdp
/usr/local/bin/cdplay
%doc /usr/local/man/man1/cdp.1
%config /etc/cdp-config

Everything looks all right, except for the %files list. There are files in /usr/local/bin, a man page in /usr/local/man/man1, and a config file in /etc. A prefix of /usr/local would work pretty well, except for that cdp-config file.

For the sake of this first build, we'll declare the config file unnecessary and remove it from the %files list. We'll then add a prefix tag line, setting the prefix to /usr/local. After these changes are made, let's try a build:


# rpm -ba cdplayer-1.0.spec

* Package: cdplayer
+ umask 022
+ echo Executing: %prep
Executing: %prep
+ cd /usr/src/redhat/BUILD
+ cd /usr/src/redhat/BUILD
+ rm -rf cdplayer-1.0
+ gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz
...
Binary Packaging: cdplayer-1.0-1
Package Prefix = usr/local
File doesn't match prefix (usr/local): /usr/doc/cdplayer-1.0-1
File not found: /usr/doc/cdplayer-1.0-1
Build failed.
#

The build proceeded normally up to the point of actually creating the binary package. The Package Prefix = usr/local line confirms that RPM picked up our prefix tag line. But the build stopped - and on a file called /usr/doc/cdplayer-1.0-1. But that file isn't even in the %files list. What's going on?

Take a closer look at the %files list. See the line that reads %doc README? In section [*] on page [*], we discussed how the %doc directive creates a directory under /usr/doc. That's the file that killed the build - the directory created by the %doc directive.

Let's temporarily remove that line from the %files list and try again:


# rpm -ba cdplayer-1.0.spec

* Package: cdplayer
+ umask 022
+ echo Executing: %prep
Executing: %prep
+ cd /usr/src/redhat/BUILD
+ cd /usr/src/redhat/BUILD
+ rm -rf cdplayer-1.0
+ gzip -dc /usr/src/redhat/SOURCES/cdplayer-1.0.tgz
...
Binary Packaging: cdplayer-1.0-1
Package Prefix = usr/local
Finding dependencies...
Requires (2): libc.so.5 libncurses.so.2.0
bin/cdp
bin/cdplay
man/man1/cdp.1
90 blocks
Generating signature: 0
Wrote: /usr/src/redhat/RPMS/i386/cdplayer-1.0-1.i386.rpm
+ umask 022
+ echo Executing: %clean
Executing: %clean
+ cd /usr/src/redhat/BUILD
+ cd cdplayer-1.0
+ exit 0
Source Packaging: cdplayer-1.0-1
cdplayer-1.0.spec
cdplayer-1.0.tgz
82 blocks
Generating signature: 0
Wrote: /usr/src/redhat/SRPMS/cdplayer-1.0-1.src.rpm
#

The build completed normally. Note how the files to be placed in the binary package file are listed, minus the prefix of /usr/local. Some of you might be wondering why the cdp.1 file didn't cause problems. After all, it had a %doc directive, too. The answer lies in the way the file was specified. Since the file was specified using an absolute path, and that path started with the prefix /usr/local, there was no problem. A more complete discussion of the %doc directive can be found in section [*] on page [*].

16.4.1 Tying Up the Loose Ends

In the course of building this package, we ran into two hitches:

  1. The config file cdp-config couldn't be installed in /etc.  

  2. The README file could not be packaged using the %doc directive.

Both of these issues are due to the fact that the files' paths do not start with the default prefix path /usr/local. Does this mean this package cannot be relocated? Possibly, but there are two options to consider. The first option is to review the prefix. In the case of our example, if we chose a prefix of /usr instead of /usr/local, the README file could be packaged using the %doc directive, since the default documentation directory is /usr/doc. Another approach would be to use the %docdir directive to define another documentation-holding directory somewhere along the prefix path.2

This approach wouldn't work for /etc/cdp-config, though. To package that file, we'd need to resort to more extreme measures. Basically, this approach would entail packaging the file in an acceptable directory (something under /usr/local) and using the %post post-install script to move the file to /etc. Pointing a symlink at the config file is another possibility.  

Of course, this approach has some problems. First, you'll need to write a %postun script to undo what the %post script does.3 A %verifyscript that made sure the files were in place would be nice, too. Second, because the file or symlink wasn't installed by RPM, there's no entry for it in the RPM database. This reduces the utility of RPM's -c and -d options when issuing queries. Finally, if you actually move files around using the %post script, the files you move will not verify properly, and when the package is erased, your users will get some disconcerting messages when RPM can't find the moved files to erase them. If you have to resort to these kinds of tricks, it's probably best to forget trying to make the package relocatable.  

   
16.4.2 Test-Driving a Relocatable Package

Looks like cdplayer is a poor candidate for being made relocatable. However, since we did get a hamstrung version to build successfully, we can use it to show how to test a relocatable package.

First, let's see if the binary package file's prefix has been recorded properly. We can do this by using the - -queryformat option to RPM's query mode:


# rpm -qp - -queryformat '%{DEFAULTPREFIX}
\n' cdplayer-1.0-1.i386.rpm
/usr/local
#

The DEFAULTPREFIX tag directs RPM to display the prefix used during the build. As we can see, it's /usr/local, just as we intended. The - -queryformat option is discussed in chapter [*], specifically, in section [*] on page [*].

So it looks like we have a relocatable package. Let's try a couple of installs and see if we really can install it in different locations. First, let's try a regular install with no prefix specified:


# rpm -Uvh cdplayer-1.0-1.i386.rpm

cdplayer ##################################################
#

That seemed to work well enough. Let's see if the files went where we intended:


# ls -al /usr/local/bin
total 558
-rwxr-xr-x 1 root root 40739 Oct 7 13:23 cdp*
lrwxrwxrwx 1 root root 18 Oct 7 13:40 cdplay -> /usr/local/bin/cdp*
...
# ls -al /usr/local/man/man1
total 9
-rwxr-xr-x 1 root root 4550 Oct 7 13:23 cdp.1*
...
#

Looks good. Let's erase the package and reinstall it with a different prefix:


# rpm -e cdplayer
# rpm -Uvh - -prefix /usr/foonly/blather cdplayer-1.0-1.i386.rpm

cdplayer ##################################################
#

(We should mention that directories foonly and blather didn't exist prior to installing cdplayer.)

RPM has another tag that can be used with the - -queryformat option. It's called
INSTALLPREFIX and using it displays the prefix under which a package was installed. Let's give it a try:


# rpm -q - -queryformat '%{INSTALLPREFIX}
\n' cdplayer
/usr/foonly/blather
#

As we can see, it displays the prefix we entered on the command line. Let's see if the files were installed as we directed:


# cd /usr/foonly/blather/
# ls -al
total 2
drwxr-xr-x 2 root root 1024 Oct 7 13:45 bin/
drwxr-xr-x 3 root root 1024 Oct 7 13:45 man/
#

So far, so good - the proper directories are there. Let's look at the man page first:


# cd /usr/foonly/blather/man/man1/
# ls -al
total 5
-rwxr-xr-x 1 root root 4550 Oct 7 13:23 cdp.1*
#

That looks ok. Now on to the files in bin:


# cd /usr/foonly/blather/bin
# ls -al
total 41
-rwxr-xr-x 1 root root 40739 Oct 7 13:23 cdp*
lrwxrwxrwx 1 root root 18 Oct 7 13:45 cdplay -> /usr/local/bin/cdp
#

Uh-oh. That cdplay symlink isn't right. What happened? If we look at cdplayer's makefile, we see the answer:

install: cdp cdp.1.Z
...
ln -s /usr/local/bin/cdp /usr/local/bin/cdplay

Ah, when the software is installed during RPM's build process, the make file sets up the symbolic link. Looking back at the %files list, we see cdplay listed. RPM blindly packaged the symlink, complete with its non-relocatable string. This is why we mentioned absolute symlinks as a prime example of non-relocatable software.

Fortunately, this problem isn't that difficult to fix. All we need to do is change the line in the makefile that creates the symlink from:


ln -s /usr/local/bin/cdp /usr/local/bin/cdplay

To:


ln -s ./cdp /usr/local/bin/cdplay

Now cdplay will always point to cdp, no matter where it's installed. When building relocatable packages, relative symlinks are your friend!

After rebuilding the package, let's see if our modifications have the desired effect. First, a normal install with the default prefix:


# rpm -Uvh - -nodeps cdplayer-1.0-1.i386.rpm

cdplayer ##################################################

# cd /usr/local/bin/
# ls -al cdplay
lrwxrwxrwx 1 root root 18 Oct 8 22:32 cdplay -> ./cdp*

Next, we'll try a second install using the - -prefix option (after we first delete the original package):


# rpm -e cdplayer
# rpm -Uvh - -nodeps - -prefix /a/dumb/prefix cdplayer-1.0-1.i386.rpm

cdplayer ##################################################

# cd /a/dumb/prefix/bin/
# ls -al cdplay
lrwxrwxrwx 1 root root 30 Oct 8 22:34 cdplay -> ./cdp*
#

As you can see, the trickiest part about building relocatable packages is making sure the software you're packaging is up to the task. Once that part of the job is done, the actual modifications are straightforward.

In the next chapter, we'll cover how packages can be built in non-standard directories, as well as how non-root users can build packages.   



Ralf S. Engelschall 2000-12-15
@ 1.1.1.1 log @Import book 'Maximum RPM' by Ed Bailey, version 1.0 @ text @@