Building a Rust snap by Example

by Alan Pope on 13 December 2019

There’s plenty of official documentation which details how to create snaps. We live in the copy/paste generation though. So lets walk through a real-world example you can use as a template, or selectively yoink sections from.

This is the first in a series of posts which break down how to build snaps based on published works in the Snap Store. This week we’re looking at “System Syzygy” , a graphical puzzle game written using Rust and SDL2.

Metadata

Let’s start with the easy part, the human-readable bit. Here we set out the name of the snap as it would appear in the Snap Store web user interface, and on the command line with snap info syzygy. As the summary is displayed in the command line output of snap info it’s worth keeping it short and sweet. The description can be more meaty, covering details about the application, command line options, keyboard shortcuts and links to help.

Once published however, the summary and description can also be maintained via the Snap Store listing page, but this is a good starting point.

name: syzygy
summary: System Syzygy - A narrative meta-puzzle game
description: |
System Syzygy is a puzzle game, in the style of Cliff Johnson's
The Fool's Errand and 3 in Three, and of Andrew Plotkin's System's
Twilight..

The version number can often be seen either with a version: or version-script: line. However we have used adopt-info to inform snapcraft that the version will be set by a part, further down the yaml. The part name is specified. We’ll come back to this later.

adopt-info: syzygy

Base

The base indicates which runtime this snap should be run against. When the syzygy snap is installed on a system, the core18 base snap will be automatically installed too. Essentially this determines the version of libc the application was built against. The core18 base snap is built from Ubuntu 18.04 (LTS) and as such, we should build this snap in a VM or container running the same release.

Other (older) bases are available and in the future newer bases will be made, but today, this is an appropriate base to build with.

base: core18

Architectures

Both the snapcraft build service and launchpad have the capability to build snaps for numerous architectures. However, not all applications may successfully build or run on all of them. Sometimes the dependencies or build tools aren’t available for every CPU architecture, or perhaps the tool-chain is not supported on anything but mainstream architectures.

In the case of System Syzygy, it only builds for 64-bit and 32-bit Intel CPU architectures. Specifying the supported architectures here means the build systems won’t waste time attempting to compile the game on CPUs we know it won’t succeed on.

architectures:
- build-on: [amd64,i386]

Confinement

System Syzygy is a simple, non-networked game. It needs access to take input from the keyboard and mouse, display things on the screen, and play back audio. As such it can be strictly confined.

confinement: strict

Grade

Grade is an indicator of the quality of this snap. Initially we might set this to devel while it’s in development. However, in order to be published in the stable or candidate channels in the store, we should set this to stable. Only applications with a stable grade are permitted in the stable and candidate channels.

grade: stable

Parts

Parts are the meat of the snap. Here’s where we outline what actually goes inside the package. As System Syzygy is a game written in Rust, and is hosted externally on github, we can list the plugin and the url to the source repository here.

parts:
syzygy:
plugin: rust
source: https://github.com/mdsteele/syzygy.git

override-pull

Many applications are not written with relocatability in mind. The override-pull section may contain anything we might want to do, to manipulate the source code before building it.

System Syzygy makes an assumption about where to load some of the required libraries or assets. We can override this by simply patching the line in the source such that it now points into the directory the snap will be installed to. These are shell commands in a list.

The snapcraftctl pull statement instructs snapcraft to do whatever is required to obtain the source as defined by the plugin and source lines above. Then we patch the source files as necessary.

override-pull: |
  snapcraftctl pull
  sed -i 's|usr/lib/syzygy|/snap/syzygy/current|' src/gui/loader/path_linux.c

override-build

Most applications use standard build tools and common patterns for compiling and installing files. However sometimes they miss out steps we assume as part of a build. The developer may omit installation steps for example, assuming the user will simply run the application directly from the build folder.

In the case of System Syzygy, we need to copy the game assets (fonts, sprites and background images) into the right place. We use the build-time variable SNAPCRAFT_PART_INSTALL as our target. Later in the snapcraft lifecycle the contents of this folder will be assimilated into the final snap.

override-build: |
  snapcraftctl build
  mkdir $SNAPCRAFT_PART_INSTALL/data
  cp -a data/fonts data/sprites data/backgrounds $SNAPCRAFT_PART_INSTALL/data
  cp -a target/release/syzygy $SNAPCRAFT_PART_INSTALL/bin
  snapcraftctl set-version $(git -C ../src describe --tags  | sed 's/v//')

