Thursday, August 29, 2019

Snapping ROS2 - that was quick!

Hearing that snapcraft now supports the ROS2 colcon build system, I figured I'd give it go. I'm a good victim, since I'm pretty new to ROS. Without much fuss or bother, I snapped two C++ ROS2 package, a publisher and subscriber, built with the snapcraft colcon plugin (beta in snapcraft 3.2).

Here's the amd64 snap in the store.

It has two commands, one to publish, one to subscribe:

$ kyle-ros2-colcon-pub-sub.publish

$ kyle-ros2-colcon-pub-sub.subscribe

Here's the github repo with a snapcraft.yaml file and the two ROS2 C++ packages.

See sample output below.


  • Build a ROS2 snap using the snapcraft colcon plugin.
  • Include two ROS2 C++ packages (a publisher and a subscriber).
  • Provide snap commands for each package node.
  • The snap commands should use ROS2's node launch system.
  • The snap should run with strict confinement.
  • The packages's source should follow the normal ROS2 workspace structure: WORKSPACE/src/PACKAGES  to allow me to develop the packages first (without thinking about snapping them) and then easily snap them (without changing the tree structure).
  • Each ROS2 package should be a separate snapcraft part, so I can independently build them.

Getting started


  • To get started, I read Kyle Fazzari's ROS2 Colcon Snap post
  • I then reviewed the state of ROS2 here.
  • I found that the Crystal Clemmy ROS2 release is current and that it is supported on Ubuntu 18.04 Bionic here
  • I reviewed colcon here.
  • I learned about the new ROS2 launch system here.

Bionic LXD container for development

I created a Bionic LXD container named ros2 to develop in (lxc launch ubuntu:18.04 ros2). Then, lxc start ros2 and lxc shell ros2 (which logs in as root user). When logged as root, I created another user in the container with my name and added the ssh key I use for github. I log in to that use with lxc exec ros2 -- su myusername.

I decided to build in the container directly instead of using the default (for core18 snaps) of multipass, which builds in a dedicated VM. The LXD container is dedicated to building just this ROS2 snap, so I don't mind if debian packages needed to build the snap get installed. So, I always used --destructive-mode flag on snapcraft to not use multipass.

Create and modify the packages

I got working C++ example ROS2 source package code here, crystal branch. 

  • examples/rclcpp/minimal_publisher/
  • examples/rclcpp/minimal_subscriber/

I modified the package.xml files and few other minor items to use my own package and node names. 

(I also added a bit of code to the publisher to publish messages consisting of a random adjective and a random noun, just for fun.)

Source tree directories

The following directory layout is structured as a ROS2 workspace, so I used this for my snap source tree structure:

└── src
    ├── kyle_publisher
    │   └── launch
    └── kyle_subscriber
        └── launch

There is a src directory that contains two packages: kyle_publisher and kyle_subscriber.

Each package contains a launch directory that contains a ROS2-style python launch file:
  • kyle_publisher launch file
  • kyle_subscriber launch file
I'll discuss snapping these launch assets below.

Colcon-source-space keyword

After a little experimentation, I realized I could create the two snapcraft parts (one for the publisher, one for the subscriber) with references to he same source: src directory and use the colcon-source-space keyword to point the part at different package directories:

    plugin: colcon
    source: src/
    colcon-source-space: kyle_publisher
[. . .]
    plugin: colcon
    source: src/
    colcon-source-space: kyle_subscriber
[. . .]

This allows each package to be a separate part that I could manage (pull, build, stage, prime) independently to save time during development. 

Issue: duplicate files in both parts

My parts approach causes an issue of some duplicated files. That, is there are a set of files that have the same path and the same name that are installed by both parts, and that is not allowed (or even possible).

The snapcraft tool is kind enough to even list the duplicated files when trying to build the snap!

Solution: suppress the files from one part

The solution is to suppress these files from one of the parts. Folks who have snapped a few things know that the snap build system has stages. First each part is pulled (into parts/PART/src/). Then it is built (into parts/PART/build/). And then the install directory is populated parts/PART/install/. Next, the install directories for all parts are merged into the single stage/ directory.  And here is where any duplicated install files clash. 

So, the solution is to suppress one of the set of duplicate files from being put into the stage/directory, which is easily done like so, in the subscriber part. 

Note: This is a yaml array, so the first hyphen indicates an array element. The second hyphen means suppress the following file, so all of these are suppressed from being placed into the stage/ directory 

