Making Your Own RPM Packages






Making Your Own RPM Packages

While it's fairly easy to install software from source, it's not much more work to build an RPM package, especially if the original source code is well-written and in a traditional tarball. The extra work will make it much easier to track, update, and remove the software installed on your system.

How Do I Do That?

In order to build an RPM, you need to have the original source tarball plus a spec file, which provides most of the metadata for the RPM package and controls how the RPM is builtbut before you build any packages, you should customize your RPM environment.

Preparing to build RPMs

RPMs are digitally signed by the packager. Although this is an optional step, it indicates that the package is from a trusted source and provides a way of verifying that no one has tampered with it.

RPM signatures are generated using GNU Privacy Guard (gpg or gnupg), which can also be used to sign or encrypt email messages. If you have not created a gpg key, this is a great time to do so:

$ gpg --gen-key
gpg (GnuPG) 1.4.1; Copyright (C) 2005 Free Software Foundation, Inc.
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions. See the file COPYING for details.

gpg: directory \Q/home/chris/.gnupg' created
gpg: new configuration file \Q/home/chris/.gnupg/gpg.conf' created
gpg: WARNING: options in \Q/home/chris/.gnupg/gpg.conf' are not yet
  active during this run
gpg: keyring \Q/home/chris/.gnupg/secring.gpg' created
gpg: keyring \Q/home/chris/.gnupg/pubring.gpg' created
Please select what kind of key you want:
   (1) DSA and Elgamal (default)
   (2) DSA (sign only)
   (5) RSA (sign only)
Your selection? 1
DSA keypair will have 1024 bits.
ELG-E keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)
Requested keysize is 2048 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y

You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) <[email protected]>"

Real name: 
                     Chris Tyler
Email address: 
                     <[email protected]>
Comment: 
                     ENTER
You selected this USER-ID:
    "Chris Tyler <[email protected]>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
You need a Passphrase to protect your secret key.

Enter passphrase: 
                     seeecret
Repeat passphrase: 
                     seeecret

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
+++++++++++++++++++++++++++++++++++++++++++++.+++++++++++++++++++++++++.+++
+++++++.+++++++++++++++.+++++++++++++++++++++++++>++++++++++...............
............................<+++++...>+++++........................<.+++++.
++....+++++
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
+++++++++++++++++++++++++.+++++..+++++++++++++++..++++++++++.+++++.++++++++
++++++++++++..+++++.+++++++++++++++.++++++++++..+++++++++++++++...+++++.+++
.+++++>.+++++...............+++++^^^^
gpg: /home/chris/.gnupg/trustdb.gpg: trustdb created
gpg: key B2B16060 marked as ultimately trusted
public and secret key created and signed.

gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
pub   1024D/B2B16060 2005-11-07
      Key fingerprint = 6283 3FDE 833B D21A 209A  75D2 369E E05E B2B1 6060
uid                  Chris Tyler <[email protected]>
sub   2048g/2931B80E 2005-11-07

Your gpg keys will be created and stored in ~/.gnupg: the private key in ~/.gnupg/secring.gpg, and the public key in ~/.gnupg/pubring.gpg.

The second step in setting up your RPM environment is to create an ~/.rpmmacros file. This file contains your personal information and controls where RPMs will be built.

Here is a version of the ~/.rpmmacros file that is fully automatic; it will discover all of the information it needs from your account configuration and gpg setup. Type it into a text editor such as vi and save it as .rpmmacros in your home directory:

#
# ~/.rpmmacros file
#
# This gets all necessary information from environment variables and 
# system utilities. The first e-mail address on your gnupg keyring 
# should be your own.
#

%packager               %(finger -l $LOGNAME|sed -n "s/.*Name: //p")
%distribution           Fedora Core %(tr -dc [0-9] </etc/fedora-release)

# The vendor here is the same as the packager. Use a company or
# organization if appropriate.
%vendor                 %{packager}

%_home                  %(echo $HOME)
%_topdir                %{_home}/rpm
%_tmppath               %{_topdir}/tmp
%_builddir              %{_tmppath}

