The end of 2019 brings about the latest ROS 2 release – Eloquent Elusor. Despite an ever growing set of features and some changes throughout the ecosystem, packaging with snaps is as easy as always. Let’s go through a quick example!
You’ll need two tools: “snapcraft,” the program that builds snaps, and “multipass,” the VM management tool that is used by snapcraft to conduct snap builds without cluttering your development machine. You can get both of these from the snap store.
At the time of this writing, the latest stable release of snapcraft is version 3.8. Until the next release comes out, in order to build Eloquent snaps, you’ll need to grab it from edge.
$ snap install snapcraft --edge --classic
If you already have snapcraft stable installed, you can change to edge using:
$ snap refresh snapcraft --edge
Multipass is grabbed the same way, requiring the
--beta flag since the version at the time of writing is 0.9.0
$ snap install multipass --classic --beta
Create the snapcraft.yaml
First of all, create a new workspace directory and add a
~$ mkdir eloquent-snap ~$ cd eloquent-snap/ ~/eloquent-snap$ snapcraft init Created snapcraft.yaml. Go to https://docs.snapcraft.io/the-snapcraft-format/8337 for more information about the snapcraft.yaml format.
snap/snapcraft.yaml, and make the following changes:
name: eloquent-talker-listener-arnatious version: "0.1" summary: Eloquent ROS2 Example description: | A ROS2 workspace containing a talker and a listener. grade: devel confinement: strict base: core18 parts: colcon-part: plugin: colcon source: https://github.com/ros2/demos.git source-branch: eloquent colcon-rosdistro: eloquent colcon-source-space: demo_nodes_cpp build-packages: [make, gcc, g++] stage-packages: [ros-eloquent-ros2launch] apps: eloquent-talker-listener-arnatious: command: opt/ros/eloquent/bin/ros2 launch demo_nodes_cpp talker_listener.launch.py plugs: [network, network-bind]
Let’s take an in depth look at this manifest.
name: eloquent-talker-listener version: "0.1" summary: Eloquent ROS2 Example description: | A ROS2 workspace containing a talker and a listener.
This is the metadata any snap requires. The only caveat to this section is that the
name must be unique on the snap store. Appending your developer name to the snap is an option to get around this (like I did).
grade: devel confinement: strict base: core18
grade should be either
devel locking out the ability to publish to the “stable” and “candidate” channels.
confinement can be
classic. These are documented in detail on snapcraft.io, but in short:
strict enforces tight confinement, restricting access to the system for security;
devmode allows full access without any restriction but logs all accesses that
strict would prevent;
classic is unconfined, without its own private namespace or other security features.
Generally, you should always target “strict” confinement unless there’s specific features that confinement will prevent. “Classic” snaps, like snapcraft itself or text editors like Visual Studio Code, have no containment and run without protection. Classic snaps need manual approval.
base keyword specifies the special snap providing things like a minimal set of core libraries for our snap’s runtime environment. We’re just using the “core18” snap, which bundles a minimal rootfs based on Ubuntu Bionic (18.04).
parts: colcon-part: plugin: colcon source: https://github.com/ros2/demos.git source-branch: eloquent colcon-rosdistro: eloquent colcon-source-space: demo_nodes_cpp build-packages: [make, gcc, g++] stage-packages: [ros-eloquent-ros2launch]
Snapcraft builds packages piecemeal, in small parts that are combined together. These are reflected by the part entries in the snapcraft.yaml.
colcon-part is specified here as pulling the source code from a remote Github repository, though it can just as easily pull source code from a local directory or any source control system.
colcon plugin is used here to build the package, with the
colcon-rosdistro argument specified as
colcon-source-space specified as the
demo_nodes_cpp directory containing just the talker/listener package we care about. We specify the packages that provide the commands we need to build the ROS nodes under
ros2launch, as we’ll need the
launch verb it provides as part of the entry point to our snap.
apps: eloquent-talker-listener-arnatious: command: opt/ros/eloquent/bin/ros2 launch demo_nodes_cpp talker_listener.launch.py plugs: [network, network-bind]
We need to express how users will interact with this package. Since we’re packaging ~our~ project, we want the snap to expose our project’s verbs. We define these user interactions as “apps” in the
In general, these verbs are invoked using
<snap-name>.<app>. An entry in
apps with the same name as the snap, like “eloquent-talker-listener-arnatious” above, can be invoked with just the snap name. Calling this app invokes the script in the
command field, in this case roslaunch-ing the talker/listener system out of the
Lastly, we specify the interfaces the snap needs access to outside of confinement – in this case
network-bind. This allows the snap to access the network and be accessed from the network, respectively. You can read more about interfaces on the snapcraft site.
Build the Snap
Now that we’ve defined the snapcraft.yaml, we can build the snap itself. Make sure you’re in the correct directory (above the “snap/” directory containing the “snapcraft.yaml”) and invoke “snapcraft.”
~$ cd eloquent-snap ~/eloquent-snap$ snapcraft <omitted for brevity> Snapped colcon-talker-listener-arnatious_0.1_amd64.snap
If you neglected to install multipass earlier,
snapcraft may prompt you to do so.
The build process may take several minutes. During that time, the colcon plugin will install rosdep and use it to determine the dependencies specified in the
package.xml of the packages, fetching and unpacking them into your snap. At the end, the result is squashed into a .snap file.
Test the Snap
To install the snap we just built before publishing it, we run
~/eloquent-snap$ sudo snap install --dangerous eloquent-talker-listener-arnatious_0.1_amd64.snap eloquent-talker-listener-arnatious 0.1 installed
--dangerous flag is required to install snaps from locations other than the store. The snapd daemon responsible for running snaps requires any snap to be cryptographically verified from the snap store, unless the
--dangerous flag is used.
Finally, we invoke the snap:
$ eloquent-talker-listener-arnatious [INFO] [launch]: All log files can be found below /home/arnatious/snap/eloquent-talker-listener-arnatious/x1/.ros/log/2019-12-11-07-56-04-973199-arnatious-ubuntu-1910 [INFO] [launch]: Default logging verbosity is set to INFO [INFO] [talker-1]: process started with pid  [INFO] [listener-2]: process started with pid  [talker-1] [INFO] [talker]: Publishing: 'Hello World: 1' [listener-2] [INFO] [listener]: I heard: [Hello World: 1] [talker-1] [INFO] [talker]: Publishing: 'Hello World: 2' [listener-2] [INFO] [listener]: I heard: [Hello World: 2] [talker-1] [INFO] [talker]: Publishing: 'Hello World: 3' [listener-2] [INFO] [listener]: I heard: [Hello World: 3] …
The snap runs without issue on the machine, regardless of linux distro or whether ROS is on the machine. You can now push to the the snap store – where anyone running snapd can install and run it.