Building ROS 2 snaps with Colcon

by Kyle Fazzari on 28 February 2019

Please note that this blog post has out technical information that may no longer be correct. For latest updated documentation about robotics in Canonical please visit https://ubuntu.com/robotics/docs.

The snapcraft CLI has supported building ROS1 snaps for a while via the catkin plugin. We supported the ROS2 betas via the ament plugin, but that was before Open Robotics had a ROS2 package repository setup, which meant that the ament plugin built the ROS2 underlay from source, and it was predictably dreadfully slow. However, the stable releases of ROS2 introduced a new build system called colcon, and also had their own package repositories setup. Version 3.2 of the snapcraft CLI (just released today) sees the addition of a colcon plugin to support those new releases, and I’d like to give you a quick run-through of its capabilities.

Prerequisites

In order to follow along, make sure you have at least v3.2 of the snapcraft CLI. The best way to do that is to install the snap:

$ sudo snap install --classic snapcraft

Some previous experience building a snap will also be helpful.

Let’s get started

Create the snapcraft.yaml

First of all, create a new directory and initialize it with a snapcraft.yaml:

~$ mkdir ros2-snap
~$ cd ros2-snap/
~/ros2-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.

Open that 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: core18

parts:
ros-demos:
plugin: colcon
source: https://github.com/ros2/demos.git
source-branch: crystal
colcon-rosdistro: crystal
colcon-source-space: demo_nodes_cpp
build-packages: [make, gcc, g++]
stage-packages: [ros-crystal-ros2launch]

apps:
run:
command: opt/ros/crystal/bin/ros2 launch demo_nodes_cpp talker_listener.launch.py
plugs: [network, network-bind]

Let’s break that down and go through it 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: core18

grade can be either stable or devel. If it’s devel, the store will prevent you from releasing into one of the two stable channels (stable and candidate, specifically). If it’s stable, you can release it anywhere.

confinement can be strict, devmode, or classic. 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 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).

Finally, the 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 core18 which is a minimal rootfs based upon Ubuntu Bionic (18.04).

parts:
ros-demos:
plugin: colcon
source: https://github.com/ros2/demos.git
source-branch: crystal
colcon-rosdistro: crystal
colcon-source-space: demo_nodes_cpp
build-packages: [make, gcc, g++]
stage-packages: [ros-crystal-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 ROS2 demos GitHub repository (this could just as easily be a directory on disk). We also specify that we’re using the newest ROS2 release as of this writing: Crystal. We point the colcon plugin at the C++ demo nodes specifically. We also provide a list of packages that need to be installed in order to build (build-packages), and also ask that ros-crystal-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:
run:
command: opt/ros/crystal/bin/ros2 launch demo_nodes_cpp talker_listener.launch.py
plugs: [network, network-bind]

This part is interesting. When we build this snap, it will include a complete ROS2 system: rclcpp, the 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 run. The command that this app actually runs within the snap uses the ros-crystal-ros2launch that we staged to fire up the demo nodes’ talker/listener launch file. Finally, we use plugs to specify that this app requires network access (read more about interfaces).

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:

~$ cd ros2-snap/
~/ros2-snap$ snapcraft
<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.

The build process will take a few minutes. You’ll see the snapcraft CLI fetch rosdep, which is then used to determine the dependencies of the packages in the colcon workspace. It then pulls those down and unpacks them into the snap, and finally builds the packages in the workspace and installs them into the snap as well. At the end, you’ll have your snap.

Test the snap

Let’s install the snap we just built:

~/ros2-snap$ sudo snap install --dangerous ros2-talker-listener_0.1_amd64.snap 
ros2-talker-listener 0.1 installed

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 snapcraft.yaml:

$ ros2-talker-listener.run
[INFO] [launch]: process[talker-1]: started with pid [26273]
[INFO] [launch]: process[listener-2]: started with pid [26274]
[INFO] [talker]: Publishing: 'Hello World: 1'
[INFO] [listener]: I heard: [Hello World: 1]
[INFO] [talker]: Publishing: 'Hello World: 2'
[INFO] [listener]: I heard: [Hello World: 2]

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 gives you a decent overview of the capabilities of the colcon plugin, and I look forward to seeing what you do with it! 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.

This article originally appeared on Kyle Fazzari’s blog.

Newsletter Signup

Related posts

Managing software in complex network environments: the Snap Store Proxy

As enterprises grapple with the evolving landscape of security threats, the need to safeguard internal networks from the broader internet is increasingly important. In environments with restricted internet access, it can be difficult to manage software updates in an easy, reliable way. When managing devices in the field, change management […]

Improving snap maintenance with automation

Co-written with Sergio Costas Rodríguez. As the number of snaps increases, the need for automation grows. Any automation to help us maintain a group of snaps is welcome and necessary for us to be able to scale. The solution detailed in this article has two main benefits: Any users of snaps that have adopted this […]

Snapcraft 8.0 and the respectable end of core18

‘E’s not pinin’! ‘E’s passed on! This base is no more! He has ceased to be! ‘E’s expired and gone to meet ‘is maker! ‘E’s a stiff! Bereft of life, ‘e rests in peace! If you hadn’t nailed ‘im to the perch ‘e’d be pushing up the daisies! ‘Is software processes are now ‘istory! ‘E’s […]