%_rpmtopdir             %{_topdir}/%{name}
%_sourcedir             %{_rpmtopdir}
%_specdir               %{_rpmtopdir}
%_rpmdir                %{_topdir}/RPMS
%_srcrpmdir             %{_topdir}/RPMS
%_rpmfilename           %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm

%_signature             gpg
%_gpg_path              %{_home}/.gnupg
%_gpgbin                /usr/bin/gpg
%_gpg_name              %(gpg --list-keys|sed -n "s/^uid *//p"|head -1)

You can also create this file by manually filling in the values you wish to use:

#
# ~/.rpmmacros file
#
# This gets all necessary information from environment variables and 
# system utilities. The first e-mail address on your gnupg keyring 
# should be your own.
#

%packager               Chris Tyler
%distribution           Fedora Core 6

# Use an organization or company in the next line if applicable
%vendor                 Chris Tyler

%_home                  /home/chris
%_topdir                /home/chris/rpm
%_tmppath               /home/chris/rpm/tmp
%_builddir              /home/chris/rpm/tmp

%_rpmtopdir             /home/chris/rpm/%{name}
%_sourcedir             %{_rpmtopdir}
%_specdir               %{_rpmtopdir}
%_rpmdir                /home/chris/rpm/RPMS
%_srcrpmdir             /home/chris/rpm/RPMS
%_rpmfilename           %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm

%_signature             gpg
%_gpg_path             /home/chris/.gnupg
%_gpgbin               /usr/bin/gpg
%_gpg_name             Chris Tyler <[email protected]>
               

To test that this file has been saved in the correct location and is being correctly interpreted by rpm, execute rpm --eval followed by the name of one of the macros:

$ rpm --eval "%_gpg_name"
Chris Tyler <[email protected]>
$ rpm --eval "%_srcrpmdir"
/home/chris/rpm/RPMS

Both versions of this file use the directory ~/rpm to hold packages being built. Within this directory, there will be:

  • A directory for each package being built, named after that package.

  • A directory named tmp, for temporary files created during the building process.

  • A directory named RPMS, to hold the final RPM packages.

You'll need to create these directories:

$ mkdir -p ~/rpm/RPMS ~/rpm/tmp
               

The fedora-rpmdevtools package provides the fedora-buildrpmtree command, which prepares a suitable directory structure within your home directory and creates a very basic .rpmmacros file. If you use this command, your RPMs will be built within the directory ~/rpmbuild.


Creating a spec file

The RPM building process is controlled by a spec file. Creating a good spec file is both a science and an art.

To start, create a new directory within ~/rpm to hold your source tarball and the spec file. In this example, I'm going to package up the game Critical Mass (also called critter), available from http://sourceforge.net/projects/criticalmass. I'll name the directory after the package:

$ mkdir ~/rpm/CriticalMass

I'll place the source tarball CriticalMass-1.0.0.tar.bz2 in this directory. The spec file will also be named after the package: CriticalMass.spec.

The first part of any spec file is called the preamble and contains the fields, or tags, outlined in Figure. Each tag is placed on a line by itself, followed by a colon and the value for that tag.

Basic preamble tags in a spec file
TagDescription
                                 Name

Name of the package.
                                 Version

Version of the software in the package (software version).
                                 Release

Release number of the package (package version).
                                 Group

The application group to which the software belongs. See /usr/share/doc/rpm-4.4.2/GROUPS for a list of possible values.
                                 URL

The software's home page on the Web.
                                 License

The license used for the software (such as GPL or Mozilla).
                                 Summary

A one-line summary of the package description.
                                 Requires

Capabilities needed by the software in order to be successfully installed. Many requirements are automatically determined, so this line is often not needed. Also include in this tag any special capabilities required by install and uninstall scripts (or triggers). If a package name is given as an argument, a version number can be provided, and a comparison can be given (such as gcc >= 4.0 or sendmail = 8.13.4).
                                 BuildRequires