[ . . . ]
      - -usr/lib/python3/
      - -usr/lib/python3.6/
      - -opt/ros/snap/local_setup.bash
      - -opt/ros/snap/local_setup.ps1
      - -opt/ros/snap/
      - -opt/ros/snap/local_setup.zsh
      - -opt/ros/snap/setup.bash
      - -opt/ros/snap/setup.ps1
      - -opt/ros/snap/
      - -opt/ros/snap/setup.zsh

With this in place, the two parts are separately buildable (if you use snapcraft build subscriber, for example), and they combine without a problem into a final snap, with no missing and no duplicate files.

Snap commands and launch files

I added two commands to the snap to launch the publisher and the subscriber from the ROS2 style python launch files in each package's launch directory.

    command: opt/ros/crystal/bin/ros2 launch kyle_publisher
    plugs: [network, network-bind]
    command: opt/ros/crystal/bin/ros2 launch kyle_subscriber
    plugs: [network, network-bind]

Issue: launch files are not installed in snap

I found that the packages' launch directories (and the ROS2 python launch files they contain) were not automatically carried through to the final snap. Specifically, I found that even though each package had a launch/ subdirectory, it was not populated into the part's install directory here, for example for the publisher part:


Solution: override each part's build stage 

The solution is to override the snapcraft build stage for each of the parts. First in the override, the normal snapcraft build is executed (with snapcraftctl build) , and then the launch/ directory  is manually copied from the part's src/ directory to its install/ directory, as follows:

[. . .]
    override-build: |
      snapcraftctl build
      cp -r ../src/kyle_publisher/launch $SNAPCRAFT_PART_INSTALL/opt/ros/snap/share/kyle_publisher/

This uses the override-build keyword to take control of the snapcraft build for the part. After completing the snapcraft build (with snapcraftctl build), the directory is copied. The only tricky part is knowing that the cp command executes in the part's build directory. Since I need to copy from the part's src directory, I need to know the relative path to it: ../src . Also note the use of the SNAPCRAFT_PART_INSTALL variable to name the part's install directory without needing to know its location. 

That's it

Using the snapcraft colcon plugin couldn't have been much easier: it pretty much worked as I intuitively expected it to. As noted, launch asset handling required manual steps to copy them into the snap. But this was a simple fix that may not be unfamiliar to anyone who uses snapcraft. I also needed to suppress some redundant files, but this resulted from my decision to treat a single ROS2 workspace with two packages as separate snapcraft parts, which may have been naive on my part.

Try out the snap

Here's the amd64 snap in the store.

Install with:

$ snap install kyle-ros2-colcon-pub-sub

Run the publisher like so: 
$ kyle-ros2-colcon-pub-sub.publish 
[INFO] [launch]: process[publisher_member_function-1]: started with pid [2508]
[INFO] [kyle_publisher]: Publishing: 'tiny television'
[INFO] [kyle_publisher]: Publishing: 'sorry sympathy'

Run the subscriber like so: 
$ kyle-ros2-colcon-pub-sub.subscribe 
[INFO] [launch]: process[subscriber_member_function-1]: started with pid [2218]
[INFO] [minimal_subscriber]: 'tiny television'
[INFO] [minimal_subscriber]: 'sorry sympathy'

Wednesday, January 4, 2017

Scratch-Qt Snap with Ubuntu-App-Platform

Over the 2016 end-of-year holidays, I scratched an itch to write a Qt app that exercises a few capabilities.

My goals were to make:
  • QML app front end with a "Job" component. This has a button and a rectangle that rotates on animation.
  • Tap a "Job" button, and the associated C++ back end method runs in a dedicated thread (so the Qt GUI is not blocked)
  • While the Job's back end code runs, the QML animation is active (a rectangle next to the button goes from orange to purple and rotates), so the user knows that job is in process
  • When the back end code is done, it emits a signal that is heard and stops the animation: the rectangle turns back to orange and stops rotating), so the user knows the job is done
  • And, the snap obtains access to Qt and Ubuntu app libs provided by the Ubuntu-app-platform snap through the Content sharing interface. 
So, this approach would support a dashboard with lots of buttons, each of which is associated with C++ code, with a non-blocking GUI.

The app has other bits, including:
  • A simple shell (enter a command, see the result), which also has buttons to show Snap execution env
  • An oxide browser page
