# Packaging software for RPM-based distros

Welcome to the world of [RPM](https://rpm.org/)s! The RPM Package Manager is a powerful package management system utilized by modern Linux distros such as [Fedora](https://getfedora.org/) and [OpenSUSE](https://www.opensuse.org/). Despite the vast repositories of RPM packages readily available, you may desire to use some software so niche or novel that it requires packaging by your very own hand, so let's learn how! I wrote previously about my project, [toolboxcutter](https://twiddlingbits.net/container-based-development-with-toolboxcutter), which facilitates the creation of RPM packages using the nifty [rpkg](https://docs.pagure.org/rpkg/) tool, and here I will describe how I use **toolboxcutter** to test RPM SPEC files locally before turning them into distributable RPM packages using the Fedora project's [Copr](https://copr.fedorainfracloud.org/) build system. ✨

# Writing SPEC files

RPM packages are produced using SPEC files as input, which specify the build and installation processes of some software in addition to metadata such as its name and version. Let's look at [Clight](https://github.com/FedeDP/Clight) as a case study. Here's its SPEC file:

%[https://gist.github.com/jcrd/5a05c5dd8d73ecabc955aedf8df4691d] 

Given such a SPEC file, it's possible to generate an RPM package using the [rpmbuild](https://www.redhat.com/sysadmin/create-rpm-package) tool, and this is a valid way of going about it. However, I use **toolboxcutter** because it builds packages in a [podman](https://podman.io/) container, meaning:

* Build dependencies won't litter your base system
    
* You won't miss a dependency specification that is coincidentally already installed
    

I wrote about using **toolboxcutter** for this purpose [here](https://twiddlingbits.net/container-based-development-with-toolboxcutter#heading-building-rpm-packages)!

The general procedure begins like this:

```bash
git clone https://github.com/FedeDP/Clight
cd Clight
tb init rpkg-toolbox
```

This `tb` command seen above is **toolboxcutter**, and calling `init` will create and open a `Dockerfile.toolbox` file in this repo's directory, built from `rpkg-toolbox`, which is a local **podman** image with **rpkg** and other packages installed. This is its `Dockerfile`:

```plaintext
FROM registry.fedoraproject.org/fedora-toolbox:37

RUN dnf install -y neovim
RUN dnf install -y rpkg
RUN dnf install -y rpmdevtools
RUN dnf install -y zsh

RUN ln -s /usr/bin/nvim /usr/local/bin/vim

CMD /usr/bin/zsh
```

**toolboxcutter** will eventually build **Clight** in the container specified by the `Dockerfile` in its directory, so let's add the required build dependencies:

```plaintext
FROM rpkg-toolbox

RUN dnf install -y cmake
RUN dnf install -y g++
RUN dnf install -y systemd-devel
RUN dnf install -y popt-devel
RUN dnf install -y libconfig-devel
RUN dnf install -y gsl-devel
RUN dnf install -y dbus-devel
RUN dnf copr enable -y jcrd/libmodule
RUN dnf install -y libmodule
```

Thankfully, **Clight**'s dependencies are recorded in its documentation. For other software, some trial and error may be necessary to determine what's required. The next step is to specify these dependencies in the **rpkg** SPEC template file. **rpkg**'s [documentation](https://docs.pagure.org/rpkg-util/v3/quick_start.html) describes how to get started with such a template.

In the directory containing the to-be-packaged software's source, let's create a subdirectory and the **rpkg** SPEC template file:

```bash
mkdir spec
touch spec/clight.rpkg.spec
```

Now, edit the created file, filling in the SPEC template details. **Clight**'s looks like this:

%[https://gist.github.com/jcrd/45070e2b19d18d09255083683127fd95] 

As you can see, it resembles the final SPEC file but contains some **rpkg** macros enclosed in `{{{ }}}`. These allow the production of an RPM package directly from the source repository where this template resides. The installed files listed under the `%files` section may not be obvious without first attempting to build the package.

From outside the **toolbox** container, run this command:

```bash
tb rpkg-install
```

Without the proper installed files list, you will receive an error such as:

```plaintext
error: Installed (but unpackaged) file(s) found:
   /etc/default/clight.conf
   /usr/bin/clight
   /usr/etc/xdg/autostart/clight.desktop
   /usr/include/clight/public.h
   /usr/share/applications/clightc.desktop
   /usr/share/bash-completion/completions/clight
   /usr/share/clight/inhibit_bl.skel
   /usr/share/clight/nightmode.skel
   /usr/share/clight/synctemp_bumblebee.skel
   /usr/share/dbus-1/services/org.clight.clight.service
   /usr/share/icons/hicolor/scalable/apps/clight.svg
   /usr/share/man/man1/clight.1.gz
   /usr/share/zsh/site-functions/_clight
```

You can fill in the `%files` section with these paths using the appropriate [RPM macros](https://docs.fedoraproject.org/en-US/packaging-guidelines/RPMMacros/#macros_installation) as seen above. Rerunning **toolboxcutter**'s `rpkg-install` command should now produce and install an RPM package.

Finally, convert the **rpkg** SPEC template into a standalone spec file by replacing rpkg's `{{{ }}}` macros with their concrete values. **rpkg** can handle this automatically to an extent (with `rpkg spec`), with the caveat being: the package `Source` will be directly derived from the software's hosted source code repository, whereas you probably want to specify a source code archive associated with a released version of this software.

Compare the aforementioned SPEC file with the above **rpkg** SPEC template if you need help determining which values belong where. Once this is complete, you have a working SPEC file capable of producing an installable RPM package! 💪

# Building distributable RPM packages

With the SPEC file in hand, building a distributable RPM package is easy using [**Copr**](https://copr.fedorainfracloud.org/). It's similar in theory to [Ubuntu](https://ubuntu.com/)'s [ppas](https://launchpad.net/ubuntu/+ppas). You can find more information about the **Copr** build system [here](https://docs.pagure.org/copr.copr/user_documentation.html#faq). An alternative might be [OBS](https://openbuildservice.org/) (Open Build Service) but I have not yet used it myself.

[This tutorial](https://docs.pagure.org/copr.copr/screenshots_tutorial.html) illustrates the process of creating a new **Copr** project and initiating a build. However, instead of specifying SRPM files by URL, you can provide the URL of a SPEC file. I host my SPEC files on *gist.github.com* for revision tracking and ease of editing. Provide the URL to the raw `.spec` file, start the build, and if everything goes according to plan, you'll see that it succeeded! Check out **Clight**'s [project](https://copr.fedorainfracloud.org/coprs/jcrd/clight/) to see what this looks like.

Now, on your local machine, enable your newly created **Copr** repository and install the RPM package:

```bash
dnf copr enable jcrd/clight
dnf install Clight
```

Voilà! This package is now distributable: it can be installed on any system for which it was built by enabling its **Copr** repository. 👏

This pairs especially well with [reproducible distro configurations](https://twiddlingbits.net/on-reproducible-distro-configuration)!