Capabilities needed by the software in order to be successfully built, but not needed simply to install the RPM. For example, the gcc C compiler may be required to build the RPM package, but not to install it once it has been built.
                                 Provides

Capabilities provided by the package. Like Requires, most of the Provides will be determined automatically.
                                 BuildRoot

Specifies where the package should be installed during the package-building process. Many packages use %{_tmppath}/%{name}-root, which will create a package-specific directory within ~/rpm/tmp. It is strongly recommended that you do not use /.


This is the initial information for the Critical Mass spec file:

Name:           CriticalMass
Version:        1.0.0
Release:        1

Group:          Amusements/Games
Summary:        An arcade-style shoot-em-up game.
License:        GPL

Source0:        CriticalMass-1.0.0.tar.bz2

URL:            http://sourceforge.net/projects/criticalmass
BuildRoot:      %{_tmppath}/%{name}-root

One more tag must be defined %descriptionbut this tag does not take the name:value form. Instead, a description of the package follows on the lines after the tag:

%description
CriticalMass is an old-style arcade-style shoot-em-up game with 
                  modern graphics and sound.
               

The description text will be automatically wrapped and formatted to fit available space when it is displayed. To include preformatted text, leave a space at the start of each preformatted line.

After this initial information are seven sections, each identified by a section name:


%prep

Commands used to prepare the package for building.


%build

Commands used to build the package from the source (such as make).


%install

Commands to install the software (such as make install).


%check

Commands to test whether the software built correctly (make test). Optional; many packages do not include this section.


%clean

Commands to remove temporary files after a build.


%files

A list of the files that are to be included in the package.


%changelog

A history of package versions.

The %prep section might include all of the commands that would normally be used to prepare the package:

%prep
tar xvjf CriticalMass-1.0.0.tar.bz2
cd CriticalMass-1.0.0
               

However, since most open source packages use some simple variation of the same steps, Fedora's standard RPM setup includes a macro script to do this work for you. It's named %setup; to use it, specify it as the only step in the %prep section of the spec file:

%prep
%setup

Similarly, the %build stage can use the predefined %configure macro to run ./configure before make is run:

%build
%configure
make %{_smp_mflags}

The %{_smp_mflags} macro, used as an argument to make, will contain the options required to configure the build process for a symmetric multiprocessor system with multiple CPUs if the package is being built on an SMP system. (For many applications, this will make no difference).

The %install section installs the filesnot into the final destination directories, but into the appropriate directories under the BuildRoot. In this case, since we've defined the BuildRoot as ~rpm/tmp/CriticalMass-root/, files that would normally be installed into /usr/bin will be installed into ~rpm/tmp/CriticalMass-root/usr/bin.

There are two advantages to placing the files into the BuildRoot instead of the final file location: the Fedora system you're using won't get messed up, and since the only files that should be in the BuildRoot are those installed by this package, you can check to see that you can account for all of them.

The %install section often consists of an rm command to clear out the BuildRoot, followed by the %makeinstall macro to run make install with the appropriate options to install into BuildRoot instead of the root directory (most, but not all, modern open source packages will respect these options). The whole %install section looks like this:

%install
rm -rf %{buildroot}
%makeinstall

If you leave out the %check section (which is optional), the next section is %cleancommands to clean up the BuildRoot. This is usually the same rm command that was used in the %install section:

%clean
rm -rf %{buildroot}

At this point, the whole spec file looks like this:

Name:        CriticalMass
Version:     1.0.0
Release:     1

Group:       Amusements/Games
Summary:     An arcade-style shoot-em-up game.
License:     GPL

Source0:     CriticalMass-1.0.0.tar.bz2

URL:         http://sourceforge.net/projects/criticalmass
BuildRoot:   %{_tmppath}/%{name}-root

%description
CriticalMass is an old-style arcade-style shoot-em-up game with 
                  modern graphics and sound.

%prep
%setup

%build
%configure
make %{_smp_mflags}

%install
rm -rf %{buildroot}
%makeinstall

%clean
rm -rf %{buildroot}

