Packaging Freeciv21

Following our release cadence process, we provide a collection of operating system (OS) specific packages for Freeciv21. More information can also be found at Installing Freeciv21.

We maintain two versions for Freeciv21: Stable and Development. The Stable version is contained in the stable branch on GitHub. The Development version is maintained in the master branch on GitHub.

Libraries

As with most software, we rely on some libraries. Stable relies on Qt5 and KDE Frameworks 5. However, both are no longer supported. KDE is only doing security patches to Frameworks 5 and Plasma 5 (KDEs Desktop Environment), to support Long Term Support (LTS) Linux OS’s such as Ubuntu 24.04 LTS.

Development relies on Qt6 and KDE Frameworks 6. Both are in mainline support and actively development. However, only very new Linux distributions have the KDE Frameworks 6 libraries included. Stability was too late to be included in Ubuntu 24.04 LTS. This makes packaging for master branch a bit more complicated.

There are other libraries we rely on as as seen in Compiling Freeciv21, but the ones from Qt and KDE impact packaging more than the others.

Here are some useful Qt and KDE Links:

We provide native packages via GitHub Actions for the following platforms:

  • Debian and its variants via CMake’s CPack system.

  • Microsoft Windows® via MSYS2 emulation and CMake’s CPack system with NSIS integration.

  • Apple macOS® via vcpkg and a utility called create-dmg to create a compressed application bundle.

The Debian and Windows packages are sourced from CMake CPack configuration here: cmake/CPackConfig.cmake

The macOS package is built inside GitHub Actions. You can refer to .github/workflows/build.yaml. Look for the macOS build step.

Outside of the GitHub Actions, we provide native packages for the following platforms:

  • Linux containerized packages for snap and flatpak. The Ubuntu world favors snap and the Fedora/Red Hat world favors flatpak.

  • Native .deb and .rpm packages for many Linux distributions are built using the openSUSE Build Service (OBS).

Note

In the future we will remove the CPack generated Debian package and only support OBS. We are in a transition period at the moment.

As noted earlier, GitHub Actions uses the current Ubuntu LTS as the base image. You can add newer operating system editions via a Docker container. We do this on master branch for the Debian package at this time. MSYS2 has packages for all the Qt6 and KDE Frameworks 6 components we need. Since macOS relies on the vcpkg package manager, it is not dependent on the base image.

CPack Debian Package

The CPack Debian package is straightforward. A .deb package is effectively a compressed archive with some configuration settings in plain text files. More information is available at the Debian Wiki: https://wiki.debian.org/Packaging

CPack does all the work. We simply tell the packaging tool what our specific needs are and the tool does all the rest. As long as we can compile Freeciv21 with the required libraries we can produce a Debian package. Given this, we leverage a feature of GitHub Actions to use a more recent edition of Ubuntu inside of a Docker container for the master branch. We use native Ubuntu 24.04 LTS for the stable branch.

CPack Windows Package

The CPack Windows package is a bit more complicated. It requires Windows, the MSYS2 Linux emulation platform, and a packaging tool called NSIS. More information on NSIS is available at: https://nsis.sourceforge.io/Docs/

CMake’s CPack extension natively supports NSIS packages. As with Debian, we use the CPack configuration file to define the base parameters of the installation. Lastly we have a template file cmake/NSIS.template.in that CPack uses to build the installer.

We also include some files to improve support for Windows:

  • dist/windows/freeciv21-server.cmd: The server needs some environment variables set to run correctly on Windows, so we include with the package.

  • dist/windows/qt.conf: This file sets some Qt parameters to aid font display and other UI/UX elements so Freeciv21 looks its best on Windows.

macOS Package

Apple uses a compressed file called an “Application Bundle” to install applications. CMake CPack does support building a bundle natively, however with some caveats that makes it hard for us to use. Given this, we rely on a tool provided by the homebrew macOS packaging system to do it ourselves.

The main build steps are noted above. A key file in establishing the macOS package is dist/Info.plist.in. This is an XML file that defines key components of Freeciv21 and is incorporated into the package at build time along with create-dmg.

The main difference between Stable and Development is Stable manually creates the application bundle in the GitHub action. In Development we have incorporated the generation of the application bundle in the --target install step, so the GitHub action only has to create the .dmg installer.

Snap Package

Source File: dist/snapcraft.yaml

Snap packages are generated via a tool called snapcraft. It is actually a snap package itself. Snapcraft relies on a virtual machine emulation system called lxd. The lxd system relies on another snap package called a base. A base is a version of Ubuntu LTS.

Stable

For stable we are using the base core22, which is Ubuntu 22.04 LTS. It is a bit old, but provides the package library stability we expect for our stable releases.

Development

For Development we are using the base core24, which is Ubuntu 24.04. Since Ubuntu 24.04 LTS does not have the newer KDE Frameworks 6 libraries we need, we source from a snap that KDE maintains: kf6-core24.

Since the whole snapcraft setup is itself an emulation layer, you cannot build snaps inside of a Docker container. This means we cannot use the same process that we use for the Debian package, which is fine for us, as we get everything we need from the team over at KDE.

Snap Notes, Tips and Tricks

The straightforward build steps are like this. Let us start with installing the base packages we need:

$ sudo apt install snapd
$ sudo ln -s /var/lib/snapd/snap /snap
$ sudo snap install lxd
$ sudo lxd init --auto
$ sudo snap install snapcraft --classic

Here are the build-time steps to create the snap package:

$ mkdir -p build/snap/local
$ cp data/icons/128x128/freeciv21-client.png build/snap/local
$ cp dist/snapcraft.yaml build/snap
$ cd build
$ sudo snapcraft
$ sudo snap install --devmode ./freeciv21_*.snap
$ sudo snap remove --purge freeciv21

You may run into trouble when running snapcraft to build the package. Here are some notes:

  • If you get an error that snapcraft cannot connect to a network, this is coming from lxd. The quickest work around is to turn off your firewall: sudo systemctl stop firewalld. Obviously you will want to do this on a well known network and not in a coffee shop. Enable the firewall when finished with systemctl start.

  • If you get an error in one of the phases, you can open a shell to the lxd container either before or after the stage: sudo snapcraft stage --shell or sudo snapcraft stage --shell-after. This allows you to look at where files are being stored (in this example the stage step), evaluate environment variables and generally try to troubleshoot any error messages you are getting.

  • If you are doing a lot of runs of snapcraft during development, it is good to start with a clean state. You can clean the environment with: sudo snapcraft clean.

  • You can run the snap in a shell as well to troubleshoot with snap run --shell freeciv21.freeciv21-client.

As it relates to Natural Language Support (NLS), snap has some challenges. In the Stable environment, NLS is not enabled and is not well supported by either the core22 or core24 bases alone. The KDE provided kf5-core22 or kf5-core24 build snaps attempt to get NLS working, but there are upstream bugs in the setup of a snap launch system called a command-chain. The good news is this is all fixed in the Development side in the master branch. With the core24 base and the KDE provided kf6-core24 build snap, NLS works as expected. The Freeciv21 developers will keep and eye on the Stable side to see if any upstream improvements are pushed periodically to see if we can get NLS to work there.

Lastly, snapcraft has a utility to upload a generated snap package to the snap store automatically. We use this capability in our .github/workflows/release.yaml using a GitHub action called snapcore/action-publish. Snapcraft uses an authentication mechanism called a store credential to authenticate and upload a package. The credential expires periodically. Run this command to generate a new one: snapcraft export-login -. After entering our email address and password, snapcraft will output the credential to stdout and it can be copied into our GitHub STORE_SECRETS secret.

Flatpak Package

The Flatpak source system resides in a separate GitHub repository: net.longturn.freeciv21, which is a fork of the main package at https://github.com/flathub/net.longturn.freeciv21.

We currently only support the stable branch with Flatpak. There are plans to support master with Flathub beta. See: https://github.com/longturn/freeciv21/issues/2513.

Setting up development for Flatpak is relatively straightforward:

$ git clone --depth 1 --recursive git@github.com:longturn/net.longturn.freeciv21.git flatpak
$ cd flatpak
$ git remote add upstream https://github.com/flathub/net.longturn.freeciv21
$ git fetch upstream
$ git pull upstream master
$ git submodule update --recursive --remote

Note

We use the --depth 1 --recursive command with git because we rely on a shared module for Lua support. This is also why we have the git submodule command in the steps.