Nothing brand new here, just putting the pieces together for my own enjoyment :).

Let's look at some highlights.

Using the ubuntu-app-platform

As is well known now, the great thing about the ubuntu-app-platform snap is that it provides the libs (Qt/Ubuntu) needed to write an Ubuntu desktop app. Install this snap once, and use it in your app snaps through the Content Sharing interface (thus sharing disk space).

A few steps are needed.


We use the desktop-ubuntu-app-platform remote part (to pull in the ubuntu-app-platform build stuff). You can see the formal description and instructions for using this with:

snapcraft define desktop-ubuntu-app-platform

(You may need to run snapcraft update first.)

Here are the modifications you need in your snapcraft.yaml.

Define the platform plug

Define a plug that connects to the ubuntu-app-platform snap through the content interface like this:

        interface: content
        content: ubuntu-app-platform1
        target: ubuntu-app-platform

This plug:
  • enables the snap to be connected to the "platform" interface
  • to access the platform slot's "ubuntu-app-platform1" content
  • by bind mounting (on interface connection) that content to the "ubuntu-app-platform" directory in my snap 

Use the platform plug

The apps section must also include platform in the list of plugs (interfaces) it uses:

    command: desktop-launch scratch-qt
    plugs: [platform, unity7, opengl, network-bind, gsettings, browser-support, pulseaudio]

Build your application part AFTER desktop-ubuntu-ap-platform

The application is a QML/C++ project that also imports Ubuntu.Components. Naturally, these libs need to be fetched during the snapcraft build process before the application that needs them is built. Therefore, you can use the convenient after: keyword to ensure the proper sequence, as follows:

        after: [desktop-ubuntu-app-platform]

CMake: make ubuntu-app-platform dir

The snap needs a directory named "ubuntu-app-platform" for the bind-mounting provided by the ubuntu-app-platform interface. I create this in CMake:

file(MAKE_DIRECTORY ubuntu-app-platform)
install(DIRECTORY "ubuntu-app-platform" DESTINATION ${DATA_DIR})

As a result, the directory is created during snapcraft in the parts/application/install directory:

$ ls parts/application/install/
components  graphics  lib  qml  ubuntu-app-platform

(My "application" part uses cmake plugin to build/install my qml/C++ app.)

Connect to the interface

The script builds (to prime stage), installs (via snap try prime), connects the interfaces, and launches the snap. The key  here is the interface connection:

snap connect scratch-qt:platform ubuntu-app-platform:platform

Update environment vars in wrapper

This snap has a wrapper script to launch qmlscene and the app.

Various environment variables need to be modified and exported to use the ubuntu-app-platform libs, including: LD_LIBRARY_PATH, QT_PLUGIN_PATH and QML2_IMPORT_PATH.

Non-blocking GUI

A key goal was to make it easy to run the C++ method associated with each  button tap without blocking the GUI, and allowing multiple C++ back-ends to run concurrently. Without implementing threads for this and moving these threads off the default GUI thread, the Qt GUI blocks until the back end processing associated with each button is completed.

Here's my approach.

I created a QML item (Job), a C++ class (Job), and registered the Job class into QML as part of my Scratchqt plugin. The Job displays as a button and a rectangle that rotates and changes color when the job is executing.

When you add a Job item, you specify its "job" string property. When you tap the Job button, it executes the JavaScript runMe() function, which starts the animation (indicating the work is in progress) and then calls the C++ Job::start(job) method.
 Job::start(job) creates an instance of the JobController class (constructed with the job so it knows which job to execute) and then emits Jobcontroller::operate signal. This operate signal is connected to the particular code to run, as explained below.

The JobController constructor creates a Jobs object on the heap.

Jobs class simply contains a method for each Job, for example: job1(), job2(), etc. These methods are where you put the back-end code to run on the particular Job button tap.

Jobs also has a jobDone signal. This is connected to Job::jobDone, as explained later.

The JobController object has a QThread. This is where the back-end execution occurs. The Jobs object is moved to the new QThread. This enables the QML GUI to remain responsive after the C++ back-end execution gets going.

JobController has an if/then/else construct to connect the "operate" signal (emitted by each Job:;start(job)) to the right Jobs slot (Jobs::job1, for example), depending on which Job it is (using the "job" that originates in the Jobs QML item). And then the QThread is started.

