wiki:RpmHowToPackagers

Version 18 (modified by dmik, 13 years ago) (diff)

Change cp -Rdp to cp -dp

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
yum install tar

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.

Instead of writing a .spec file from scratch, you can get an existing one: a good source for them is the RPM find service. Enter the name of your package, and choose a distribution. Usually packages for Fedora or OpenSUSE 11.3 are good. 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.

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:

  • name:
  • version:
  • license:
  • source:
  • source1:
  • patch0:
  • patch1:

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.

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 expectes 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.

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
%defattr(-,root,root,-)
%{_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, -bl are performing also previous steps, you can add the special option --short-circuit: this will skip all previous steps (rpmbuild will assume they are already ok), and do only the selected task.

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

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) BuildRequire: 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 DLLs or packages: Unix rpm does this analyzing the ELF binaries and adding the required DLLs on the fly; the same is done for python or perl scripts, the required abi level is added 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 if someone needs that).

  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.

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).

%post 
%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 
%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