Here are several things not necessary to build a first RPM. You should become familiar with the preceding stuff, then come back here and read this section.
As of the 7.1 version of Mandrake Linux, we now use the Menu System written by Debian.
This excellent software provides a Window-Manager independant way to register an application to the system. Most of the time, this registration will become effective in the Start button or alike of your favourite Window Manager.
It works like this: each package includes a file in directory /usr/lib/menu/. Most of the time the filename will be the name of the package. In this file, it will have one line per menu-entry, like this:
?package(xbase):command="/usr/bin/X11/xedit" icon="xbase.xpm" \ needs="X11" section="Applications/Editors" title="Xedit" \ longtitle="The basic editor for the X Window system" |
It is suggested that you put the menu file within the spec, instead of creating a separate source for it. To do that you can :
cat << EOF > $RPM_BUILD_ROOT/%{_menudir}/myprogram ... menu stuff here ... using ONE line only per menu entry ... EOF |
command: the command to launch ; it can be absolute or not (if so, it will use the PATH of the Window-Manager)
icon: the xpm icon to be used by the Window Manager to refer to the application ; mainly used in the start button alike, but also can be used to dock (include in a toolbar/panel) the app ; therefore, you will not give an absolute path, and provide three classes of icons in your rpm package: a 16x16, a 32x32, and a 48x48, respectively in /usr/share/icons/mini, /usr/share/icons, and /usr/share/icons/large with the same name. There are macros for these three directories: %{_miconsdir} ; %{_iconsdir} ; %{_liconsdir}.
needs: there are three classes of needs:
x11 indicates an application that will be launched under a private window provided by the X-Window system;
text indicates an application needing a terminal to be run (for example, this is the case for "telnet", "gnuplot", etc);
the last one is the name of a Window Manager, in order to prevent the other WM's from proposing this app; for example you will like that "iceconf" appear in the menu only under icewm, and you will use needs="icewm" for this one.
section: the section where the application will go ; it has to be normalized in order to be efficient, therefore see Appendix D for the complete menu structure
title: the name under which the application will be registered in the start button alike of the Window Managers
longtitle: used by the Window Managers which support tooltips to display more information
Whenever root runs update-menus, it will check all new or changed menufiles in /etc/menu and /usr/lib/menu, and run the installation scripts Window Managers should provide in /etc/menu-methods on them. Use our special macros for that:
%post %{update_menus} %postun %{clean_menus} |
You will have to provide the three icons described earlier. One popular way of doing it is to declare the three icons, bzipped, with Source: tags, and install them in appropriate directories in the %install section:
Source10: %{name}.16.xpm.bz2 Source11: %{name}.32.xpm.bz2 Source12: %{name}.48.xpm.bz2 # .. %install # .. install -d $RPM_BUILD_ROOT/%{_miconsdir} install -d $RPM_BUILD_ROOT/%{_liconsdir} bzcat %{SOURCE10} > $RPM_BUILD_ROOT/%{_miconsdir}/%{name}.xpm bzcat %{SOURCE11} > $RPM_BUILD_ROOT/%{_iconsdir}/%{name}.xpm bzcat %{SOURCE12} > $RPM_BUILD_ROOT/%{_liconsdir}/%{name}.xpm |
In order to enjoy better upgrades, it is important to keep old major library versions in the system so that programs which use them still work.
Libraries in /usr/lib, /lib and /usr/X11R6/lib must be separately packaged, in library-only package, named with the name of the main library concatenated with the major of the library. These packages should not contain any binaries, they should be separately packaged. The goal is to be able to install libfoo1 and libfoo2 on the same system.
In the first time it is fundamental that the sources rpm keep the same name without any major number, so that the CVS repository only contains one branch for each package.
When the distribution must contain two versions of the same library, for example qt1 and qt2, sources rpms will be separated so that we can include the two of them in the distribution. And since then the two branches will be independently maintained.
Here's a generic example; the following case happens when the library comes with binaries, config files, or anything else that won't fit in the main library package (where only libraries go) nor in the devel package (where headers and devel libraries go (e.g. *.so and *.a)).
Source Package: foo-2.3.4-4mdk.src.rpm Binary Packages: foo-2.3.4-4mdk.arch.rpm libfoo2-2.3.4-4mdk.arch.rpm libfoo2-devel-2.3.4-4mdk.arch.rpm |
In the opposite case to this example, when replacing an existing package, for example foobar, with a new one called libfoobar3, in the case foobar will not exist any more because there are no binaries, and no config files, libfoobar3 must have an obsolete tag on foobar. This is often the case for "simpler" libraries.
Source Package: foobar-3.1-7mdk.src.rpm Binary Packages: libfoobar3-3.1-7mdk.arch.rpm libfoobar3-devel-3.1-7mdk.arch.rpm |
Ok we described the default policy for library packages, however some special cases can happen and must be handled using the brain. Remember to always check the soname of the libraries ("objdump -x libfoo.so.1 | grep SONAME"), because some sonames include library version number, for example libfoo-1.2.so.4, in this case the package should be named libfoo1.2_4-1.2.4-3mdk.
Packages ending with a number should be handle with a "_" before the major, for example libfoo23_4-1.2-3mdk (in this case the soname would be libfoo23.so.4).
It is not necessary to split each library in separate packages, if a package contains several library, the name would be build with the main library of the package. If there are problems keeping libraries in the same package, the package should be splitted.
It is important to supply some Provides. First reason: concerning the devel package, most of the time the client app will only want to put libfoo-devel in the BuildRequires, without any version information. Second reason, for compatibility with other RPM-based systems, it's important to provide also original names, qualifying versions. Please refer to following skeleton examples. It's important to understand that putting a Provides without the version information, makes it impossible for later client RPM's to put a version information on dependencies, e.g. "Provides: foo-devel" is NOT enough, please use "Provides: foo-devel = 1.2.4-3mdk".
Here's an example of specfile for a package with no binary and config files, so only lib* binary packages are needed.
%define name gtkmm %define version 1.2.4 %define release 1mdk %define lib_name_orig libgtkmm %define lib_major 1.2 %define lib_name %{lib_name_orig}%{lib_major} Name: %{name} Summary: C++ interface for popular GUI library gtk+ #(!) summary for SRPM only Version: %{version} Release: %{release} %description #Full and generic description of the whole package. (this will be the SRPM #description only) #main package (contains *.so.[major].* only) %package -n %{lib_name} Summary: Main library for gtkmm #(!) summary for main lib RPM only Group: System/Libraries Provides: %{name} = %{version}-%{release} %description -n %{lib_name} This package contains the library needed to run programs dynamically linked with gtkmm. %package -n %{lib_name}-devel Summary: Headers for developing programs that will use Gtk-- Group: Development/GNOME and GTK+ Requires: %{lib_name} = %{version} Provides: %{lib_name_orig}-devel = %{version}-%{release} #(!) **MANDATORY** Provides: %{name}-devel = %{version}-%{release} #(!) **MANDATORY** %description -n %{lib_name}-devel This package contains the headers that programmers will need to develop applications which will use Gtk--, the C++ interface to the GTK+ (the Gimp ToolKit) GUI library. %post -n %{lib_name} -p /sbin/ldconfig %postun -n %{lib_name} -p /sbin/ldconfig %files -n %{lib_name} # .. %{_libdir}/*.so.* %files -n %{lib_name}-devel # .. %{_bindir}/gtkmm-config %{_includedir}/*.h %{_libdir}/*.so |
Here are some more tags that you might see, some that you will see often, some not.
Prefix: This is for package relocation. It should NOT be used if your package is not relocatable. Don't be fooled! Prefix: does not define where a package should go. To specify you want the prefix to be /usr, all you need to do is put Prefix: /usr, and rpm will verify that every files in the packages does start with /usr; if this verification succeeds, the package will be marked as relocatable.
Requires: What is required for the binary package to run. Be advised that RPM will compute the dependencies on dynamic libs automatically.
BuildRequires: What is required for this package to build. Most likely it is one of the X-devel packages. For example, for an ncurses based application, you would probably want to put libncurses-devel (see Section 8.2 for details about the Library policy). Logical operaters such as <, >, are accepted (but not the "not"), like in the Requires: field.
BuildConflicts: What should not be installed on the machine for the build.
Provides: This is used to provide "ghost" packages. For example, apache provides webserver, wu-ftpd provides ftpserver. It can also makes available articifial alternates: proftpd will also provide ftpserver, so that anything needing "any" ftp server will be able to add ftpserver in its Requires: field.
Conflicts: What other packages does this conflict with. Logical operaters are supported.
BuildArchitectures: Specify specific arch to build this package, and let RPM bail out with errors if one of the Architectures is not the one specified in the list.
ExclusiveArch: Can only build on the specific arch specified. Packages like MILO, the Alpha equivilant of LILO will need this.
ExcludeArch: Exclude the specified archs.
ExclusiveOS: We really don't know why the heck you would need to use this. This excludes a piece of software from a a particular operating system.
Epoch: (supersedes Serial) Sometimes, naming scheme has been done the wrong way and the authors goes back to an earlier version. In this case, you need the Epoch: tag with a number. This number will take over the versioning comparison, when rpm has to guess whether a given package is newer than another. Please see Section 8.4 for how to name packages released via pre-versions.
There is also some interesting macros to note. Say you wanted to do something, but not on every platform. You can do this within RPM with the conditional macros:
%ifarch alpha sparc ppc %patch0 -p1 %endif |
The explanation: only apply the patch if the architecture is alpha, sparc, or ppc. It is not limited to using here, for example you can include this in your %files section if you so wish.
Ok. So you know that you can use rpm -ba to build a package. But you have more to play around with:
rpm -bp: stops after completion of the %prep section. Basically, it uncompresses the sources and applies the relevant patches.
rpm -bc: stops after completion of the %build section. Generally involves the equivalent of "make".
rpm -bi: stops after completion of the %install section. Well, do you have to use -ba to build again? No! All is not lost. The Mandrake RPM system has been specially patched to allow the -bi option to work with --short-circuit, which will basically resume the process. So, let's say you screwed up your %files section, and now you have fixed it, you don't have to build again from -ba. You can just use rpm -bi --short-circuit file.spec, and then rpm -ba --short-circuit file.spec to build it. Bear in mind, this is for advanced users ONLY as it is very easy to build a broken spec if you do that.
rpm --eval %macroname can evaluate a particular RPM macro. This seems to be an undocumented feature in the RPM 3.0.x series. For example, if you would like to find the %packager macro stands for, do: rpm --eval %packager.
Sometimes, program naming schemes are good, but rpm is lost. For example, with Proftpd, Pre-releases are named with the version followed by the suffix pre and then the pre version number, like so: 1.2.0pre5. Because of string comparisons, rpm thinks that 1.2.0pre5 is newer than 1.2.0 (we know that this is not true).
To permit upgrades to newer packages, we must help rpm to know which packages are newer than others. We could use the Epoch: tag (superseding the Serial: tag) but this is dirty. You have to use another naming convention, putting the "pre" stuff in the release tag; in our example you will call the package "1.2.0-0.pre5.1mdk". Then, when 1.2.0 comes out, you'll release "1.2.0-1mdk", and this package will be able to upgrade the pre5.
The program update-alternatives, originally written by Debian, is here to provide default system-wide commands through the use of symbolic links. An example will help to understand the point:
# Whenever you call 'vim'... lrwxrwxrwx 1 root root 21 Jan 31 10:25 /bin/vim -> /etc/alternatives/vim # It will go to /etc/alternatives... lrwxrwxrwx 1 root root 26 Mar 20 00:51 /etc/alternatives/vim -> ../../usr/bin/vim-enhanced # Then, to the real program. -rwxr-xr-x 1 root root 1724604 Jun 18 13:25 /usr/bin/vim-enhanced |
When a new program wants to provide the same functionality (say, vim-minimal) it would register to the update-alternatives system, and potentially become the real program called under vim. The choice is either made automatically (under that scenario, the program with highest 'priority' will be used, see below) or manually.
Here's how emacs-nox and emacs-X11 register and unregister themselves to the update-alternatives system:
%post nox update-alternatives --install %{_bindir}/emacs emacs %{_bindir}/emacs-nox 10 %post X11 update-alternatives --install %{_bindir}/emacs emacs %{_bindir}/emacs-%{version} 20 %postun nox update-alternatives --remove emacs %{_bindir}/emacs-nox %postun X11 update-alternatives --remove emacs %{_bindir}/emacs-%{version} |
The syntax with the --install command is pretty self-explanatory: you need to provide the link, the name, the full path of the real binary, and the priority. Default behaviour of the resulting system is to follow the priority (e.g. the 'automatic' mode).
Paranoid people sign their packages. This is not really necessary, but signing makes it more secure. Before Mandrake 7.2, you needed to download the crypto packages from a FTP site because of laws prohibiting the distribution of crypto packages. This is no longer true. The GNU Privacy Guard (GPG) is already included in the standard distribution. We recommend that if you wish to sign you use GPG to sign but RPM supports PGP as well.
First you will need to install PGP/GPG (depending on your liking). Now read the documentation. As with most *nix applications these come with good documentation. Generate a key, and then extract the ASCII key to a plain text file.
To sign your RPM packages while building, you can use the --sign argument. To add one to an existing RPM, do rpm --addsign file.rpm. To resign an existing one, use the --resign option. But alas, when you type this you get nothing, because you still haven't set up your ~/.rpmmacros yet.
Now, add 2 lines to your ~/.rpmmacros. Add %signature pgp and %_pgp_name Your_Signature_Name. Replace pgp with gpg, if you choose to use that one instead.
Sometimes setting the permissions of the installed files for RPM is not always %defattr(-,root,root). You can set individual permissions for files, with the %attr() macro. %attr follows a similar format to %defattr, except that %attr is put in front of the filename.
To tell RPM that the file specified is a configuration file, place a %config at the front of the file. That way, RPM will upgrade the file, but save a copy of the old one on the local hard drive. Most of the time, it is NOT the desired behaviour, because you shall lose (temporarily) your precious configuration in exchange of the default one which is obviously far poorer than what you need. To address this issue, use %config(noreplace).
Besides (noreplace), there are two other flags which are infrequently used but nevertheless listed here for the sake of being complete. They are (missingok) and (ghost). (ghost) means that the file listed need not be actually present while building the RPM, but may be present at a later stage, most probably after installation. (missingok) is the converse of (ghost).
To split off into a subpackage you can specify %package foo where foo is the suffix that will be added to the main package name.
For example, if your main package name is fred, and you have a %package alice line , then your subpackage will be called fred-alice. You will need to add in the corresponding name in the %description and %files, for example, %description alice, %files alice.
If you don't wish to have RPM inherit the name of your main package as the prefix of the subpackage, you can specify the -n argument to %package, like so: %package -n mysubpackage. You will need to add -n for all other relevant RPM entries as well such as %description, %files and the RPM scripts. One such application of this would be the library policy mentioned earlier.
This subsection is for experienced RPM builders -- to be sure that they do not miss the point.
Avoid any reference to $RPM_SOURCE_DIR. When using multiple sources, some people tend to reference these sources using this tag. This should be avoided and replaced by %{SOURCE2} for the Source2: source, for example.
Avoid to do tricky/clever/strange things in the specfile without documenting it. Most of the time, putting a comment on the line before, with your initials, is very helpful for future re-packagers, in the following style:
# (gc) I want to grab the xmms-config built in .., for bootstrapping nicely export PATH=".:$PATH" |
Some packages are only meant to be used together with special localization(s). For example, cttex, a Thai word separator program, will be useless when you don't use your system in Thai.
For such packages, you need to put an explicit Requires: on the corresponding locale package (here, locales-th). With these information, the installer will be able to change the rates of the packages according to selected internalization configuration, so that the necessary packages get installed.
Here is how Chmouel handles finely the differences between two RPM's.
rpmdiff () { if [[ -z $1 ]] then "Need a package" return 1 fi package=$(rpm -qp --qf '%{NAME}' $1) if [ -f /RPMS/$package-[0-9]* ] then /usr/bin/rpmdiff /RPMS/$package-[0-9]* $1 | \ egrep '^(missing|added)' | egrep -v '/usr(/share)?/doc/' else echo "Can't find the pacakge in /RPMS/" fi } |
example :
(chmou@kenobi)[RPMS/i586]-% rpmdiff util-linux-2.10n-1mdk.i586.rpm missing /usr/lib/more.help added /usr/share/misc/more.help (chmou@kenobi)[RPMS/i586]-% |
In a nutshell, Emacs can automate much of the work, with appropriate configuration file. A good start is Chmouel's, which is too big even to fit in appendices (> 1000 lines). You can find it on the web here: ftp://ftp.mandrakesoft.com/pub/chmou/rpm-spec-mode-mdk.el
Once installed, in addition to nice syntax highlighting, you'll increase the release tag with C-c r, add a new changelog entry (with your name, email address, correct version and release tag) with C-c e; you will even visit the bzipped patches (granted you have jka-compr and ffap) with minimal pain! And much more...
To use ffap/jka-compr in spec-file you can tell to the user to set this in ~/.emacs:
(if (not (jka-compr-installed-p)) (toggle-auto-compression)) (require 'ffap) (defvar ffap-spec-path '("../SOURCES")) (defun ffap-spec (name) (ffap-locate-file name t ffap-spec-path)) (add-to-list 'ffap-alist '(rpm-spec-mode . ffap-spec)) |