Lastly, JobController always connects the Jobs::jobDone signal to the Job::jobDone signal. This allows communication back into the QML so that when the particular back-end job is completed, the animation is turned off (via onJobDone).

Adding an independent Job

Add a new Job item in ThreadController.qml and adjust the values appropriately.

    Job {
        id: job5
        job: "job5"
        button_text: "Job 5" job4.bottom

In the Jobs object jobs.h, add a new slot for your new job5 code:

public slots:
    void job1();
    void job2();
    void job3();
    void job4();
    void job5();

And in jobs.cpp, add the the method:

void Jobs::job5()
    qDebug() << "==== in Jobs::job5()";
    long sleepTime = 3000000;
    Q_EMIT jobDone();

In JobController.cpp constructor, add the if/then/else stanza to connect the operate signal to job5:

    else if (job_name == "job5")
        connect(this, &JobController::operate, jobs, &Jobs::job5);

And here's the new job5 running (so is job3):


Monday, July 18, 2016

Running X Apps on Ubuntu Devices

Running X Apps on Ubuntu Devices You can install, launch, and use traditional debian-packaged X apps on Ubuntu devices. This may be unexpected given that Ubuntu devices do not seem to support user-installed debian packages, nor do they run the X Display Server. But it does work, courtesy of Mir/XMir and Libertine.

So here’s a bit of background to get started.

But first, please note that at this time, display and use of X apps on an external monitor is only available on the Pro5/M10 and on future devices. (BQ 4.5/E5 and Meizu MX4 do not support this feature.)

Hello Mir (Goodbye X)

Traditionally, and still on the Ubuntu Classic desktop with Unity 7, Ubuntu runs an X Display Server. Apps are debian packaged. And, they are written for X:

Due in part to X’s inherent security shortcomings, the Mir display server is now used on Ubuntu Devices under Unity 8 (although not yet by default on the desktop). XMir bridges traditional X apps to Mir. That is, apps written for X can run fine in a Mir/XMir environment:

Packages and the root file system

Ubuntu Classic has a root file system (rootfs) that is populated through installation of a carefully curated set of debian packages. At run time, users can install debian packages to add apps or modify their system.

This approach raises security concerns because debian packages execute installation scripts with root level privileges and because debian packages can alter what the rootfs provides by modifying or replacing core system components.

Ubuntu devices are designed for security and reliability. Ubuntu devices have a read-only rootfs that is small and tight, providing just what is needed and simplifying system updates. The rootfs is not modifiable by the user. Indeed it is mounted as a read-only partition. Users install apps through click packages that do not modify the rootfs.

Given all of this: how do users install debian packaged apps that use X on Ubuntu Devices? The answer is LIbertine with XMir.

Hello Libertine

Libertine is a system to manage app containers. It is specifically designed to support the many traditional X apps that are debian packaged. Each container is a separate Ubuntu rootfs populated through debian package installations. (Currently these containers are chroots: later, LXD contains will be supported. Also, currently the containers must be of the same Ubuntu series as the device: Vivid.)

So, you can install or create a libertine container, install debian packaged X apps into it, and launch them using the XApps scope. The apps access to the user’s key directories: Documents, Downloads, Music, Pictures, and Videos. So data files created and saved by an app in one container are available to apps in any other container, and indeed outside of the containers.

Let’s take a quick look at the XApps scope.

XApps Scope

This scope simply lists the containers and, for each container, it displays its apps. Here’s a look at a device with two containers. This system has two containers (Puritine and My Container). And each has a few apps:

  • Tap an app to launch it.
  • long press an app to hide it.
  • If you have any hidden apps, see them from the search icon (magnifying glass) and tap Hidden X Apps. Long press a hidden app to unhide it.
  • Note that a container with no apps does not display in the scope.
So how does one create and delete containers, and add or remove apps from them?

Libertine Container Manager

libertine-container-manager is a command line tool you use on the device to create and manage containers. This includes installing debian packaged apps into them. (These containers are created in the phablet user’s home directory and are not a part of the read-only rootfs.)

Note: libertine-container-manager currently cannot be run in the Terminal App. Instead please connect to your device from an Ubuntu system using phablet-shell.
Listing Containers
phablet@ubuntu-phablet:~$ libertine-container-manager list

The “puritine” container is pre-installed on many devices through the com.ubuntu.puritine click package (“Desktop Applications”):

phablet@ubuntu-phablet:~$ click list | grep puritine
com.ubuntu.puritine 0.11

The second container (“my-container”) was created on the device with libertine-container-manager.

Note: It is possible to pre-install customized containers through bespoke channels.
Creating a Libertine Container
You can create a new container on a device. The container needs a unique ID and (optionally) a name.

Note: The container must be the same Ubuntu series as the device, currently: vivid.

phablet@ubuntu-phablet:~$ libertine-container-manager create --id my-container --name "My Container" --distro vivid --type chroot

I: Retrieving Release
I: Retrieving Release.gpg
I: Checking Release signature
I: Valid Release signature (key id 790BC7277767219C42C86F933B4FE6ACC0B21F32)
I: Retrieving Packages
I: Validating Packages
I: Resolving dependencies of required packages...
Listing Apps in a Container
It’s easy to list the apps in a container. You just use the container’s id, as follows:

Note: We add the optional --json argument here and show only lines with “name” for display convenience.

phablet@ubuntu-phablet:~$ libertine-container-manager list-apps --id my-container --json | grep "\"name\""
"name": "Panel Manager",
"name": "Python (v3.4)",
"name": "Python (v2.7)",
"name": "gedit",
"name": "Help",
"name": "Notification Daemon",
"name": "Terminal",
Also note that all apps that install a .desktop file are listed by this command, although many of them are not displayed in the XApps scope since they are not appropriate.
Installing an app in a container
To install a debian package in a container, you just use install-package with the container id and the debian binary package name, as follows:

phablet@ubuntu-phablet:~$ libertine-container-manager install-package --id my-container --package terminator

The package and all of its dependencies are installed in the container. After this, assuming the package installs a .desktop file, it displays in the XApps scope and is launchable with a tap as expected.
Installing an app from a specific Launchpad PPA
By default, available debian packages are installed from the standard Ubuntu archive the chroot’s apt configuration points to. You can add a launchpad PPA, as follows:

phablet@ubuntu-phablet:~$ libertine-container-manager configure --id my-container --archive ppa:USER/PPA-NAME

(Currently, private PPAs are scheduled for an upcoming release.)

After this, you can install packages into the container as usual, including from the PPA.
Removing apps from a container
Remove a debian package from a container with:

phablet@ubuntu-phablet:~$ libertine-container-manager remove-package --id my-container --package PACKAGE_NAME
Libertine-container-manager help
Use the --help for top level help.

You can see details on each subcommand, for example remove-package, as follows:

phablet@ubuntu-phablet:~$ libertine-container-manager remove-package --help
usage: libertine-container-manager remove-package [-h] -p PACKAGE [-i ID] [-r]

optional arguments:
-h, --help show this help message and exit
-p PACKAGE, --package PACKAGE
Name of package to remove. Required. -i ID, --id ID Container identifier. Default container is used if
-r, --readline Readline mode. Use text-based frontend during debconf
Updating a container
Want the debian packages in a container updated? Easy:

phablet@ubuntu-phablet:~/.cache/libertine-container/my-container$ libertine-container-manager update --id my-container
Executing a Command in a Container
phablet@ubuntu-phablet:~/.cache/libertine-container/my-container$ libertine-container-manager exec --command "apt-get update" --id my-container
Atteint vivid InRelease
Atteint vivid InRelease
Atteint vivid-updates InRelease
Atteint vivid/main armhf Packages
Atteint vivid/main Translation-en
Atteint vivid/main armhf Packages [...]

Note: Running the apt-get update command in a container may be useful to update the container’s knowledge of newly available packages without installing/updating them all. You can then see whether a package is available, with:

phablet@ubuntu-phablet:~/.cache/libertine-container/my-container$ libertine-container-manager exec --command "apt-cache policy firefox" --id my-container
Installé : (aucun)
Candidat : 44.0+build3-0ubuntu0.15.04.1
Table de version :
44.0+build3-0ubuntu0.15.04.1 0
500 vivid-updates/main armhf Packages
37.0+build2-0ubuntu1 0
500 vivid/main armhf Packages

More about the Libertine Containers

As noted, the container is a directory containing an Ubuntu rootfs. Container directories are here:

phablet@ubuntu-phablet:~/.cache/libertine-container$ pwd
phablet@ubuntu-phablet:~/.cache/libertine-container$ ls
my-container puritine
phablet@ubuntu-phablet:~/.cache/libertine-container$ cd my-container/
phablet@ubuntu-phablet:~/.cache/libertine-container/my-container$ ls

You can get a bash shell into the container as follows:

phablet@ubuntu-phablet:~/.cache/libertine-container/my-container$ libertine-container-manager exec --command "/bin/bash" --id my-container
groups: cannot find name for group ID 1001

Monday, April 7, 2014

New Unity 8 Scopes Docs

I have completed and published several documents on the Ubuntu development portal, scopes section. I hope these are useful when developing Unity 8 Scopes: 
The team also published these new docs:

Friday, March 7, 2014

Thursday, December 19, 2013

Cordova 3.3 adds Ubuntu

Upstream Cordova 3.3.0 is released just in time for the holidays with a gift we can all appreciate: built-in Ubuntu support!

Cordova: multi-platform HTML5 apps

Apache Cordova is a framework for HTML5 app development that simplifies building and distributing HTML5 apps across multiple platforms, like Android and iOS. With Cordova 3.3.0, Ubuntu is an official platform!

The cool idea Cordova starts with is a single www/ app source directory tree that is built to different platforms for distribution. Behind the scenes, the app is built as needed for each target platform. You can develop your HTML5 app once and build it for many mobile platforms, with a single command.

With Cordova 3.3.0, one simply adds the Ubuntu platform, builds the app, and runs the Ubuntu app. This is done for Ubuntu with the same Cordova commands as for other platforms. Yes, it is as simple as:

$ cordova create myapp REVERSEDOMAINNAME.myapp myapp
$ cd myapp
(Optionally modify www/*)
$ cordova build [ ubuntu ]
$ cordova run ubuntu


Cordova is a lot more than an HTML5 cross-platform web framework though.
It provides JavaScript APIs that enable HTML5 apps to use of platform specific back-end code to access a common set of devices and capabilities. For example, you can access device Events (battery status, physical button clicks, and etc.), Gelocation, and a lot more. This is the Cordova "plugin" feature.

You can add Cordova standard plugins to an app easily with commands like this:

$ cordova plugin add org.apache.cordova.battery-status
(Optionally modify www/* to listen to the batterystatus event )
$ cordova build [ ubuntu ]
$ cordova run ubuntu

Keep an eye out for news about how Ubuntu click package cross compilation capabilities will soon weave together with Cordova to enable deployment of plugins that are compiled to specified target architecture, like the armhf architecture used in Ubuntu touch images (for phones, tablets and etc.).


As a side note, I'm happy to note that my documentation of initial Ubuntu platform support has landed and has been published at Cordova 3.3.0 docs.

Friday, November 8, 2013

Ubuntu HTML5 API docs

HTML5 API docs published

I'm pleased to note that the Ubuntu HTML5 API docs I wrote are now done and published on These cover the complete set of JavaScript objects that are involved in the UbuntuUI framework for HTML5 apps (at this time). For each object, the docs show how the corresponding HTML is declared and, of course, all public methods are documented.

A couple notes:
  • I wrote an html5APIexerciser app that implements every available public method in the framework. This was helpful to ensure that what I wrote matched reality ;) It may be useful to folks exploring development of  Ubuntu HTML5 apps. The app can be run directly in a browser by opening its index.html, but it is also an Ubuntu SDK project, so it can be opened and run from the Ubuntu SDK, locally and on an attached device.
  • The html5APIexerciser app does not demonstrate the full set of Ubuntu CSS styles available. For example, the styles provide gorgeous toggle buttons and progress spinnners, but since they have no JavaScript objects and methods they are not included in the API docs. So be sure to explore the Gallery by installing the ubuntu-html5-theme-examples package and then checking out /usr/share/ubuntu-html5-theme/0.1/examples/
  • I decided to use yuidoc as the framework for adding source code comments as the basis for auto generated web docs.  After you install yuidoc using npm you can build the docs from source as follows:
  1. Get the ubuntu-html5-theme branch: bzr branch lp:ubuntu-html5-theme
  2. Move to the JavaScript directory: cd ubuntu-html5-theme/0.1/ambiance/js/
  3. Build the docs: yuidoc -c yuidoc.json . This creates the ./build directory.
  4. Launch the docs by opening build/index.html in your browser. They should look something like this 
Thanks to +Adnane Belmadiaf for some theme work and his always helpful consultation, to +Daniel Beck for his initial writeup of the Ubuntu HTML5 framework, and of course to the team for their always awesome work!