From Fedora Project Wiki

< PackagingDrafts

Revision as of 23:27, 5 December 2008 by Fab (talk | contribs) (→‎GConf: remove old <code> tag)

#!html
<div class="changelog">
:Changelog for Revision: 0.2<br/>
:StartDate: June 25, 2006<br/>
:Description:<br/>
<a href="#1">[1] </a> Add areas that need to be filled in<br/>
<a href="#2">[2] </a> Update GConf to recommend using the configure flag --disable-schemas-install<br/>
</div>

RPM scriptlet recipes

Rpm spec files have several sections which allow packages to run code on installation and removal. These scriptlets are mostly used to update the running system with information from the package. This page offers a quick overview of the RPM scriptlets and a number of common recipes for scriptlets in packages. For a more complete treatment of scriptlets, please see the Maximum RPM book .

Contents:


Syntax

The basic syntax is similar to the %build, %install, and other sections of the rpm spec file. The scripts support a special flag, -p which allows the scriptlet to invoke a single program directly rather than having to spawn a shell to invoke the programs. (ie: %post -p /sbin/ldconfig)

The scriptlets also take an argument, passed into them by the controlling rpmbuild process. This argument, accessed via $1 is the number of packages of this name which will be left on the system when the action completes. So for the common case of install, upgrade, and uninstall we have:

install upgrade uninstall
%pre $1 == 1 $1 == 2 --
%post $1 == 1 $1 == 2 --
%preun -- $1 == 1 $1 == 0
%postun -- $1 == 1 $1 == 0

Note that these values will vary if there are multiple versions of the same package installed (This mostly occurs with parallel installable packages such as the kernel. However, it can also occur when errors prevent a package upgrade form completing.) So it is a good idea to use this construct:

%pre
if [ $1 -gt 1 ] ; then
fi

For %pre and %post scripts rather than checking that it equals 2.

Scriptlet Ordering

The scriptlets in %pre and %post are respectively run before and after a package is installed. The scriptlets %preun and %postun are run before and after a package is uninstalled. On upgrade, the scripts are run in the following order:

1. %pre of new package 2. package install 3. %post of new package 4. %preun of old package 5. removal of old package 6. %postun of old package

Snippets

Shared libraries

Installing shared libraries requires running /sbin/ldconfig to update the dynamic linker's cache files. These can be invoked like:

%post
/sbin/ldconfig
%postun
/sbin/ldconfig

It is also common to invoke these with the '-p' option as they are often the only program invoked in a scriptlet:

%post -p /sbin/ldconfig
%postun -p /sbin/ldconfig

If applicable, the latter way is recommended because doing so will automatically add appropriate dependencies on /sbin/ldconfig to the package (and FWIW, will prevent unnecessarily launching a shell process in the scriptlets).

Services

Initscripts Conventions

These are the general conventions in use in Fedora Core scriptlets. As always, there are exceptions, or places where we haven't fully implemented this.

Initscripts conventions in the scripts themselves

initscripts need to definitely support the following arguments:

  • start

starts the service

  • stop

stops the service


initscripts in almost all cases should support:

  • restart

restarts the service. Can be implemented as stop/start.

  • condrestart

restarts the service if it is already running.

  • status

displays status on the service

initscripts may implement

  • reload

reloads the service's configuration without restarting it


For more information, see:

/usr/share/doc/initscripts-*/sysvinitfiles

If you don't have this on your system you'll need to install the initscripts rpm.

Initscripts in spec file scriptlets

Requires(post): /sbin/chkconfig
Requires(preun): /sbin/chkconfig
Requires(preun): /sbin/service
...
%post
/sbin/chkconfig --add <script>

%preun
if [ $1 = 0 ] ; then
/sbin/service <script> stop >/dev/null 2>&1 || :
/sbin/chkconfig --del <script>
fi

'if [ $1 = 0 ] ' checks that this is the actual deinstallation of the package, as opposed to just removing the old package on upgrade. These statements stop the service, and remove the /etc/rc*.d links.

Requires(postun): /sbin/service
...
%postun
if [ "$1" -ge "1" ] ; then
/sbin/service <script> condrestart >/dev/null 2>&1 || :
fi

'if [ "$1" -ge "1" ] checks that this is an upgrade of the package. If so, restart the service if it's running. (This may not be appropriate for all services.)

Why don't we....

  • run 'chkconfig <service> on'?

If a service should be enabled by default, make this the default in the init script. Doing otherwise will cause the service to be turned on on upgrades if the user explicitly disabled it.

Note that the default for most network-listening scripts is off. This is done for better security. We have multiple tools that can enable services, including GUIs.

  • start the service after installation?

Installations can be in changeroots, in an installer context, or in other situations where you don't want the services started.

GConf

GConf is a configuration scheme currently used by the GNOME desktop. Programs which use it setup default values in a [NAME] .schemas file which is installed under %{_sysconfdir}/gconf/schemas/[NAME] .schemas. These defaults are then registered with the gconf daemon which monitors the configuration values and alerts applications when values the applications are interested in change. The schema files also provide documentation about what each value in the configuration system means (which gets displayed when you browse the database in the gconf-editor program).

For packaging purposes, we have to disable schema installation during build, and also register the values in the [NAME] .schemas file with the gconf daemon on installation and unregister them on removal. Due to the ordering of the scriptlets, this is a four step process.

%build
%configure --disable-schemas-install
...

In the cases where the package doesn't use autotools or doesnt support the --disable-schemas-install configure flag we instuct gconftool to ignore schema installation commands by setting the GCONF_DISABLE_MAKEFILE_SCHEMA_INSTALL environment variable.

Note.png
The AM_GCONF_SOURCE_2 autoconf macro supplies this simple command line flag. Since nearly all packages that use gconf use this macro, this flag can be used to turn off installation in nearly all cases.
%install
rm -rf $RPM_BUILD_ROOT
export GCONF_DISABLE_MAKEFILE_SCHEMA_INSTALL=1
make install DESTDIR=$RPM_BUILD_ROOT
...

Here we suppress the installation of the schema during the building of the package so that we can subsequently handle it when the package is actually installed or uninstalled.

Here's the second part:

Requires(pre): GConf2
Requires(post): GConf2
Requires(preun): GConf2
...
%pre
if [ "$1" -gt 1 ] ; then
export GCONF_CONFIG_SOURCE=$(gconftool-2 --get-default-source)
gconftool-2 --makefile-uninstall-rule \
%{_sysconfdir}/gconf/schemas/%{name}.schemas >/dev/null || :
killall -HUP gconfd-2 || :
fi

In this section we uninstall the old schemas when we upgrade. The way we do this is first to get information about where gconf stores its values via the gconftool-2 --get-default-source line. Then we uninstall the schema from that source. If the package could be upgrading a package which had another name for the schema at one time, then we uncomment the lines to uninstall those as well. Finally, we send a SIGHUP to the gconf daemon so it notices the changes to the database.

The next section is for installing the new schema:

%post
export GCONF_CONFIG_SOURCE=$(gconftool-2 --get-default-source)
gconftool-2 --makefile-install-rule \
%{_sysconfdir}/gconf/schemas/%{name}.schemas > /dev/null || :
killall -HUP gconfd-2 || :

Here we do the same things as in the %pre section for upgrading except the gconftool-2 switch used is --makefile-install-rule to install the new schemas instead of the uninstall-rule to remove the old schemas.

The last section deals with deleting the schemas on package removal:

%preun
if [ "$1" -eq 0 ] ; then
export GCONF_CONFIG_SOURCE=$(gconftool-2 --get-default-source)
gconftool-2 --makefile-uninstall-rule \
%{_sysconfdir}/gconf/schemas/%{name}.schemas > /dev/null || :
killall -HUP gconfd-2 || :
fi

This snippet is nearly the same as the one for upgrading. Why can't we just combine this portion with the %pre portion? The answer is that we want to delete any old versions of the schema during an upgrade. But this has to happen before we install the new version (in the %post script) otherwise we end up removing the schema that the upgrading package installs. However, if it really is a removal that will leave no other instances of this package on the system, we have to clean up the schema before deleting it.

Note: When Bug #173869 is resolved, gconftool-2 will contain code to signal gconfd to reload its schemas. Therefore, the killall -HUP gconfd-2 || : lines in all the scriptlets will be optional.

Texinfo

The GNU project and many other programs use the texinfo file format for much of its documentation. These info files are usually located in /usr/share/info/. When installing or removing a package, install-info from the info package takes care of adding the newly installed files to the main info index and removing them again on deinstallation.

Requires(post): /sbin/install-info
Requires(preun): /sbin/install-info
...
%post
/sbin/install-info %{_infodir}/%{name}.info %{_infodir}/dir || :

%preun
if [ $1 = 0 ] ; then
/sbin/install-info --delete %{_infodir}/%{name}.info %{_infodir}/dir || :
fi

These two scriptlets tell install-info to add entries for the info pages to the main indexfile on installation and remove them at deinstall time.

Scrollkeeper

Gnome and KDE use the scrollkeeper cataloging system to keep track of documentation installed on the system. Scrollkeeper allows the help system to sort and search documentation metadata stored in .omf files. When you add documentation in these systems you need to make scrollkeeper aware that the documentation has been changed.

Note that we BuildRequires scrollkeeper as most Makefile's are setup to install the necessary scrollkeeper files only if scrollkeeper is present at install time.

BuildRequires:  scrollkeeper
Requires(post): scrollkeeper
Requires(postun): scrollkeeper
...
%post
scrollkeeper-update -q -o %{_datadir}/omf/%{name} || :

%postun
scrollkeeper-update -q || :

These two scriptlets tell scrollkeeper to update its indexes to account for the new scrollkeeper files.


desktop-database (Needs description)

Use this when a desktop entry has a MimeType key.

%post
update-desktop-database &> /dev/null ||:

%postun
update-desktop-database &> /dev/null ||:

Note: For FC5+, this scriptlet follows the same convention as mimeinfo files and gtk-icon-cache. Namely, the spec file should not Require desktop-file-utils for this. For older releases, one should

Requires(post): desktop-file-utils
Requires(postun): desktop-file-utils

(See http://bugzilla.redhat.com/180898 and http://bugzilla.redhat.com/180899)

mimeinfo (Needs description)

Use this when a package drops an XML file in %{_datadir}/mime/packages.

%post
update-mime-database %{_datadir}/mime &> /dev/null || :

%postun
update-mime-database %{_datadir}/mime &> /dev/null || :

Note that similarly to the gtk-update-icon-cache code, these scriptlets should be run only if the user has update-mime-info installed and without a specific Requires: shared-mime-info. If shared-mime-info is not installed, update-mime-database won't be run when this package is installed. This does not matter because it will be run when the shared-mime-info package is installed.

GTK+ icon cache

If an application installs icons into one of the subdirectories in %{_datadir}/icons/ (such as hicolor in the following examples), gtk-update-icon-cache should be run after the package is installed/uninstalled on FC4 and later. This is required so that the installed icons show up in GNOME menus right after package installation, and speeds up GTK+ applications' access to the icons. For KDE, just 'touch'ing the top-level icon directory is enough.

Note that no dependencies should be added for this. If gtk-update-icon-cache is not available, there's nothing that would be needing the cache update. Not adding the dependency on gtk-update-icon-cache (ie. gtk2 >= 2.6.0) makes it easier to use the package (or the same specfile) on systems where it's not available nor needed, such as older distro versions or (very) trimmed down installations.

%post
touch --no-create %{_datadir}/icons/hicolor || :
if [ -x %{_bindir}/gtk-update-icon-cache ] ; then
%{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor || :
fi

%postun
touch --no-create %{_datadir}/icons/hicolor || :
if [ -x %{_bindir}/gtk-update-icon-cache ] ; then
%{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor || :
fi

Fonts (Needs description)

Use this when your package installs new fonts.

%post
if [ -x /usr/bin/fc-cache ] ; then
/usr/bin/fc-cache /usr/share/fonts
fi
%postun
if [ "$1" = "0" ] ; then
if [ -x /usr/bin/fc-cache ] ; then
/usr/bin/fc-cache /usr/share/fonts
fi
fi

XML Catalogs (Needs entry)

[Toshio has examples in the qa-assistant package]

Tips for writing new recipes

  • As you may have noticed, most commands in snippets need to have a "|| :" on the end of them. This little piece of code causes

each command to exit with a successful exit status whether or not the command worked. This is important because the scriptlet as a whole will error the moment it tries to execute a command that has a non-zero exit status. Because a normal scriptlet has many distinct commands that need to execute independently of each other, the ability to run as many of the commands within a scriptlet as possible is generally what you want.