This file is saved in ~/rpm/CriticalMass/CriticalMass.spec. Note that the %prep, %build, %install, and %clean sections are pretty generic and could be used with many different packages.

The next section required is a list of files to be included in the package. The easy way to prepare this list is to have rpmbuildthe RPM package-building toolbuild the package and install it into the BuildRoot, and then see what's there:

$ cd ~/rpm/CriticalMass
$ ls
CriticalMass-1.0.0.tar.bz2  CriticalMass.spec
$ rpmbuild -bi CriticalMass.spec
Executing(%prep): /bin/sh -e /home/chris/rpm/tmp/rpm-tmp.54511
+ umask 022
+ cd /home/chris/rpm/tmp
+ LANG=C
...(Lines snipped)...
Checking for unpackaged file(s): /usr/lib/rpm/check-files /home/chris/rpm/tmp/CriticalMass-root
error: Installed (but unpackaged) file(s) found:
   /usr/bin/Packer
   /usr/bin/critter
   /usr/share/Critical_Mass/lg-criti.xm
   /usr/share/Critical_Mass/resource.dat
   /usr/share/man/man6/critter.6.gz

RPM build errors:
    Installed (but unpackaged) file(s) found:
   /usr/bin/Packer
   /usr/bin/critter
   /usr/share/Critical_Mass/lg-criti.xm
   /usr/share/Critical_Mass/resource.dat
   /usr/share/man/man6/critter.6.gz

If your build fails because you need additional software, you must find that software and add it to a BuildRequires line in the spec file.


The -bi argument to rpmbuild instructs it to build up to the end of the %install stage. You can see that rpmbuild has detected files in BuildRoot that are not included in the package. To see the actual contents of the BuildRoot, you can change to the ~/rpm/CriticalMass directory and look around:

$ cd ~/rpm/tmp/CriticalMass-root
$ find
.
./usr
./usr/bin
./usr/bin/Packer
./usr/bin/critter
./usr/lib
./usr/lib/debug
./usr/lib/debug/usr
./usr/lib/debug/usr/bin
./usr/lib/debug/usr/bin/critter.debug
./usr/lib/debug/usr/bin/Packer.debug
./usr/share
./usr/share/man
./usr/share/man/man6
./usr/share/man/man6/critter.6.gz
./usr/share/Critical_Mass
./usr/share/Critical_Mass/resource.dat
./usr/share/Critical_Mass/lg-criti.xm
./usr/src
./usr/src/debug

The find command recursively lists all of the files found in the current directory.

From this list of files, you can build the %files section of the spec file. You can safely ignore the files in /usr/lib/debug and /usr/src since the RPM system will package these up into a separate debug RPM package automatically.

Among these files, there are some binaries:

./usr/bin/Packer
./usr/bin/critter

There is also a manpage:

./usr/share/man/man6/critter.6.gz

plus a data directory and some datafiles:

./usr/share/Critical_Mass
./usr/share/Critical_Mass/resource.dat
./usr/share/Critical_Mass/lg-criti.xm

The /usr/share/CriticalMass directory belongs to the package and should be removed when the package is removed. To configure this, you must list only the directory in the %files section of the spec file; the contents of the directory will automatically be included.

Other directories, such as /usr/bin and /usr/share/man/man6, also contain files belonging to other packages, so those directories must not be included in the %files list; only the individual files in those directories should be included.

Because the RPM package is being built by a regular useryou or meand our accounts may not exist on the target machine, you must reassign the ownership (and possibly the permissions) of the files using the %defattr directive. %defattr accepts four arguments: the default permission for files, the owner, the group, and the default permission for directories. Use a hyphen for permissions to signify that the existing file permissions should be left untouched:

%defattr(-, root, root, -)

To set specific attributes for a specific file, use %attr with three arguments (permission, user, group):

%defattr(0511, root, nogroup) foofile
               

In addition to files in the BuildRoot, you should also identify files in the top-level directory of the tarball that should be included in the file as documentation; this is done using the %doc directive. When the package is installed, these files will be placed in /usr/share/doc/ <packagename-version>. Good candidates for documentation files include README, TODO, BUGS, INSTALL, COPYING, and any other notes the program author has provided. In the case of the CriticalMass software, only the files COPYING and TODO fit into this category:

%doc COPYING TODO

In a similar way, the %config directive specifies configuration files that are included in the RPM:

%config /etc/master.conf
%config(noreplace) /etc/master.conf

When an RPM update is performed, a file marked as %config is replaced with the new version, but the old version is saved as <filename>.rpmsave. Files marked as %config(noreplace) are not replaced; the new version of the config file is instead installed as <filename>.rpmnew.

In the case of CriticalMass, there are no configuration files installed by the RPM.


The whole %files section looks like this:

%files
%doc COPYING TODO
./usr/bin/Packer
./usr/bin/critter
./usr/share/man/man6/critter.6.gz
./usr/share/Critical_Mass

You can simplify this a bit by using ambiguous pathnames and macros:

%files
%doc COPYING TODO
%{_bindir}/*
%{_datadir}/Critical_Mass
%{_mandir}/man?/*

Finally, the %changelog section contains entries describing the changes that have been made to the RPM spec file (and, if desired, to the underlying software as well). The entries are placed in reverse chronological ordernewest firstand each entry takes the form:

* WWW MMM DD YYYY email version
- point form note
- another point
               

with the meaning:


WWW MMM DD YYYY

The date, such as Sat Jan 1 2006.


email

The name and email address of the person who made the change, such as Chris Tyler <[email protected]>


version

The version number in which the change was made (optional).

For example:

%changelog
* Mon Nov 7 2005 Chris Tyler <[email protected]> 1.0.0-2
- Improved summary

* Sat Nov 5 2005 Chris Tyler <[email protected]>
- Initial RPM package.
               

Putting all of this together, the final spec file looks like this (note that I've incremented the release number to be consistent with the information in the %changelog section):

Name:        CriticalMass
Version:     1.0.0
Release:     2

Group:       Amusements/Games
Summary:     An arcade-style shoot-em-up game.
License:     GPL

Source0:     CriticalMass-1.0.0.tar.bz2

URL:         http://sourceforge.net/projects/criticalmass
BuildRoot:     %{_tmppath}/%{name}-root

%description
CriticalMass is an old-style arcade-style shoot-em-up game with 
                  modern graphics and sound.

%prep
%setup -q

%build
%configure
make %{_smp_mflags}

%install
rm -rf %{buildroot}
%makeinstall

%clean
rm -rf %{buildroot}

%files
%defattr(-, root, root)
%doc COPYING TODO

                  %{_bindir}/*
                  %{_datadir}/Critical_Mass
                  %{_mandir}/man?/*

%changelog
* Mon Nov 7 2005 Chris Tyler <[email protected]> 1.0.0-2
                  - Improved summary

                  * Sat Nov 5 2005 Chris Tyler <[email protected]>
                  - Initial RPM package.
               

To build the final RPM package, use buildrpm with the -ba option (build all):

$ cd ~/rpm/CriticalMass
$ rpmbuild -ba CriticalMass.spec
Executing(%prep): /bin/sh -e /home/chris/rpm/tmp/rpm-tmp.61308
+ umask 022
+ cd /home/chris/rpm/tmp
+ LANG=C
+ export LANG
...(Lines snipped)...
Checking for unpackaged file(s): /usr/lib/rpm/check-files /home/chris/rpm/tmp/CriticalMass-root
Wrote: /home/chris/rpm/RPMS/CriticalMass-1.0.0-2.src.rpm
Wrote: /home/chris/rpm/RPMS/CriticalMass-1.0.0-2.i386.rpm
Wrote: /home/chris/rpm/RPMS/CriticalMass-debuginfo-1.0.0-2.i386.rpm
Executing(%clean): /bin/sh -e /home/chris/rpm/tmp/rpm-tmp.76425
+ umask 022
+ cd /home/chris/rpm/tmp
+ cd CriticalMass-1.0.0
+ rm -rf /home/chris/rpm/tmp/CriticalMass-root
+ exit 0

You'll find that rpmbuild created three RPM packages and placed them in ~/rpm/RPMS/:


CriticalMass-1.0.0-2.i386.rpm

The binary RPM, ready to be installed and used.


CriticalMass-debuginfo-1.0.0-2.i386.rpm

Debugging info (from the /usr/lib/debug directory mentioned earlier). This package is rarely used, except by developers.


CriticalMass-1.0.0-2.src.rpm

A source RPM, which contains the source tarball and spec file. You can use this source RPM to easily generate a new binary RPM for a different type of system (see Lab 5.8, "Rebuilding an RPM Package for a Different Architecture").

The binary RPMthe most useful package, if you just want to play the gamecan be installed like any other RPM package:

# rpm -i CriticalMass-1.0.0-2.i386.rpm

You can also query it like any other package:

# rpm -qi CriticalMass
Name        : CriticalMass                 Relocations: (not relocatable)
Version     : 1.0.0                            Vendor: Chris Tyler
Release     : 2                             Build Date: Mon 07 Nov 2005 11:59:11 PM EST
Install Date: Tue 08 Nov 2005 12:07:00 AM EST      Build Host:bluesky.fedorabook.com
Group       : Amusements/Games              Source RPM: CriticalMass-1.0.0-2.src.rpm
Size        : 4474014                          License: GPL
Signature   : (none)
Packager    : Chris Tyler
URL         : http://sourceforge.net/projects/criticalmass
Summary     : An arcade-style shoot-em-up game.
Description :
CriticalMass is an old-style arcade-style shoot-em-up game with
modern graphics and sound.
# rpm -ql CriticalMass
/usr/bin/Packer
/usr/bin/critter
/usr/share/Critical_Mass
/usr/share/Critical_Mass/lg-criti.xm
/usr/share/Critical_Mass/resource.dat
/usr/share/doc/CriticalMass-1.0.0
/usr/share/doc/CriticalMass-1.0.0/COPYING
/usr/share/doc/CriticalMass-1.0.0/TODO
/usr/share/man/man6/critter.6.gz

And, of course, you can remove it easily:

# rpm -e CriticalMass

When you are certain that your RPM package is in good shape, you can digitally sign it:

$ rpm --addsign CriticalMass-1.0.0-2.i386.rpm
Enter pass phrase: 
                     seeecret
Pass phrase is good.
CriticalMass-1.0.0-2.i386.rpm:

How Does It Work?

The default macro definitions for the RPM system are merged from several files when either rpm or rpmbuild starts:


/usr/lib/rpm/macros

Standard definitions distributed with the RPM software.


/etc/rpm/macros

Site-specific macros. Definitions that are local to your system and that should apply to all users should be placed here.


~/.rpmmacros

Per-user configuration information.

rpmbuild uses the spec file to create a script. This script contains an expansion of all of the macros (such as %configure and %makeinstall) used in the spec file and is executed to prepare the RPM for packaging. (If rpmbuild is aborted or encounters a serious error, you will find the script in ~/rpm/tmp/). This script, in turn, references scripts found in /usr/lib/rpm to perform some of the processing involved in building a package.

When packages are built by the root user, the default RPM directories are used:


/usr/src/redhat/BUILD

Temporary build files


/usr/src/redhat/RPMS

Binary and debug RPMs that have been built


/usr/src/redhat/SOURCES

Source tarballs (as well as patches, RPM icons, and related files)


/usr/src/redhat/SPECS

Spec files


/usr/src/redhat/SRPMS

Source RPMs that have been built

Since these directories are writable only by root, and it is not recommended that RPMs be built by the root user, it's best to use a set of directories within your home directory.

What About...

...creating a desktop menu entry for a packaged program?

To create an entry in the menu, you will need to create a .desktop file in /usr/share/applications and (ideally) an icon in /usr/share/icons.

In the case of Critical Mass, there is an icon available in the top level of the tarball, so it can be fairly easily copied over to /usr/share/icons in the %install section of the spec file:

mkdir -p %{buildroot}%{_datadir}/icons
install -m 744 critter.png %{buildroot}%{_datadir}/icons/critter.png

Creating the .desktop file is almost as easy. Here are the contents of a .desktop file for Critical Mass:

mkdir -p %{buildroot}%{_datadir}/applications

echo "[Desktop Entry]
Name=Critical Mass
Comment=Shoot-em-up Game
Categories=Application;Game
Encoding=UTF-8
Exec=critter
Icon=critter.png
StartupNotify=true
Terminal=False
Type=Application" > %{buildroot}%{_datadir}/applications/CriticalMass.desktop

The .desktop file identifies all of the information necessary to create an additional entry in the desktop menu (whether KDE or GNOME):


Name

The name of the menu entry


Comment

The comment displayed as a tool tip message if you hover over the menu entry with the mouse pointer


Categories

The menu categories under which this entry will appear


Encoding

The character encoding used for this entry


Exec

The name of the command to be executed when this menu entry is selected


Icon

The name of the icon file


StartupNotify

Whether this icon supports the xdg startup notification protocol, which is used to manage a visual indication that the application is in the process of starting up


Terminal

Whether the application should be run in an terminal window (for nongraphical programs)


Type

Indicates that the program is a standalone application

The extended %install section looks like this:

%install
rm -rf %{buildroot}
%makeinstall

mkdir -p %{buildroot}%{_datadir}/icons
install -m 744 
                     critter.png %{buildroot}%{_datadir}/icons/
                     critter.png

mkdir -p %{buildroot}%{_datadir}/applications
echo "[Desktop Entry]
Name=
                     Critical Mass
Comment=
                     Shoot-em-up Game
Categories=Application;
                     Game
Encoding=UTF-8
Exec
                     =critter
Icon
                     =critter.png
StartupNotify=
                     true
Terminal=
                     False
Type=Application" > %{buildroot}%{_datadir}/applications/
                     CriticalMass.desktop

It is also necessary to modify the %files section to include the icon and .desktop file:

%files
%defattr(-, root, root)
%doc COPYING TODO

%{_bindir}/*
%{_datadir}/Critical_Mass
%{_mandir}/man?/*
%{_datadir}/applications/*
%{_datadir}/icons/*

...running a script when a package is installed or removed?

This can be done by specifying a %pre, %post, %preun, or %postun section. The difference between these sections is in when they designate the script to run: before installation (%pre), after installation (%post), before removal (%preun), or after removal (%postun).

As a simple example, if your script contains shared object libraries ( .so files), you should run ldconfig after installation and after removal:

%post 
/sbin/ldconfig

%postun
/bin/ldconfig

In this case, you should add a Requires tag to the preamble:

Requires: /sbin/ldconfig

...including an icon to identify the package?

A package icon can be included; graphical installation tools can pick up this icon and display it instead of a generic package icon. Place the icon in the same directory as the tarball, and create an Icon tag in the preamble:

Icon: CriticalMass.xpm

The icon should be in XPM format. You can use convert to make an XPM file from a file in another format:

$ convert critter.png critter.xpm

...viewing the source code and the spec file for an existing package?

This is an excellent way to learn about writing advanced spec files. You don't even need root privileges to open and view the files!

After downloading the source RPM for a package (.src.rpm file), install it in the normal way:

$ rpm -ivh ImageMagick-6.2.2.0-2.src.rpm
1:ImageMagick            ########################################### [100%]

The files will be installed into ~/rpm/ namein this case, ~/rpm/ImageMagick:

$ ls ~/rpm/ImageMagick
ImageMagick-5.5.6-mask.patch
ImageMagick-6.2.0-compress.patch
ImageMagick-6.2.1-fixed.patch
ImageMagick-6.2.1-hp2xx.patch
ImageMagick-6.2.1-local_doc.patch
ImageMagick-6.2.1-pkgconfig.patch
ImageMagick-6.2.2-0.tar.bz2
ImageMagick.spec
magick_small.png

Where Can I Learn More?



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows