wiki:RpmHowToPackagers

Version 66 (modified by dmik, 4 years ago) (diff)

--

RPM How-To for packagers

Basic informations for packagers: from writing a spec file, to testing a package, to releasing a package.

Pre Req

yum install rpm-build

in the below description the %HOME% is used for the top rpm directory. If you want to have your rpm topdirectory in another location you have to do the following:

  • create a file called .rpmmacros in your %HOME% dir
  • add the following to the file "%_topdir x:/rpmbuild" (w/o the quotes)

Introduction

RPM uses a special file to compile source code, prepare installation and create .rpm files: it is a .spec file, which is just a bunch of shell commands to prepare, build, install, check and clean the setup.

The Fedora project has a very good manual on creating RPM packages. It is recommended for reading in addition to these brief instructions to get deeper understanding of the RPM creation process.

Also, there is a Fedora RPM Guide that may be used as an RPM reference documentation.

If you package cross-platform open-source software, there is a great chance that a .spec file for it already exists and it is a good idea to take it as a base and adapt to OS/2 as needed instead of creating a new one from scratch. The best source for .spec files is Fedora (it has RPMs for virtually each and every open source project) and all its .spec files are available online. In fact, the majority of OS/2 .spec files are created from Fedora .spec files so you should check this source first and only proceed with other ways of creating .spec if you fail to find one there. The repository is very big but there is a search field in the top right corner that allows you quickly navigate to the needed .spec file by project/library name. There are some open-source packages, however, that Fedora refuses to provide for various reasons. Some of them may be found here: http://rpmfusion.org.

If, for some reason, you fail to find the right .spec file in the Fedora repository (which is unlikely), you may try to search it in other places using the RPM find services like rpm.pbone.net or www.rpmfind.net. Enter the name of your package, and choose a distribution: prefer Fedora (see above) or OpenSUSE if Fedora is not available. Select your package from the left link, you will get a page with all details; one of them is the .src.rpm package, it contains the spec file, platform specific patches, package sources (in .tar.gz, .tar.bz2, .tar.xz format). Use unrpm.cmd (check bootstrap directory) to extract all files to a temporary location.

When you create an OS/2 .spec file based on the existing one, please always reference the original file at the first line of the resulting .spec file. If you use the Fedora .spec file taken from the Git repository, please reference the commit number like in this this .spec file: spec/trunk/SPECS/libvpx.spec?rev=950#L1. Otherwise please specify an URL of the source where you take the .spec file from. This way we will be able to keep our .spec files properly updated as the original .spec file gets updates to reflect new software versions.

If you create a .spec file for new or proprietary software then you most likely don't have a .spec file to copy from. In this case it's best to select some similar application or library among existing OS/2 or Fedora .spec files and use it as a template.

RPM packages are created from .spec files using the rpmbuild command. The first time you try to build a package (see Building package below), rpmbuild will create a set of directories in your %HOME% directory that it uses for the package generation process:

  • %HOME%\rpmbuild\BUILD (temporary storage for compiled files)
  • %HOME%\rpmbuild\BUILDROOT (temporary storage for installed files)
  • %HOME%\rpmbuild\RPMS (destinations of built .rpm files)
  • %HOME%\rpmbuild\SOURCES (source files and patches)
  • %HOME%\rpmbuild\SPECS (spec files)
  • %HOME%\rpmbuild\SRPMS (destination of built .src.rpm files)

Basic RPM tags:

  • summary: a brief (usually one line) description of the package
  • name: the name of the package
  • version: the package version
  • release: the release of the package, e.g., an initial release is 1%{?dist} a next release of the same version is +1. When the version changes, the release starts with 1 again.
  • license: the license under which the package is being released (GPLv3, etc.)
  • url: the main url to the original project or maintainer
  • group: see more on the group tag, below
  • source: the url (or filename) of the source package used
  • source1: the url (or filename) of additional source package, etc.
  • patch0: the url (or filename) of the first patch file applied
  • patch1: the url (or filename) of the next patch file applied, etc.

For more information on tags, see this link.

Package groups

Groups help to organize packages according to different functionality which they provide. In order to be useful, certain guidelines should be followed when specifying a group tag. In general, the following groups should be used (specified verbatim):

  • Applications/Archiving
  • Applications/Databases
  • Applications/File
  • Applications/Internet
  • Applications/Publishing
  • Applications/System
  • Applications/Text
  • Development/C
  • Development/Languages
  • Development/Libraries
  • Development/Libraries/C and C++
  • Development/Other
  • Development/Tools
  • Documentation
  • Libraries
  • Productivity/Archiving/Compression
  • System Environment/Base
  • System Environment/Daemons
  • System Environment/Libraries
  • System Environment/Shells
  • System/Libraries
  • User Interface/X

If left empty, the group tag will be interpreted as "Unspecified" which may not be very helpful.

It is also interesting to note that while Fedora 18 has apparently done away with the requirement to utilize the group tag, we continue to use this for OS/2.

Prepare source code

This is done in the %prep section. Here the %setup macro will expand your source package (the one in the Source: line) and write the files by default in the %HOME%\rpmbuild\BUILD\{name}-{version} directory. You can change this with -n parameter for %setup macro.

If you need to extract more source files, use the -a parameter.

%prep
# -D Do not delete the directory before unpacking.
# -T Disable the automatic unpacking of the archives.
%setup -q -n %{name}-%{srcver} %{?with_int_bdb:-a 1} -a 2

If your files are packed in the root directory of the zip file, use -c to tell rpmbuild to create a new directory using %{name}-%{version} scheme and to put files there (preserving zip directory layout):

%prep
%setup -q -c

To apply first patch add patch0 -p1 -b .my_suffix; the second will use patch1... and so on. Example:

%prep
# -D Do not delete the directory before unpacking.
# -T Disable the automatic unpacking of the archives.
%setup -q -n %{name}-%{srcver} %{?with_int_bdb:-a 1} -a 2
%patch001 -p1 -b .base

Note that rpmbuild defaults expects to find source tarball and patches in the SOURCES directory.

Build source code

Done in the %build section: common steps are execution of the configure script and use of gnu make to build code (this may be different for other programs). Example:

%build
CONFIG_SHELL="/bin/sh" ; export CONFIG_SHELL ; \
LDFLAGS="-Zbin-files -Zhigh-mem -Zomf -Zargs-wild -Zargs-resp" ; export LDFLAGS ; \
LIBS="-lurpo -lmmap -lpthread" ; export LIBS ; \
%configure \
    --enable-shared --disable-static --without-lua \
    %{!?with_int_bdb: --with-external-db} \
    %{?with_sqlite: --enable-sqlite3} \
    --enable-python \
    "--cache-file=%{_topdir}/cache/%{name}.cache"

make %{?_smp_mflags}

In this case, %configure is another macro, and it will be expanded to add proper paths for common variables, like prefix, exec_prefix and others. Keep in mind that rpmbuild will use /@unixroot/usr as prefix, so all built software will go to the same tree layout. Also programs will be able to be executed from other drives.

Most of the times, the CONFIG_SHELL variable is required, otherwise bash shell will be selected if available: bash does not work very well, ash is more compatible. CFLAGS will be added by rpm. If you need some special CFLAGS please set them as follows:

CFLAGS = "your_flags %{optflags}"

So the special optimizer flags for the build arch get also added.

Install binaries

Done in the %install section: most of the times, make install is the required step,

%install
rm -rf $RPM_BUILD_ROOT
make DESTDIR="$RPM_BUILD_ROOT" install

DESTDIR is usually a macro supported in most recent build environments.

File packaging

Done in the %files section: here all required package files must be listed. While /* could be fine since it will include all files, it is better to have a detailed list, so it is easier to catch make install errors.

%files
%{_bindir}/rpm.exe
%{_mandir}/man8/rpm.8*
%{_libdir}/rpm*.dll

Building package

When the .spec file is in place, packaging of binary files and source code can be started with

rpmbuild -ba myfile.spec

Special parameters can be used instead of -ba:

  • -bb build only binaries
  • -bs build only sources
  • -bc compile only code and stop.
  • -bi install only code and stop.
  • -bl check file list only and stop.

Since -bc, -bi, and -bl are also normally performing the previous steps, you can add the special option --short-circuit - this will skip all previous steps (rpmbuild will assume they are already okay), and do only the selected task.

All output files will be written in the %HOME%\rpmbuild tree, regardless of current directory and drive.

Taking sources from source repositories

It's a common practice to take the source code for building a set of RPM packages for a program from a tarball file (referred to by the Source: keyword in the .spec file) which is provided by the program vendor. This works fine when the packages are built from unmodified vendor sources but this is rarely a case on OS/2. This is mainly because we don't push our patches upstream (for many reasons) and instead host our own repositories for software that we support (many of the software is located here: http://trac.netlabs.org/ports/).

Given this, using the tarball approach on OS/2 requires to pull a set of patches from our own source repository and apply it to the vendor's tarball to make the program work on OS/2 each time a new change is made and a new RPM version is to be released. This requires a lot of manual work which very inconvenient and time consuming, especially when dealing with a large number of packages. For this reason, it is strongly recommended to write .spec files that download the sources directly from the source repositories rather than use tarballs and patch files. This may be easily achieved with a couple of RPM macros that we added to RPM for OS/2 specifically for this purpose. The following instructions apply to both porting the existing .spec files to OS/2 and creating new ones.

Note that you should not worry too much about the network speed and traffic issues when using these macros since the sources are downloaded only once for each given repository revision; any subsequent attempt to build a package form the same revision will simply use the source ZIP created when the revision is fetched for the first time w/o involving any network connection. This ZIP is also stored in a SRPM file as usual, so a rebuild of the package may be done without accessing the repository as long as the SRPM file for the given set of packages is available. There is also a possibility to use local working copies to fetch the sources from instead of online repositories (see below).

%scm_source

This macro is intended as a replacement for the standard Source: tag. The format is as follows:

%scm_source SCM URL REV

where SCM is the type of the source code management system (currently supported ones are svn, git and github), URL is the full URL of the source code repository for the given program and REV is the revision to use.

%scm_setup

This macro is intended as a replacement for the standard %setup macro that starts a %prep section.

The format is as simple as:

%scm_setup

Further sections describe the supported SCM types in detail.

SVN

An example:

%scm_source svn http://svn.netlabs.org/repos/ports/which/trunk 733

This will check out revision 733 of program which from the trunk of the our main OS/2 repository.

Note that for testing purposes you may use a local working copy of the SVN repository by specifying something like D:/Coding/ports/which/trunk instead of the URL. You may also omit the revision number if you want your local changes to be used instead of a clean revision. But please never distribute RPM packages built this way as it's generally impossible to rebuild them on another machine which breaks RPM repository consistency and our deployment rules. Also note that if you omit the revision number, no source ZIP file will be created by %scm_source so the source code will be checked out each time you build an RPM and an attempt to run the full build cycle that creates an SRPM file will fail.

Git

An example:

%scm_source git git://mygitserver.com/foobar.git mytag

This will check out a commit named mytag from the foobar's repository (you may use any supported Git commit identifier instead of tags, of course).

You may also omit the revision specification at all in which case it will checkout the HEAD commit. Note, however, that as well as in case of SVN, packages with a missing revision specification must not be distributed No ZIP files are created for them, so a clean checkout will happen each time and SRPM creation will fail.

Using local clones of git repositories for test builds is also possible, the URL in this case will be something like file://D:/Coding/foobar.

GitHub

An example:

%scm_source github https://github.com/komh/kai kai-%{version}

This will checkout a commit named kai-%{version} from the GitHub?-hosted project kai which is owned the user named komh. Any valid Git commit identifier may be used. Note that you cannot omit the commit specification in case of GitHub?.

You may use local clones of GitHub? projects for test builds but in this case you should use the git SCM type as described in the #Git section above.

Handling documentation in Info format

Many packages provide extended documentation in Info format (.info files in /@unixroot/usr/share/info). These .info files need special processing when the package is installed (and removed) so that the Info reader could properly find them. Many .spec files already contain the required commands but they need tailoring to OS/2. This tailoring is rather easy and consists of 3 steps.

  1. Fix the Requires: statements. You will usually find this in a .spec file:
    Requires(post): /sbin/install-info
    Requires(preun): /sbin/install-info
    
    Replace this block with these lines:
    # @todo Replace with `%info_requires` when it's available. 
    Requires(post): %{_sbindir}/install-info.exe 
    Requires(preun): %{_sbindir}/install-info.exe 
    

Note also that the package that provides Info files should normally build-depend on the texinfo package. This will make sure that all tools necessary to process the documentation are installed. So please add this line

BuildRequires: texinfo

to the .spec file if it's not already there.

  1. Fix the post-install script. Replace lines looking like this:
    %post
    /sbin/install-info %{_infodir}/foobar.info %{_infodir}/dir 2>/dev/null || :
    
    with this:
    %post 
    # @todo Replace with `%info_post foobar.info` when it's available. 
    if [ -f %{_infodir}/foobar.info ]; then 
        %{_sbindir}/install-info.exe %{_infodir}/foobar.info %{_infodir}/dir || : 
    fi
    
  1. Fix the pre-uninstall script. Replace lines looking like this:
    %preun 
    if [ $1 -eq 0 ]; then 
      /sbin/install-info --delete %{_infodir}/help2man.info \ 
        %{_infodir}/dir 2>/dev/null || : 
    fi
    
    with this:
    %preun 
    # @todo Replace with `%info_preun foobar.info` when it's available. 
    if [ $1 -eq 0 ]; then 
        if [ -f %{_infodir}/foobar.info ]; then 
            %{_sbindir}/install-info.exe --delete %{_infodir}/foobar.info %{_infodir}/dir || : 
        fi 
    fi
    

Note that the lines you insert contain a @todo block which you should leave in. It is to remind to replace these lines with simpler constructs when #119 is fixed.

Creating doc packages

A lot of software comes with various types of documentation. User-level documentation (such as manuals in MAN and INFO formats) is usually installed with the main package as it should be always available once the software is installed. However, many software packages also come with the development libraries (in a sub-package called PACKAGE-devel where PACKAGE is the main .spec package name) accompanied by the documentation for developers. This documentation may occupy quite a lot of space on disk (several or even several dozen MB) when installed and unpacked. Given that it's usually more convenient to read it online, it makes little sense to have it installed each time when PACKAGE-devel is installed.

Some packages already separate the developer's documentation from the development sub-package by putting it to a separate package PACKAGE-devel-doc but there is a plenty of them that still put everything in PACKAGE-devel. A .spec file for such a packages should be altered to separate the documentation from development libraries and tools. Here is a template for the respective part of the .spec file:

%package    devel-doc
Summary:    Development documentation files for %{name}
Group:      Documentation
BuildArch:  noarch
Requires:   %{name}-devel = %{version}-%{release}

%description    devel-doc
This package contains the documentation files useful for
developing applications that use %{name}.

...

%files devel-doc
%doc docs/html/
%doc examples/*.c

Please note that PACKAGE-devel-doc requires PACKAGE-devel but make sure that PACKAGE-devel does NOT require PACKAGE-devel-doc as otherwise this separation will be useless because the documentation sub-package will be implicitly installed each time the development sub-package is installed.

Another benefit of this separation is that (due to BuildArch: noarch) the RPM repositories will only have one copy of documentation files instead of multiple copies stored in multiple platform-dependent devel packages.

Of course, if you create a new .spec file from scratch, you should separate devel and devel-doc right away.

Note that in some cases user-level documentation may also be quite bulky (especially when it contains a lot of HTML files with pictures, big PDF files, etc). In such cases it may make sense to put it to a separate package, named PACKAGE-doc (%package doc in terms of the .spec file) similarly to the template shown above. A rule of thumb is that brief documentation (MAN, INFO) should be always installed with the main package and extended documentation, if it's several dozen MB or more, should go to a doc sub-package.

Filtering out unneeded automatic dependencies

Recent RPM versions support filtering automatic Provides and Requires dependencies generated by dependency detection scripts. Filtering out some directories from detection or some dependencies from attaching them to the generated RPMs may be necessary in cases where detection scripts give wrong results or when we lack some packages (usually Perl ones) that don't actually break program behaviour.

The standard dependency generation and filtering facility is described in detail in the official RPM documentation: http://rpm.org/user_doc/dependency_generators.html.

Generating debug info packages

RPM can automatically extract debug info from the EXEs and DLLs built by the .spec file and put this info into a separate sub-package named PACKAGE-debuginfo (formally PACKAGE-debug) where PACKAGE is the main .spec package name. In order to do that, the following directive needs to be added to the .spec file (usually, after all package and sub-package definitions and before the %prep section, surrounded by empty lines):

%debug_package

Normally nothing else needs to be done, RPM will automatically handle all *.exe, *.dll and *.pyd files generated by your .spec. However, sometimes it's desirable to disable debug info generation for certain files. This may be done with the %_strip_opts definition placed at the beginning of the .spec file, e.g. as follows:

# Exclude myfile*.dll from the processed files
%define _strip_opts --debuginfo -x "myfile*.dll"

# Add *.mymod files to the default list of files to be processed
%define _strip_opts --debuginfo -i "*.mymod"

# Process only *.mymod files (do not process default extensions at all)
%define _strip_opts --debuginfo -n "*.mymod"

Note that if you don't need any debug info generation at all, simply remove the %debug_package macro from your .spec file and debug file generation will be automatically suppressed (all the debug info will be silently discarded from the executable files during the compression phase in such case).

Generating legacy runtime packages

Many dynamic libraries use versioning in DLL names to express incompatible ABI changes (i.e. if the old version is named libxyz1.dll, the new one becomes libxyz2.dll and so on). This way, an old application built against libxyz1.dll will continue to work even when libxyz2.dll gets installed. However, this popular deployment approach is not directly supported by RPM (you cannot have two versions of the same package installed at the same time, even if the file names differ).

In order to nicely solve this problem, an extension was added to RPM for OS/2 that allows users to have two incompatible versions of the same runtime DLL installed side by side and at the same time keep the normal package update functionality fully intact. This extension works in a fully automatic manner, provided that the packager uses the source:/rpmbuild-bot script.

Given a package named package, the following steps need to be done in order to generate legacy runtime packages with rpmbuild-bot.sh:

1. Add a rpmbuild-bot configuration entry

Add an entry named RPMBUILD_BOT_LEGACY_package to rpmbuild-bot-env.sh containing a package specification string in the following format:

ABI|NAME|VERSION-RELEASE|[FILEMASK]|[ARCH]

ABI is the ABI version (normally, a suffix added to the DLL basename to differentiate between incompatible DLL versions), NAME is a name of the old package where to take the DLL from (usually is the same as package), VERSION-RELEASE is the version of the old package and FILEMASK is a file mask for runtime DLLs (by default, *.dll). You may specify more than one ABI specification (separated by a space) tho this variable if you need to provide more than one version of legacy runtime DLLs.

Given the above information, rpmbuild-bot will automatically extract the runtime DLLs from the specified RPM package located in the stable repository (the one specified in aRPMBUILD_BOT_UPLOAD_REPO_STABLE variable) and make these DLLs available for rpmbuild by storing them in a SOURCES directory of its directory tree.

Note that rpmbuild-bot will properly handle all supported target platforms (as specified in RPMBUILD_BOT_ARCH_LIST) when extracting old runtime DLLs in order to make sure that the automatically generated legacy sub-package will carry a DLL built for the same target platform as the main package. If an old version of the DLL didn't exist for this platform, rpmbuild-bot will fail. In such a case you should force a specific platform of the old DLL to be used for all target platforms of the new package by using an optional ARCH field in the legacy package specification string.

2. Add a magic macro to package.spec

Add a %legacy_runtime_packages macro somewhere before the %prep section of the .spec file. This will cause rpmbuild to create a sub-package named package-legacy-ABI for each ABI listed in the configuration variable for this package. There are a few things to note about the generated legacy sub-packages and %legacy_runtime_packages specifics:

  1. The legacy sub-package will carry a version and release number of the original package (i.e. the package where the legacy runtime DLL is taken from), not the main package version. This is done to to simplify identification purposes and also to make it possible refer to a particular version from other .spec files when necessary.
  1. The legacy sub-package will have Provides: field set to the old version (i.e. the version of the original package) and Obsoletes: field set to less than the old version. This serves for two purposes: a) to provide smooth package updates with yum update (which will simply replace the old package with the new package-legacy-ABI) and b) to keep other software packages which still depend on the old runtime installable.
  1. The use of the %legacy_runtime_packages macro has one side effect (due to this RPM design flaw): it overwrites the definitions of the standard %version and %release macros for all sections of the .spec file except the %package ones. Normally this is not a problem since these macros are rarely used anywhere besides %package. But there is at least one common case where they are typically used: the %setup macro. If a source archive uses an unusual directory structure and requires manual unpacking, the setup macro invocation may look like this: %setup -n %name-%version-%release. This will not work as expected when %legacy_runtime_packages is present (in particular, the setup macro will create a build directory carrying the version of the legacy package rather than the main .spec version). However, this and all similar problems may be easily fixed by replacing %version and %release through out the .spec file (excluding the %package sections!) with special macros %main_version% and %main_release defined by %legacy_runtime_package to preserve the original values of the top-level Version: and Release: tags of the .spec file.

Setting up executable compression

By default, RPM performs compression for the following executable files built by the .spec file before putting them to .rpm bundles: *.exe, *.dll and *.pyd. Compression is done using the lxlite tool by optimizing LX object layout and discarding debug info. Sometimes it is desirable to change the list of files to be compressed (or to disable compression at all). Here are the examples of how to do it:

# Disable compression completely
%define _strip_no_compress 1

# Exclude myfile*.dll from the compressed files
%define _strip_opts --compress -x "myfile*.dll"

# Add *.mymod files to the default list of files to be compressed
%define _strip_opts --compress -i "*.mymod"

# Compress only *.mymod files (do not compress default extensions at all)
%define _strip_opts --compress -n "*.mymod"

Cleaning up the build

The .spec file specification supports a special section intended for performing a cleanup after a successful build by deleting intermediate files and directories created during the build process. This optional section starts with the %clean keyword and, if present, goes right after the %install section in the .spec file.

If the %clean section is absent, rpmbuild does not perform any cleanup and leaves both the package build directory and the sandboxed build root directory on disk. This is usually a waste of space because these directories will be automatically removed anyway at the beginning of a subsequent rpmbuild run for the same package. For this reason it is advised to always add the %clean section to the .spec file which at least removes the sandboxed build root directory (as its contents matches the contents of the created .RPMs and therefore doesn't contain any usueful information). Here is how such a section will look like:

%clean
%{__rm} -rf ${RPM_BUILD_ROOT}

Note that while it may seem reasonable to delete the package build directory (containing both the sources and all the output files of the build process) as well, it is normally not a good idea to do it from the %clean section as it may be needed to check the build details later to analyze possible misbehaviour of the released RPMs.

OS/2 specific notes

While the syntax of .spec files is the same used under Unix, not everything is currently working in the OS/2 port. Also many packages are already built and manually installed: this means RPM is not aware of their presence.

Many Unix .spec files have a (or multiple) BuildRequires: rules: if the package is not already built with RPM, the line should be commented until the required package will have a RPM install. The same applies to Requires: rules.

Another missing feature is the automatic detection of required packages: while OS/2 rpm can analyze the LX binaries to detect the required DLLs on the fly, the same is not possible for python or perl scripts (unix rpm detects the required abi level by parsing scripts and querying python/perl modules).
Under OS/2 these requirements must be manually added using a BuildRequires: or Requires: rule. Example:

Requires: python
Requires: python(abi) = 2.6

Repacking binary software archives

When you create RPM packages for software that you don't build from sources (like old OS/2 programs or any other software that is only available in the binary form), you need to follow a special procedure in order to preserve the sizes and timestamps of the original files (which may be important for their identification in case someone needs that information).

  1. Add the following to the .spec file (somewhere before the %install section):
    # disable lxlite compression
    %define __os_install_post	%{nil}
    
  2. Use
    cp -dp <src> <dst>
    
    in the %install section when you copy files from BUILD to BUILDROOT for getting packed by rpmbuild; the naked cp command will kill the original timestamp (the <dst> file will get the current time). The -p flag causes cp to preserve timestamps and the -d flag preserves symlinks (just in case a file you copy is a LIBC symlink). Another cp flag you may find useful is -R which causes it to copy directories recursively.

Note that it doesn't make much sense to use cp -dp in packages which you build from sources, since the original timestamp in this case is the build time which won't differ too much from the install time (as the %install stage happens right after the %build stage).

WPS object creation

RPM provides a number of macros to create WPS objects for your package from %post and %postun scriplets. These macros start with %wps_object_. All WPS objects created using these macros are associated with the package and recorded in a special database. Each object also has a reference count (which is increased when the object is created and decreased when it is deleted). The physical object deletion only happens when the reference count drops to zero. This is useful for objects (e.g. application folders) shared between several packages since it allows to keep them alive as long as there is one or more packages which reference these objects installed and automatically remove them once all of the referencing packages are removed.

Here is an example:

%post
if [ "$1" -ge 1 ]; then # (upon update)
    %wps_object_delete_all
fi
%wps_object_create_begin
MYAPP_EXE:WPProgram|My App|<WP_DESKTOP>|EXENAME=((%{_bindir}/myapp.exe))
%wps_object_create_end

%postun
if [ "$1" -eq 0 ]; then # (upon removal)
    %wps_object_delete_all
fi

For a sub-package named "extras", the scriptlets will look as follows:

%post extras
if [ "$1" -ge 1 ]; then # (upon update)
    %wps_object_delete_all -n %{name}-extras
fi
%wps_object_create_begin -n %{name}-extras
MYAPP_EXTRAS_FOLDER:WPFolder|My App Extras|<WP_DESKTOP>
MYAPP_FOOBAR_EXE:WPProgram|My App FooBar|<MYAPP_EXTRAS_FOLDER>|EXENAME=((%{_bindir}/myapp-foobar.exe))
%wps_object_create_end

%postun
if [ "$1" -eq 0 ]; then # (upon removal)
    %wps_object_delete_all -n %{name}-extras
fi

Creating fake WarpIN database entries

Some packages still distributed as WarpIN packages (WPI) now depend on packages distributed via rpm/yum. In order to make these WPI packages happy, it is possible to let the rpm/yum package create fake entries in the WarpIN database.

wpi4rpm will fake the vendor and the package name to "wpi4rpm", get the appropriate app name and translate the version from rpm/yum into a version number scheme supported by WarpIN. Most notably version numbers, which have letters are translated into 99. The filelist and several other fields hold dummy entries.

This is done the following simple way:

Requires:  wpi4rpm

%post
if [ "$1" -ge 1 ]; then # (upon update)
    wpi4rpm del %{vendor}/%{name}/packagename %{version}-%{release}
fi
wpi4rpm add %{vendor}/%{name}/packagename %{version}-%{release}

%postun
if [ "$1" -eq 0 ]; then # (upon removal)
    wpi4rpm del %{vendor}/%{name}/packagename %{version}-%{release}
fi

Note: The purpose of this utility is not to enable to uninstall rpm/yum packages using WarpIN, but only to fake the presence of a certain WarpIN package to the installation of other WarpIN packages. Removing the database entries using WarpIN is possible, while verification and deinstallation will fail.

Adding vendor and packagename is optional. In case they are not provided, they will be replaced with fixed values of wpi4rpm.

Using macros at install time

There is a group of OS/2-specific RPM macros that provide access to various aspects of the configuration of the OS/2 machine RPM is being run on. All these macros have names starting with os2_.

They are normal RPM macros and may be used wherever any other RPM macro does. There is, however, one important detail that should be taken into account when using OS/2-specific macros in .spec files — some of these macros depend on user-specific aspects of configuration.

A common case of such a dependency is the OS/2 boot drive letter. It may differ on a machine where the RPM package is being created and on a machine where it is later installed. The latter is up to the user and out of control of the RPM package creator. Normally all RPM macros are expanded at RPM creation time. For .spec sections that are run on the RPM creator's machine (such as %prep, %build, %install) it's not a problem as macro expansion happens at the same time the macro is used in the relevant section. However, for sections run on the user's machine at install time (%post, %postun etc) it is possible that the value of a macro expanded at package creation time will not match the value of the same macro when the package is later installed. Note that it may happen even if it's the same machine (e.g. the RPM creator changes its boot drive later).

A solution RPM provides to overcome this issue is secondary macro expansion. For this solution to work two prerequisites are necessary: a) the relevant %post, %postun or similar section must contain the -e option (to enable an extra expansion at install/uninstall time); b) the macro which is subject to secondary expansion should be prefixed with double % sign (to prevent it from being expanded at package creation time).

Here is the list of all OS/2-specific macros that depend on user-specific install-time configuration properties:

Macro Dependency
os2_boot_driveRefers to boot drive
os2_config_sysRefers to %{os2_boot_drive}
os2_expand_unixrootRefers to %UNIXROOT% env.var
os2_expand_dos_varsRefers to %MYVAR%-like env.vars
os2_unixroot_pathRefers to %{os2_expand_unixroot}

Also, all other macros which refer to one of the above macros in their definitions must be subject to secondary expansion too.

A good example of how to use such macros is provided in the next section.

CONFIG.SYS changes

It is sometimes necessary to modify the CONFIG.SYS file during installation, even when using RPM. One of the examples is when your package installs a device driver that needs to be loaded at boot time or when a global environment variable needs to be set. This can be done from the %post section using the special macro %cube which is based on the CUBE tool.

For example, this snippet will add a DEVICE= line to CONFIG.SYS after the first DEVICE= line (or to the bottom if there are no DEVICE= statements), add an environment variable (using the similar logic).

NOTE: Using %{os2_config_sys} below requires secondary macro expansion. See the previous section for more info.

%post -e
%cube {ADDLINE "DEVICE=%UNIXROOT%\usr\%{_lib}\mydrv.sys" (AFTER "DEVICE="} %%{os2_config_sys} >nul
%cube {ADDLINE "SET MYENVVAR=myvalue" (AFTER "SET "} %%{os2_config_sys} >nul
echo; echo "NOTE:"
echo; echo "The file '%%{os2_config_sys}' has been changed. You need to reboot your"
echo "computer in order to activate these changes."
echo

Here is the corresponding uninstallation snippet that will undo changes made by the above code. You should always perform symmetric uninstallation steps to make sure that all changes your package does to CONFIG.SYS are rolled back when the package is uninstalled from the system.

%postun -e
%cube {DELLINE "SET MYENVVAR="} %%{os2_config_sys} >nul
%cube {DELLINE "DEVICE=%UNIXROOT%\usr\%{_lib}\mydrv.sys"} %%{os2_config_sys} >nul
echo; echo "NOTE:"
echo; echo "The file '%%{os2_config_sys}' has been changed. You need to reboot your"
echo "computer in order to activate these changes."
echo

The complete CUBE documentation is available here: http://www3.sympatico.ca/gjarviseng/cube/cube.html