The snapcraft CLI (the tool used to create snaps) has long had support for building snaps that use both ROS 1 and ROS 2. ROS 2 Foxy Fitzroy is the latest ROS 2 LTS, which runs on Ubuntu 20.04 (Focal Fossa). The snapcraft CLI recently gained experimental support for building Foxy snaps, so I wanted to walk you through doing exactly that with the goal of helping both of us: getting you familiar with this new feature, and at the same time getting some mileage on this so as to make it not experimental. Let’s get to it!
In order to follow along, you’ll need at least v4.2 of the snapcraft CLI:
$ sudo snap install --classic snapcraft
Of course, some previous experience building a snap will also be helpful.
Let’s get started
The first step toward creating any snap is to create the
snapcraft.yaml, which serves as the recipe instructing the snapcraft CLI on how to piece together your snap.
Create the snapcraft.yaml
Let’s create a new directory, and then initialize it with a
$ mkdir ~/ros2-foxy-snap $ cd ~/ros2-foxy-snap $ snapcraft init Created snap/snapcraft.yaml. Go to https://docs.snapcraft.io/the-snapcraft-format/8337 for more information about the snapcraft.yaml format.
snap/snapcraft.yaml file, and make it look like this:
name: ros2-talker-listener version: '0.1' summary: ROS2 Talker/Listener Example description: | This example launches a ROS2 talker and listener. grade: devel confinement: strict base: core20 parts: ros-demos: plugin: colcon source: https://github.com/ros2/demos.git source-branch: foxy source-subdir: demo_nodes_cpp build-packages: [make, gcc, g++] stage-packages: [ros-foxy-ros2launch] apps: ros2-talker-listener: command: opt/ros/foxy/bin/ros2 launch demo_nodes_cpp talker_listener.launch.py plugs: [network, network-bind] extensions: [ros2-foxy]
Let’s break that down by section.
name: ros2-talker-listener version: '0.1' summary: ROS2 Talker/Listener Example description: | This example launches a ROS2 talker and listener.
This is the basic metadata required by all snaps. These fields are fairly self-explanatory, but note that the
name must be globally unique among all snaps. You might consider appending your developer name to the end of the snap name, for example.
grade: devel confinement: strict base: core20
grade can be either
devel. If it’s
devel, the store will prevent you from releasing into one of the two stable channels (
candidate, specifically). If it’s
stable, you can release it anywhere.
confinement can be
strict enforces confinement, whereas
devmode allows all accesses, even those that would be disallowed under
strict confinement, and logs access that would be disallowed.
classic is even less confined than
devmode in that it doesn’t even get private namespaces anymore (among other things). There is more extensive documentation on confinement available.
As I’ve said multiple times in the past, I typically use
strict confinement unless I know for sure that the thing I’m snapping won’t run successfully under confinement, in which case I’ll use
devmode. I typically avoid
classic unless I never intend for the snap to run confined (e.g. you’ll notice the snapcraft CLI is a classic snap, since it needs more access to the host than confinement would allow).
base keyword specifies a special kind of snap that provides a minimal set of libraries common to most applications (e.g. libc). It will be the root filesystem for this snap. In this case, we’re using
core20 which is a minimal rootfs based upon Ubuntu 20.04 (Focal).
parts: ros-demos: plugin: colcon source: https://github.com/ros2/demos.git source-branch: foxy source-subdir: demo_nodes_cpp build-packages: [make, gcc, g++] stage-packages: [ros-foxy-ros2launch]
The snapcraft CLI is responsible for taking many disparate parts and orchestrating them all into one cohesive snap. You tell it the parts that make up your snap, and it takes care of the rest. Here, we’re saying that we have a single part called
ros-demos. We specify that it builds using the colcon plugin, and we point it to the ROS 2 demos GitHub repository (this could just as easily be a directory on disk). We point the colcon plugin at the subdirectory containing the C++ demo nodes as opposed to letting it build the whole thing. We also provide a list of packages that need to be installed in order to build (
build-packages), and also ask that
ros-foxy-ros2launch gets staged into the snap alongside the rest of the part to be used at runtime (specifically, we’ll use it in the app, below). To view all the options supported by the
colcon plugin, run the command
snapcraft help colcon.
apps: ros2-talker-listener: command: opt/ros/foxy/bin/ros2 launch demo_nodes_cpp talker_listener.launch.py plugs: [network, network-bind] extensions: [ros2-foxy]
This part is interesting, and even if you’ve built snaps in the past there might be something new here. When we build this snap, it will include a complete ROS 2 system:
demo_nodes_cpp workspace, etc. It could contain the entire system necessary for a robot in one installable blob. It’s a standalone unit: we’re in total control of how we want our users to interact with it. We exercise that control via the
apps keyword, where we expose specific commands to the user. Here we specify that this snap has a single app called
ros2-talker-listener (the same name as the snap itself), although it could be anything. The command that this app actually runs within the snap uses the
ros-foxy-ros2launch that we staged to fire up the demo nodes' talker/listener launch file. We use
plugs to specify that this app requires network access (read more about interfaces). Finally, the new bit: we specify that this uses the
ros2-foxy extension. This is where a significant chunk of the magic happens, wrapping the app in the environment necessary to run ROS 2 Foxy stuff, etc. Every app that needs ROS 2 will need to use that extension, and those that don’t need ROS 2 don’t need the extension (read more about snapcraft extensions).
Build the snap
Now that we’ve defined the
snapcraft.yaml, it’s time to build the snap. Make sure you’re in the directory we created earlier (the one that contained the
snap/ directory), and run
snapcraft with the
--enable-experimental-extensions flag (I told you this was experimental):
$ cd ~/ros2-snap $ snapcraft --enable-experimental-extensions *EXPERIMENTAL* extensions enabled. *EXPERIMENTAL* extension 'ros2-foxy' enabled. <snip> Snapped ros2-talker-listener_0.1_amd64.snap
Note that depending on your host and whether or not you’ve built snaps in the past, the snapcraft CLI may prompt you to install Multipass, a tool used by the snapcraft CLI to manage VMs for building snaps (rather than building straight on your host).
The build process will take a few minutes. You’ll see the snapcraft CLI install the build dependencies of the demo nodes, build the packages in the workspace and install them into the snap, and fetch their runtime dependencies and unpack them into the snap as well. This is a bit different than previous ROS snap build processes that used
rosdep: this uses
catkin_pkg directly to avoid
rosdep’s issue of unpacking even build dependencies into the snap, which means ROS snaps using extensions will be significantly smaller than in the past (and take less time to rebuild). Anyway, at the end, you’ll have your snap.
Test the snap
Let’s install the snap we just built:
$ cd ~/ros2-snap $ sudo snap install ros2-talker-listener_0.1_amd64.snap --dangerous
Note the use of the
--dangerous flag. That’s required because we’re installing a snap from disk instead of using the store, and snapd (the daemon with which we’re communicating using the
snap command) only trusts snaps that it can cryptographically verify as being from the store unless we tell it otherwise with this flag.
Finally, let’s run the app we defined in the
$ ros2-talker-listener <snip> [INFO] [talker-1]: process started with pid  [INFO] [listener-2]: process started with pid  [talker-1] [INFO] [1597444182.799512859] [talker]: Publishing: 'Hello World: 1' [listener-2] [INFO] [1597444182.800671590] [listener]: I heard: [Hello World: 1] [talker-1] [INFO] [1597444183.799313385] [talker]: Publishing: 'Hello World: 2' [listener-2] [INFO] [1597444183.799922986] [listener]: I heard: [Hello World: 2]
CTRL+C to stop that. As you can see, it works great! You could hand this snap to anyone with a snap-capable system, even if they don’t have ROS installed, and it would work exactly the same way for them.
I hope this is helpful in your snap journey, and gives you a decent guide to building ROS 2 Foxy snaps using the new
ros2-foxy extension. Please feel free to ask any questions here, on the Snapcraft forums, or on the ROS forums. I’d love to hear any feedback you have.