Linux#

Packaging#

RPMs#

RPMs are the packages used by Redhat systems (so in our case usually CentOS). The acronym RPM orignially came from Red Hat Package Manager.

Most of the information is taken from the RPM Packaging Guide, that you should consult for more details, examples, explanations, etc.

There are two kinds of rpms: binary and source. A source binary contains the source code of a package and the spec file to build a binary package from it. Binary packages contain the compiled binaries or libraries, scripts, and if neccessary include files along with sometimes configuration files (pkgconfig, cmake, …) and a license.

Very often different binary packages are built, one for general use, one for development (usually package-devel) and one with debug information. Rpmbuild will build the debug one automatically, if everything is set up correctly, the devel part has to be specified.

If possible you should use a new machine for building the package, so that you don’t forget any dependencies.

There you need to install for CentOS 8:

dnf install gcc rpm-build rpm-devel rpmlint make python2 bash diffutils patch rpmdevtools

and for CentOS 7:

yum install gcc rpm-build rpm-devel rpmlint make python bash coreutils diffutils patch rpmdevtools

After installing everything you need it is advised to switch to a non-root user for security reasons.

As this user you can setup the needed directory structure by calling

rpmdev-setuptree

This will create an rpmbuild directory in your home with the following subdirectories.

  • BUILD: This is where rpmbuild will build your application

  • RPMS: This will contain the binary rpms when they are built

  • SOURCE: Directory for the tar packed source code

  • SPEC: Directory for the spec files that contain all information on a package

  • SRPMS: This will contain the source rpms

The spec file#

Each package you want to create needs a spec file called package-name.spec in the SPEC directory. As soon as you create such a file, it will be filled with basic boilerplate.

This file consists of different sections.

First, basic information is filled in, that can be later used as variables. Then you can specify commands that should prepare, build and install the application.

Finally you need to specify the files that should be added to the package.

In case anything goes wrong you can see the created files of the make install directive in the BUILDROOT directory for comparison. They will appear there in the same structure as they will on the system once you installed the package.

For more information and examples on the spec files, see the RPM Packaging Guide, as mentioned above.

If you want to build a normal and a devel package you can add additional subpackage sections like %package devel. These can have different Summary and Requires fields, and it will need an own %files devel section.

The nice thing with package managers is, that all dependencies you added to the spec file will be automatically installed, if you install you newly built package with yum or dnf on any machine. Of course that also means that you have to take care to list all needed packages in the Requires: field!

Building it#

Of course you need the source code to build the application. You can either pack it yourself into a tar.gz archive or if you have a link to it add this link to the Source0 field in the spec file and download it via

spectool -g -R package.spec

Either way, once you have added the tarball to the SOURCES directory you can build both source and binary packages with

rpmbuild -ba package.spec

(-bb will build only binary, -bs only source package)

And that’s it! This will create packages based on the information you gave in the spec file. The name of the package will usually be {packagename}-{version}-{rpmrelease}.{distro}.{arch}.rpm.

Keys and local repository#

Two more things you can do to make everything more professional are signing your package and creating a local repository for all your packages.

For signing a package you need to create a GPG key and give the --signed argument to rpmbuild. This will help making sure, that the package people are installing is definitely from you and has not been swapped with malicious software.

The local repo does not make sense for single packages or single machines, but rather if you have a lot of packages, that will be installed to several machines and updated regularly.

In the end you create a file specifying the local folder containing all the packages you want to be able to install and add this to the repository lists of yum/dnf.

For more information on both of these approaches: www.google.com

A working spec file for building user, devlopment and debuginfo packages of spdlog looks like this:

Name:       spdlog
Version:    1.8.0
Release:    1%{?dist}
Summary:    Very fast, header-only/compiled, C++ logging library.

License:    MIT
URL:        https://github.com/gabime/spdlog
Source0:    https://github.com/gabime/%{name}/archive/v%{version}.tar.gz

BuildRequires: cmake3,devtoolset-7-gcc,devtoolset-7-gcc-c++

%description
Very fast, header-only/compiled, C++ logging library.


%package devel
Summary:    Spdlog development files
Provides:   spdlog-devel

%description devel
Deveoplment files for Spdlog, a very fast, header-only/compiled, C++ logging library.


%prep
%setup -q


%build
cmake3 -DSPDLOG_BUILD_TESTS=OFF -DSPDLOG_BUILD_SHARED=ON -DCMAKE_INSTALL_PREFIX:PATH=/usr .


%install
%make_install


%files
%license LICENSE
/usr/lib64/libspdlog.so.*

