How to build ROS 2 Eloquent Snaps
by Ted Kern on 14 December 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 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!
Prerequisites
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
Getting Started
Create the snapcraft.yaml
First of all, create a new workspace directory and add a snapcraft.yaml
.
~$ 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.
Open snap/snapcraft.yaml
, and make the following changes:
name: eloquent-talker-listener-arnatious
version: "0.1"
summary: Eloquent ROS 2 Example
description: |
A ROS 2 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 ROS 2 Example
description: |
A ROS 2 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 stable
or devel
, with devel
locking out the ability to publish to the “stable” and “candidate” channels.
confinement
can be strict
, devmode
, or 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.
The 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.
The colcon
plugin is used here to build the package, with the colcon-rosdistro
argument specified as eloquent
, the 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 build-packages.
stage-packages
contains 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 apps
” section.
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 demo_nodes_cpp
package.
Lastly, we specify the interfaces the snap needs access to outside of confinement – in this case network
and 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
The --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 [28330]
[INFO] [listener-2]: process started with pid [28331]
[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.
I hope this gives you a good look at how to build ROS 2 Eloquent snaps! Feel free to ask questions on the Snapcraft forums or on the ROS Forums.