Now you can edit the net.longturn.freeciv21.yaml configuration file. Let us start with installing the base packages we need:

$ sudo apt install flatpak-builder
$ sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo

Here are the build-time steps to create the flatpak package:

$ flatpak-builder --install-deps-from=flathub --force-clean build net.longturn.freeciv21.yaml
$ flatpak-builder --user --install --force-clean build net.longturn.freeciv21.yaml
$ flatpak run net.longturn.freeciv21
$ flatpak remove net.longturn.freeciv21

As with the snap package, flatpak packages rely on some platform dependencies, which are also flatpak packages. For flatpak we use a runtime that matches the run time environment for the application. The Stable version of Freeciv21 needs Qt5, so we leverage the 5.15-24.08 runtime provided by KDE, which is Qt v5.15 and built in August of 2024. We also need an SDK (Software Development Kit). As you would expect, we use the org.kde.Sdk SDK.

Flathub will build the package from source by grabbing it from our GitHub repository. Near the bottom of the configuration file you will see where we tag a git commit ID, which should correspond to the tagged release of a stable branch Git Tag.

Flatpak Notes, Tips and Tricks

  • Due to the way that Flathub “hosts” packages, with each in their own repository, it makes updating the package a bit interesting. Our Longturn repository fork is origin in your local and the Flathub repository is upstream. When you update the package you follow a similar pattern like updating code in the main Freeciv21 repository. You push changes to origin and then open a Pull Request (PR) in upstream. When you open a PR in upstream you will be asked some questions. Answer them and then submit. The flathubbot will automatically run. If the build is successful, then you can merge it.

  • Every downloaded file has to have a hash (sha256) associated with it. If you get stuck and do not know how to get the hash, then run the flatpak-builder command in your local. The process will error out and give you the hash that was expected. You can then edit the configuration with the proper hash. Be sure you are grabbing files from raw.githubusercontent.com or tagged package files from a Releases page.

  • The KDE runtime we use is hosted here: https://invent.kde.org/packaging/flatpak-kde-runtime. You can look at the list of protected branches to determine the correct version to use.

openSUSE Build Service (OBS)

Source Files: Refer to .obs/ in our GitHub repository.

OBS is a package building service hosted by the folks over at openSUSE. It gives us the ability to support packages for multiple distributions for both the .deb and .rpm worlds.

As of this writing, the stable OBS repository only supports the Stable release and all of the source files are hosted at OBS. Support for the Development edition is on our sandbox at this time. The master branch hosts the files that OBS needs in the .obs/ directory as noted above.

Both versions of our package rely on an OBS hosted file named _service. This is an XML file telling OBS how to build the package. The file on the Stable repository is very simple, just download the tarball and build. The Development version of the file is more complex as we rely on some GitHub integration tools: obs_scm, tar, recompress, and set_version. All are hosted in OBS at the openSUSE:Tools project.

Stable Support

The Stable package is supported on the following distributions:

  • Debian 12 Bookworm LTS

  • Debian 13 Trixie LTS

  • Debian Testing (future 14 Forky)

  • Debian Unstable (Sid)

  • Fedora 42

  • Fedora 43

  • Fedora Rawhide

  • openSUSE Tumbleweed

  • Ubuntu 22.04 LTS

  • Ubuntu 24.04 LTS

  • Ubuntu 24.10

  • Ubuntu 25.04

Development Support

The Development package is supported on the following distributions:

  • Debian 13 Trixie LTS

  • Debian Testing (future 14 Forky)

  • Debian Unstable (Sid)

  • Fedora 41

  • Fedora 42

  • Fedora Rawhide

  • openSUSE Tumbleweed

  • Ubuntu 24.10

  • Ubuntu 25.04

DEB Packages

The .deb packaging system relies on the following files:

  • debian.changelog — A list of changes between versions.

  • debian.control — The package control file with list of build and runtime dependencies.

  • debian.rules — You can override the debhelper system with this file.

  • freeciv21.dsc — The Debian Source Control file.

The freeciv21.dsc controls the whole process. For Stable we have code in the file telling the system where to get the source tar.gz. For Development we use an OBS service integration called obs_scm that pulls the code from our GitHub repository. The tar and recompress services builds a tar.gz automatically. Lastly the set_version service helps us with auto versioning.

