Warning

You're reading the documentation for a version of ROS 2 that has reached its EOL (end-of-life), and is no longer officially supported. If you want up-to-date information, please have a look at Jazzy.

Packaging your ROS 2 application as a snap [community-contributed]

What are snaps?

Snaps are containers that bundle an application and all its dependencies. They offer several features that address important concerns as one gets closer to shipping a robotic platform:

  • Container solution: Snaps bundle your application along with all the necessary dependencies (including ROS 2) and assets in one package. Your application is then easily installable on dozens of Linux distributions and across distro versions.

  • Strict confinement: Snaps are designed to be secure and isolated from the underlying system and other applications, with dedicated interfaces to access the host machine.

  • Managing updates: Snaps can update automatically and transactionally, making sure your robot is never broken and always up-to-date.

  • Release management: Snaps’ multiple release channels allow you to have role-based access controls and application versioning, making A/B testing easy and releasing fixes faster.

Creating a snap

This tutorial will demonstrate how to use snapcraft to package your ROS 2 application as a snap, and then how to use it.

First, let us install snapcraft.

sudo snap install --classic snapcraft

(Note that the snapcraft debian package from the apt repositories is largely deprecated. One should use the snap package.)

Snapcraft has built-in support for Colcon.

For your example, you will use the demo_nodes_cpp package from the ros2_demos.

Initialize a new snapcraft project here:

mkdir ~/demo_nodes_cpp_snap
cd ~/demo_nodes_cpp_snap
snapcraft init

This will create a file in a subdirectory snap/snapcraft.yaml.

The snapcraft file

Open the freshly created snap/snapcraft.yaml file and copy over the following:

name: ros2-talker-listener
version: '0.1'
summary: ROS 2 Talker/Listener example
description: |
  This example launches a ROS 2 talker and listener.

confinement: devmode
base: core20

parts:
  ros-demos:
    plugin: colcon
    source: https://github.com/ros2/demos.git
    source-branch: foxy
    colcon-packages: [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
    extensions: [ros2-foxy]

Let’s break it down.

Metadata

name: ros2-talker-listener
version: '0.1'
summary: ROS 2 Talker/Listener example
description: |
  This example launches a ROS 2 talker and listener.

This is the basic metadata that all snaps require. These fields are fairly self-explanatory but note that the name must be globally unique across all snaps.

Base

base: core20

The base keyword defines a special kind of snap that provides a run-time environment with a minimal set of libraries that are common to most applications. Core20 is the current standard base for snap building and is based on Ubuntu 20.04 LTS. It is, therefore, the base used for foxy.

Security model

confinement: devmode

To get started, you won’t confine this application. Unconfined applications, specified with devmode, can only be released to the edge channel of the snapcraft store. For more information about snaps Security model, please refer to the online documentation.

Parts

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

Parts define how to build the app. In this case, you have one: ros-demos. Parts can point to local directories, remote git repositories, or tarballs. Here, you specify your source as a GitHub repository at a specific branch. You also specifically tell Colcon to build the demo_nodes_cpp package. Furthermore you tell snapcraft that packages such as make are necessary at build time while the package ros-foxy-ros2launch is necessary at run time. For more information about the plugin and its options, please refer to the online documentation.

Apps

apps:
  ros2-talker-listener:
    command: opt/ros/foxy/bin/ros2 launch demo_nodes_cpp talker_listener.launch.py
    extensions: [ros2-foxy]

Apps are the commands exposed to end users. Each key under apps is the command name that should be made available on users’ systems. The command keyword specifies the command to be run as its name suggests. Finally, the extensions ros2-foxy essentially sets up the ROS 2 apt package repository together with the necessary environment variables.

Building the snap

Now that you are all set up, let’s build the snap:

cd ~/demo_nodes_cpp_snap
snapcraft --enable-experimental-extensions

Giving:

*EXPERIMENTAL* extensions enabled.
Launching a VM.
Launched: snapcraft-ros2-talker-listener
[...]
This part is missing libraries that cannot be satisfied with any available stage-packages known to snapcraft:
libnddsc.so
libnddscore.so
libnddscpp.so
librosidl_typesupport_connext_c.so
librosidl_typesupport_connext_cpp.so
librticonnextmsgcpp.so
Snapped ros2-talker-listener_0.1_amd64.snap

Note

The warnings regarding the missing libraries are false positives. These libraries are build time dependencies only. This will be fixed in a future release of snapcraft.

That will take a few minutes. From the logs, and among other things, you can see snapcraft using rosdep to pull the dependencies for your example but also Colcon building the application.

Testing the snap

This snap is completely standalone: it includes ROS 2 and your application, meaning that one doesn’t even need to install ROS 2 on the host system. Let’s test it out:

sudo snap install ros2-talker-listener_0.1_amd64.snap --devmode

Note that you use --devmode here because the snap confinement is set as devmode. The moment of truth, will it run?

ros2-talker-listener
[talker-1] [INFO] [1646934735.523191674] [talker]: Publishing: 'Hello World: 1'
[listener-2] [INFO] [1646934735.524428480] [listener]: I heard: [Hello World: 1]
[talker-1] [INFO] [1646934736.523025881] [talker]: Publishing: 'Hello World: 2'
[listener-2] [INFO] [1646934736.523614075] [listener]: I heard: [Hello World: 2]

It does! You see the expected output!

You can find more information about snap on the snapcraft documentation and ROS 2 snap page.