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

Snapcraft:多应用客户端-服务端snap开发教程

在过去几个月我们发布了一些如何使用Rust,Java,C/C++和其他语言来开发snap桌面应用的文章。在这些从入门到精通的教程中,我们以一个代表性的snapcraft.yaml文件来介绍开发构建snap所需的具体细节。 今天,我们希望脱离这一过程,而将重点放在服务器端。我们将为你提供一个包含两个有趣组件的snapcraft.yaml的概述:a)它将拥有多个应用程序; 通常,snap包含一个应用程序。b)它具有简单的后台服务,其他应用程序可以连接到该服务。 我们一起来看一下。 Snapcraft yaml 以下是snapcraft.yaml文件的内容: apps: borg: command: bin/borg daemon: simple restart-conditi […]

Learn snapcraft by example – multi-app client-server snap

Over the past few months, we published a number of articles showing how to snap desktop applications written in different languages – Rust, Java, C/C++, and others. In each one of these zero-to-hero guides, we went through a representative snapcraft.yaml file and highlighted the specific bits and pieces developers need to successfully bui […]

Snapcraft tricks: Improve release flexibility with pull and build overrides

Sometimes, software projects are simple – one architecture, one version, one release. But often, they can be complex, targeting multiple platforms, and with different versions at that. If you are packaging your apps as snaps, you might wonder about the optimal way to accommodate a multi-dimensional release matrix. One yaml to rule them al […]