Finally we use git to extract the version number via git and assign that to the snap with the snapcraftctl statement. This bubbles up via the adopt-info line above to become the version number

build-packages

System Syzygy has a very small set of build dependencies outside those specified in the rust Cargo.toml. We list the packages which fulfil those build dependencies in the build-packages section of the part. These will be installed by snapcraft before the build starts.

build-packages:
  - libsdl2-dev

stage-packages

Once built, the game leverages a few libraries to draw on the screen and play audio. If we didn’t specify these, the game would not work even if the libraries existed on the host OS. The snap is strictly confined, so cannot see any external libraries, so we list them here to bundle them with the game.

We can discover which packages to add by running snapcraft without specifying any at all. Snapcraft will introspect the binaries shipped in the snap, and list a best-guess array of required packages.

stage-packages:
  - libglu1-mesa
  - libsdl2-2.0-0
  - libpulse0

Application

The apps stanza is where we setup the environment and expose the binary inside the snap to the outside world. This includes ensuring the binary can find the GL drivers required to paint the game window, and extend the library search path to include the pulseaudio libraries for audio playback.

In addition, the game has an internal environment variable which should point to the game data folder. We use the SNAP runtime environment variable to construct the correct path to the game files configured earlier.

Finally we specify the required plugs to enable the game to draw on the screen, play audio, access the GPU via opengl, and suppress the screensaver.

apps:
syzygy:
environment:
SHELL: bash
LC_ALL: C.UTF-8
SNAPCRAFT_ARCH_TRIPLET: ${SNAPCRAFT_ARCH_TRIPLET}
__EGL_VENDOR_LIBRARY_DIRS: $SNAP/etc/glvnd/egl_vendor.d:$SNAP/usr/share/glvnd/egl_vendor.d
LIBGL_DRIVERS_PATH: ${SNAP}/usr/lib/${SNAPCRAFT_ARCH_TRIPLET}/dri
LIBVA_DRIVERS_PATH: ${SNAP}/usr/lib/${SNAPCRAFT_ARCH_TRIPLET}/dri
LD_LIBRARY_PATH: "$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/pulseaudio"
SYZYGY_DATA_DIR: "$SNAP/data"
HOME: "$SNAP_USER_DATA"
XDG_CACHE_HOME: "$SNAP_USER_DATA/.cache"
command: syzygy
plugs:
- x11
- pulseaudio
- opengl
- screen-inhibit-control

Summary

Building and publishing snaps of applications written in Rust is pretty straightforward. The example above highlights how we can be flexible if the build system requires tweaking, or where an application needs minor patching. A non-graphical Rust app would be simpler than this example by virtue of not needing the SDL, GL and pulse libraries and environment configuration.

Building and distributing a game written in any language is mostly similar to what’s outlined above. Only the part definition changes, the environment and plugs configuration will be mostly common.

We welcome new games and applications in the Snap Store. The developers of snapd, snapcraft and the Snap Store hang out over on the snapcraft forum. Join us there if you have any questions or comments about this article or need assistance building a new snap.

Newsletter Signup

Related posts

Popular snaps per distro (2020 edition)

Back in mid-2019, we wrote a blog post detailing and comparing the most popular snaps across multiple distributions – Arch Linux, CentOS, Debian, Fedora, Manjaro, and Ubuntu. This article gave us a fascinating insight into the usage patterns across these different systems and their audiences. We wanted to see if and how the situation has […]

The Hunt for Rogue Time – How we investigated and solved the Chromium snap slow startup problem

In October, we shared a blog post detailing significant snap startup time improvements due to the use of a new compression algorithm. In that article, we focused on the cold and hot application startups, but we did not delve much into the first-run setup scenario. While running our tests, we observed a rather interesting phenomenon, […]

Introducing etrace – a multi-purpose application profiling tool

These days, the internal workings of Linux applications involve many different moving parts. Sometimes, it can be rather difficult to debug them when things go wrong or run slower than expected. Tracing an application’s execution is one way of understanding potential issues without diving into the source code. To this end, we wrote an app […]