Rpm tips
From blag.wiki.aktivix.org
This is based on some notes written for another project, but much of it is relevant to BLAG.
Contents |
Better .spec files
There are some pretty grody spec files lurking in (project). Here are some tips on making better ones.
Group:
The Group: is used to organize packages during a Red Hat install, so is fairly meaningless to (project), but unfortunately compulsory. The official list of groups is in /usr/share/doc/rpm-*/GROUPS.
Copyright: / License:
The Copyright: field is obsolescent: use License: instead. If you are putting documentation in the system default directory (as recommended below), you might want to say something like this.
License: Proprietary: see %_docdir/%name-%version/copyright
(Yes, Virginia, "obsolescent" is a word. It means something like "is marked to become obsolete, but isn't yet".)
_builddir
rpmbuild starts by changing its working directory to _builddir. By default, this is /usr/src/redhat/BUILD, which is almost certainly not what you want. (In fact, I can't think offhand of any situation where this default behaviour would be useful, but never mind.) Almost certainly, you want to override _builddir like this:
%define _builddir ./
Please note the leading underscore! Also, the trailing slash isn't needed, but the single period gets visually lost without it.
_rpmdir and _rpmfilename
rpmbuild drops any RPM files it builds into _rpmdir, using the name _rpmfilename. The default for _rpmdir is /usr/src/redhat/RPMS, which is almost certainly not what you want. The default for _rpmfilename is %{ARCH}/%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}.rpm which is perfect except for the leading %{ARCH} directory.
What we generally want is for the RPM file to end up in the current directory (where the Makefile invoking rpmbuild lives). You can achieve that like this:
%define _rpmdir ./
%define _rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm
Again, watch out for the leading underscores!
BuildRoot
You must define a BuildRoot, either on the rpmbuild command line or, preferably, in the .spec file. The canonical, if ungainly, definition looks like this (but note that this is not a default: you still need to specify it). It will set the BuildRoot to something like /var/tmp/libdb3-3.2.9-17-root.
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
The BuildRoot is the "staging area" where rpmbuild constructs a portion of a complete file system tree. For example, if your package includes the file /usr/lib/quux/foo.txt then it needs to be copied (at some stage) to <BuildRoot>/usr/lib/quux/foo.txt. The value of BuildRoot is available to scripts run by rpmbuild in the environment variable $RPM_BUILD_ROOT.
Many of the .spec files floating around (project) use a BuildRoot under the current directory: usually they define BuildRoot: package. This is not an unreasonable thing to do, although it does have a couple of drawbacks: first, tmp directories are likely to be quicker (especially if the current directory is across NFS); secondly, it clutters up the source tree even further. (The second objection is fairly minor, since we already build in the source tree, cluttering it up with .o files and executables.) The motivation for using package is that you don't have to do anything clever to tell the Makefile where the BuildRoot is: it's in package.
%prep %build %install %clean
These four are the scripts that are supposed to be used to construct (and destroy!) the BuildRoot from sources. We don't make much use of them in (project), since the BuildRoot is generally constructed by the Makefile that then invokes rpmbuild. However, it is worth considering if you can use the %install script to generate the BuildRoot tree instead.
%pre %post %preun %postun
These scripts are executed by rpm before installation, after installation, before uninstallation, and after uninstallation respectively. I have just a couple of things to say about them. First, they are always executed with /bin/sh -e. I have seen a number of .spec/tt> files with <tt>#! /bin/sh at the top of their script sections. This doesn't do any harm (it's just a shell comment), but it doesn't do any good either, and someday somebody's going to be very disappointed when they change it to #! /usr/bin/perl and the script is still run by /bin/sh! So don't bother including a #!= line. Note also the -e, flag, which means that the script will exit if any command fails, where "fails" means "exits non-zero". (This does not apply to commands used for flow control: the conditions of if statements and while loops, and the left hand side of || and &&.) This means that you don't have to do lots of error checking in the script itself. If, for some reason, you want to run a command and don't care if it fails (perhaps it's something sloppily written that doesn't exit with zero when it should), the canonical way to do it is command || true.
Also, these scripts are always invoked with an argument $1 which holds the number of instances of this package which will be installed when the current rpm command has completed. This is a bit convoluted, but basically it means that you should wrap your %pre and %post scripts in
if [ "$1" = 1 ]; then # this is the first install ... fi
and your %preun and %postun scripts in
if [ "$1" = 0 ]; then # this is the last uninstall ... fi
It goes without saying that the uninstall scripts should tidy up anything done by install scripts. For example, if you create a new user with useradd in the %pre script, you should remove it with userdel in the %postun script.
%files:
file list
The %files section of the .spec file is where we tell rpmbuild which files go into the RPM. The files will come from the "staging area", which was defined by BuildRoot and constructed by %install. There are several things to say about %files.
First, it is never necessary to list parent directories! If you need the file /foo/bar/baz/qux in your RPM, the %files section should contain /foo/bar/baz/qux. It does not need to contain %dir /foo, %dir /foo/bar, and so forth, and should not do so. (We'll talk more about %dir in a moment.)
Many of the .spec files I have seen in (project) start their %files section something like this:
%files %dir "/usr" %dir "/usr/lib" %dir "/usr/lib/quux" %dir "/usr/lib/quux/bsc"
Not only is this unnecessarily verbose, it is also wrong! This is claiming that the RPM we are building "owns" the /usr directory, which obviously it doesn't. (Fortunately, rpm doesn't totally believe the claim. However, there is at least one (project) RPM that claims ownership of /var and makes it group-writable! This causes fits in pam, which complains about the wrong permissions on /var for evermore.)
Speaking of verbosity, all those quote marks are redundant. You only need quotes around the filename if it contains spaces or glob characters.
So the first rule is: list only the files you care about, let rpm handle creation of parent directories. But we can be much more succinct than this. If you list a directory in the %files section (without the %dir attribute), rpmbuild will pick up everything under that directory. By "everything", we mean everything that is in the BuildRoot staging area. For example, if your package consists of the files /usr/lib/quux/foo/bar and /usr/lib/quux/baz/qux, you could define this with
%files /usr/lib/quux
This does mean that your package is claiming to own /usr/lib/quux, which shouldn't be a problem. If you have many files under a system directory, though, we need a slightly different approach. Suppose your package includes the files /bin/foobar and /bin/bazqux; we don't want to claim ownership of /bin. But we can achieve the correct result using a wildcard, which will be expanded according to whatever exists in the BuildRoot:
%files /bin/*
I mentioned the %dir modifier earlier. This causes rpmbuild to include the directory so marked, but not its contents; it's analogous to the -d flag to the ls command (think of the difference between ls -l /bin and ls -ld /bin). You don't need to use %dir just to tell rpmbuild that the file mentioned is a directory: it can work that out for itself. In fact, I can't think of a very good use for %dir; try to excise it from any .spec files you are working with. (It might come in handy if you need a directory which has a different owner from its contents, but then it's hard to think of a good reason why you'd want that!)
file attributes
This brings us on to file attributes, in other words, the owner, group, and file permissions that rpm will set on installed files. The first thing to know about file attributes is that you can save yourself a lot of effort by using the %defattr macro. I recommend that you start every %files section like this:
%files %defattr(-, root, root, -) ...
This tells rpmbuild that every file should have owner root and group root, with permissions for files (the first hyphen) and directories (the second hyphen) copied from the permissions they already have in the BuildRoot. Conceivably, it might make sense to specify a different owner or group, for example %defattr(-, quux, quux, -). You'd need to ensure that this owner and group exist at the time the .rpm is installed, perhaps by creating them in the %pre script. Less likely is that you'd want to set default permissions: something like %defattr(0644, root, root, 0755) looks promising, but will give 0644 permissions to every file, even if it's supposed to be executable! Much better to make sure that the files in the BuildRoot have the right permissions in the first place.
Having defined default attributes, you only need to use the %attr modifier for files that should differ from the default. For example, the .spec file for quux-base creates a few directories under /var/lib/quux which need to have different owners...
%files %defattr(-, quux, quux, -) %attr(-, admin, admin) /var/lib/quux/admin %attr(-, backup, backup) /var/lib/quux/backup ...
Note that "-" for the first argument to the %attr modifier means the same as it does to %defattr: copy the file permissions from the file in BuildRoot.
An important point is that every file in the .rpm should have its owner and group set either by %defattr or by %attr. Since you're running rpmbuild as yourself, not root, if you don't specify the owner for a file, it will end up going into the .rpm owned by you (e.g. ken). This is not the end of the world: rpm will use the root user if ken doesn't exist on the system where it is installing the .rpm, but it complains as it does so.
miscellaneous
Another modifier you might want to use is %config. This is intended for configuration files that the end user may need to edit. By marking them with the %config modifier, rpm will do extra work to avoid overwriting user changes when a package is upgraded, either saving the old file with a .rpmsave extension, or installing the new file with a .rpmnew extension. (Offhand, I'm not sure exactly which of these options it chooses when, but you get the idea.) Assuming we're doing things in a Unixy sort of way, it's a safe bet that any files marked %config are text files somewhere under /etc.
Finally, we'll just briefly mention ad hoc documentation files, such as README files or copyright notices. Such files exist in the source tree, but are not copied anywhere by make install. (We're not talking here about the actual package documentation, be it man pages, info files, or whatever, which are installed when you type make install.) The recommended way to handle these is not to install them in the BuildRoot. Rather, you should leave them in the %_builddir, normally the top level directory where you're running make or rpmbuild, and name them in the %files section with the %doc modifier. Then rpmbuild will arrange to install them in the system default directory for such things, normally something like /usr/share/doc/quux-base-1.2.3/.
%files ... %doc README
More resources
There are a couple of books about RPM available online.
Maximum RPM by Edward Bailey. Although there is some useful stuff in here, it is very old - dating back to rpm-2 - before rpmbuild was a separate command - and so it cannot be considered authoritative. It does, however, feature an index!
RPM Guide by Eric Foster-Johnson. This is much more up-to-date than Maximum RPM, and seems to describe rpm-4 (the current version) reasonably accurately. Disappointingly, irritatingly, infuriatingly, there is no index!
As well as the man pages for rpm and rpmbuild, there is a surprising amount of useful information in /usr/share/doc/rpm-<ver>. Do read it.
Tim Goodwin. Last revised 2009-09-11.