%files devel
%license LICENSE
/usr/lib64/*
/usr/include/*


%changelog
* Thu Sep 03 2020 Sophie Wenzel-Teuber <sophie.wenzel-teuber@ichec.ie> - v1.8.0
- First spdlog package

DEB packages#

The basic dependencies for the steps in this tutorial are:

apt-get update
apt-get install build-essential debhelper dh-make

The approach is to create a source package first, and then build the binary from it. To do that you need to add a debian directory to the source, create a few files in there and make sure that everything has the correct names.

There are lots of different scripts and packages to help you in the process. It seems to be a rabbit hole in itself to look through them and compare them. The guide mentioned above is mainly using debmake, but later on also dh, which is what I will be using in the following.

As far as I understood the differences are more because of historic reasons, the typical approach to introducing something new to make everything easier, that usually ends up in just confusing people who are new to the whole process.

To make everything a bit clearer, I will add examples for building a spdlog package.

Download the upstream source#

The original code you want to package is called the “upstream source”, assuming that you might want to make changes to it, that are needed for the package (like the debian folder), ending up at the real source.

Download the source as a tar.gz archive and unpack it, or create one from a source directory. Either way you need to change the names in order to end up with an unpacked directory called package-version and an archive package_version.orig.tar.gz

wget https://github.com/gabime/spdlog/archive/v1.8.0.tar.gz
tar -zxvf v1.8.0.tar.gz
mv v1.8.0.tar.gz spdlog_1.8.0.orig.tar.gz
# unpacked directory spdlog-1.8.0 already has the correct name

Make sure that the - and _ are correct, because yes, it does make a difference.

Prepare packaging files#

To create the additional files needed to create a package a very easy and seemingly accepted approach is to call dh_make in you new source directory (the unpacked one from before with the -). You should run this as a normal user instead of root. It will also use the variable $LOGNAME for filling in information about who is building that package. In this case we are building a library, so choose l when asked.

This command will create a debian subdirectory with a lot of files. In this example most of them are filled out fine per default, and there only four files that you always need to update:

  • control

  • rules

  • copyright

  • changelog

debian/changelog#

The whole process assumes that you will make changes to the original code before packaging it. This is contained in the changelog file.

If you close a bug, you can add the bug number in this file and it will autmoatically mark this bug as closed, but I assume we will harldy use that.

The changelog file for spdlog looks like this:

spdlog (1.8.0-1) unstable; urgency=medium

  * Initial release (Closes: #nnnn)  <nnnn is the bug number of your ITP>

 -- unknown <builder_monkey@unknown>  Tue, 08 Sep 2020 12:52:33 +0000

which should be fine for now.

debian/control#

The control file contains metadata to your package. Of course you will want to add lots of information there along with nice descriptions once you really get into it, but for now this should suffice:

Source: spdlog
Priority: optional
Maintainer: unknown <builder_monkey@unknown>
Build-Depends: cmake,g++,debhelper (>= 11)
Standards-Version: 4.1.3
Section: libs
Homepage: https://github.com/gabime/spdlog
#Vcs-Browser: https://salsa.debian.org/debian/spdlog
#Vcs-Git: https://salsa.debian.org/debian/spdlog.git

Package: spdlog-custom-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: spdlog-custom (= ${binary:Version}), ${misc:Depends}
Description: <insert up to 60 chars description>
 <insert long description, indented with spaces>

Package: spdlog-custom
Architecture: any
Multi-Arch: same
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: <insert up to 60 chars description>
 <insert long description, indented with spaces>

I only added build requirements, the homepage and changed the package names compared to the the automatically created file.

The architecture is set to any in this case, which is good: Any means that it has to be build for a specific architecture, whereas all would be independent (ie a package consisting of bash scripts).

debian/rules#

For what I assume are again historic reasons, rules is a Makefile and will contain all steps to build your package. When you look at the generated file though, it will more or less just contain two lines:

%:
	dh $@

The magical dh is a master command for a whole chain of debhelper commands. If you want to know which commands this entails have a look at the manpage debhelper(7).

This command chain that is executed by dh constist of commands, all starting with dh_ to configure, build and install whatever the package should contain.

In the spdlog case, the Makefile works as is, magically finding cmake, running and building it. However, you might want to customise certain steps of this process (ie additional cmake flags). For that purpose, each step can be overridden in the rules file.

For example the dh_auto_configure part can be changed by setting override_dh_auto_configure like this:

override_dh_auto_configure:
    dh_auto_configure -- \
        -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) \
        -DSPDLOG_BUILD_TESTS=OFF \
        -DSPDLOG_BUILD_SHARED=ON \
        -DCMAKE_INSTALL_PREFIX:PATH=/usr 

Building the package#

As with every step of this tutorial, there are several ways to build the package in the end.

The ones that work fine with the steps explained above are debuild and dpkg-buildpackage.

Running dpkg-buildpackage -us -uc in the source package will create package files in the upper directory. The us and uc flags tell the comamnd not to sign the source or the changes. If you are developing decent software and have great plans for it, you should sign it of course.

What we end up with is:

  • spdlog-1.8.0: the source directory we were working in

  • spdlog_1.8.0-1.dsc: a text file containing the metadata along with checksums

  • spdlog-custom-dev_1.8.0-1_amd64.deb: the package for development

  • spdlog_1.8.0-1_amd64.buildinfo: Information on metadata and the build environment, including all systemwide installed packages

  • spdlog-custom_1.8.0-1_amd64.deb: the main package

  • spdlog_1.8.0-1_amd64.changes: And another file with metadata, also including a list of files and the changelog.

  • spdlog_1.8.0-1.debian.tar.xz: The archived files with all changes for debian (so basically the source package)

  • spdlog_1.8.0.orig.tar.gz: The original, “upstream” source that we created/downloaded before

Most of them are only used for uploading the package to an official repository and I assume they can be ignored otherwise.

Now you can install the newly created package with

apt install ./spdlog-custom_1.8.0-1_amd64.deb

Additional tips#

If you want to look at the debian/* files of a working package, you can download the source of that package with

apt-get source package-name

After adding deb-src http://deb.debian.org/debian buster main or similar to /etc/apt/sources.list.

This will create a directory with all the contents needed to create the package. The metadata information like requirements, etc can be found in the .dsc file.