To update the package on Stable:

  1. Edit debian.changelog and add a new version to the top.

  2. Edit freeciv21.dsc with a new Version and update the Files and DebTransform-Tar sections with a new tarball from our releases page on GitHub.

RPM Packages

The .rpm packaging system relies on the following files:

  • freeciv21.changes — A list of changes between versions.

  • freeciv21.spec — The control file for the rpmbuild system. One file can support multiple .rpm based distributions.

The freeciv21.spec controls the whole process. On Stable it relies on what we have in the freeciv21.dsc to know where to get the tarball for compilation. For Development we use the same system as for the .deb packages.

To update the package on Stable:

  1. Ensure you have updated the freeciv21.dsc as noted in the DEB section above.

  2. Edit freeciv21.spec with a new version.

Notes on Usage of Microsoft® VCPKG

As noted above, we use the Microsoft® open source package manager vcpkg. This is a cross-platform, library package manager and is hosted on GitHub at: https://github.com/microsoft/vcpkg.

True to form, Microsoft has extensive documentation on vcpkg at: https://learn.microsoft.com/en-us/vcpkg/.

We utilize vcpkg in manifest mode (versus native install mode). The manifest file is vcpkg.json and is in the root of the Freeciv21 source code respository. Manifest mode is enabled by adding an operating system environment variable (VCPKG_ROOT) and a custom CMake settings to the project’s root CMakeLists.txt file (FREECIV_USE_VCPKG). When VCPKG_ROOT is set to a path of the cloned repository and FREECIV_USE_VCPKG is set to ON, then our build system will invoke vcpkg to obtain the required dependencies we need to compile.

Notes specific to how Freeciv21 uses vcpkg

Inside our Continuous Integration (CI) system, we run a GitHub action called lukka/run-vcpkg. This action automatically clones and sets up a base vcpkg setup in the build environment. We pin the binary using a variable called vcpkgGitCommitId, which is the git hash of a relatively recent vcpkg release. You can see it referenced as part of the release cadence. We do this to ensure that we run a relatively recent binary to handle all the dependency processing from the manifest file correctly. The hash can be updated at any time and does not have to follow the release cadence for updates.

Inside of the manifest file, you will see another git hash noted as builtin-baseline. This hash is a commit ID related to the version of a set of packages that we need. Consider it a pin to a set of package versions. Over time, the many developers that contribute to the main vcpkg repository will update and change packages. Many times this is to update the version of a known package, and of course, to fix bugs.

Troubleshooting vcpkg failures

As noted in the section above, vcpkg gets updated constantly. We help ourselves keep up to date by keeping the tagged vcpkgGitCommitId up to date, but invariably something will happen that breaks the build.

If a build fails, start by looking at the logs to see if its obvious what the issue is. Things like a source package site outage can cause failures and can be ignored. If the failure looks like a core build problem (e.g. a package compilation is failing) you might need to check how old the vcpkgGitCommitId hash is set in the CI. If it is recent, then the next thing to do is grab the lastest builtin-baseline and update the manifest file. The easiest way to do that is run this from the root of the Freeciv21 repository:

$ path/to/vcpkg x-update-baseline --add-initial-baseline

This will alter the manifest file. Run a local test and see what happens. Most likely you will run into version mismatch issues with the current package versions and our own requirements.

Some things to consider:

  • The stable branch requires a minimum of Qt 5.15.18#2. The master branch is not as picky and simply requires a current Qt 6.x.

  • Both branches must have a version of the Lua library that is less than v5.5. Anything in the v5.4.x range is acceptible. We are stuck at this older version due to dependencies in the scriptcore and sol dependency.

Microsoft provides a means to pin the manifest to a specific package version or a minimum version. You can find documentation here: https://learn.microsoft.com/en-us/vcpkg/consume/lock-package-versions.

Note

This is where the difference between the pinned vcpkgGitCommitId and builtin-baseline becomes important. You must have a pinned vcpkgGitCommitId that is newer than the pinned builtin-baseline so vcpkg knows how to build the dependency tree.

If you pin package versions, to a minimum or a specific version, you can find the appropriate builtin-baseline by running a modified version of the command above. From the root of the Freeciv21 respository run:

$ path/to/vcpkg x-update-baseline

This command will find the appropriate baseline based on the versions you have pinned in the manifest.