Thursday, June 9, 2016

Building Ubuntu Click applications - minimal package built manually

Note: This has been obsoleted by the move from schroot to LXD. See this post.

There is some information available on the Ubuntu developer portal on building Click applications. But this mostly covers pure QML applications or QML applications with a C++ shared library, all with the Ubuntu SDK IDE (rebranded Qt Creator).
If you care about what is happening below this level, there is not that much info available, at least not in one place. I found:


  • Great blog post on the click command. This explains what happens below the hood when creating or upgrating "kits" (chroots with toolchains) and building.
  • Click specification (the source being here). This defines what a click package contains, how it relates to deb packages, how it's installed, etc. Unfortunately it doesn't contain all that is actually required of a Click package to work on a phone with Ubuntu.
  • Click security. This discusses the AppArmor file that needs to be included and how it's referred to from the manifest.
While useful, I find that the docs are often not up to date (i.e. things have moved on) and I miss something putting together all the parts.

So I thought I'd experiment with building Click packages a bit.
Let's try to create a minimal (sort-of) working Click package.

The simplest package can be created from a directory containing the following files:
  • manifest.json
  • $APPNAME.desktop
  • $APPNAME.png (or some other format) - the icon used by to display the app in the launcher
  • $APPNAME.apparmor - an AppArmor file specifying capabilities the application requires
  • the application itself
(I used $APPNAME to denote the name of the application. The individual files - desktop file, icon and AppArmor file don't necessarily have to have the same name.)
Note that  the AppArmor file is not strictly needed - a package without one seems to build and can be installed, but it's required by the Ubuntu Software Center. So let's use it. On the other hand the icon seems to be strictly required. If the desktop file does not contain Icon=..., package build fails.

A minimal manifest.json looks like this:
{
  "name": "zub.hello-world",
  "title": "Click minimal app",
  "description": "Test of a minimal click package.",
  "version": "1.0",
  "maintainer": "Author <author email>",
  "architecture": "armhf",
  "framework": "ubuntu-sdk-15.04",
  "hooks": {
    "hello-world": {
      "apparmor": "hello-world.apparmor",
      "desktop": "hello-world.desktop"
    }
  }
}

The name field specifies the name of the package. There are some rules for what the name has to fulfill, one of these being it must be lower case. If you break the rules, launching the package on the phone from Ubuntu SDK IDE fails with the not-so-helpful error message:
** (process:14951): WARNING : Unable to find keyfile for application 'PlainProjectTest.zub_PlainProjectTest_0.1'**

The conventions for package names are not clear for me. Canonical uses names like com.canonical.scopes.bbc or com.ubuntu.camera (so, the Java way), but many third-party packages use names like falldown.rpadovani, neverputt.lb or shorts-app.mrqtros (reverse of the Java style).

The architecture is either all (for pure QML apps that contain no native code), or a single architecture name (for phones what would typically be armhf), or even an array of architectures for multi-arch click packages.

The hooks key is important.  It contains a dictionary of application names mapped to the respective AppArmor file and desktop file.

A minimal desktop file can look like this:
[Desktop Entry]
Name=hello-world
Icon=./hello-world.png
Exec=hello-world
Terminal=false
Type=Application
X-Ubuntu-Touch=true

Setting Terminal=true would cause the package to fail to build. Launching apps in terminal is not supported on the phone. What a pity. :)

A minimal AppArmor file looks like this:
{
  "policy_groups": [
  ],
  "policy_version": 1.3
}

Typically the policy_groups array would contain an array of policy groups to allow access to e.g. sound playback or network. In the simplest situation nothing extra is requested and the array can be empty.

Let's use the following C++ source (hello-world.cpp) to build the application binary:
#include 

int main()
{
    std::cout << "Hello World!" << std::endl;
}

To build it manually the following could be used (assuming framework 15.04 & armhf target):
$ click chroot -a armhf -f ubuntu-sdk-15.04 run arm-linux-gnueabihf-g++ hello-world.cpp -o hello-world

The command consists of:
$ click chroot -a armhf -f ubuntu-sdk-15.04 run ...

which runs whatever follows the run argument inside a chroot for given arch and framework, and the actual compilation for armhf target:
... arm-linux-gnueabihf-g++ hello-world.cpp -o hello-world

The result is an armhf binary hello-world built in the current directory. (There are restrictons on which directories are accessible in the chroot.)

Copy the binary to the directory that contains the rest of the future package. The directory for building the minimal package should now contain the following files:
  • the application binary: hello-world
  • apparmor file: hello-world.apparmor
  • desktop file: hello-world.desktop
  • application icon: hello-world.png
  • click manifest: manifest.json
I will assume the directory containing these files is named pkg_source. Now to build the click package run (in the parent of the pkg_source directory):
$ click build pkg_source

This should build the click package, e.g.:
Now executing: click-review ./zub.hello-world_1.0_armhf.click
./zub.hello-world_1.0_armhf.click: pass
Successfully built package in './zub.hello-world_1.0_armhf.click'.

Hooray! To install the package on a phone, connect the phone via USB, unlock the phone (i.e. wake the phone up and unlock the lock screen, otherwise adb - which is what phablet-shell uses - does not connect), then run phablet-shell (contained in the package phablet-tools) to get a shell on your phone. Copy (e.g. via scp) the .click package to the phone, then in the phone's shell, install the package by:
pkcon install-local --allow-untrusted zub.hello-world_1.0_armhf.click

Why not install via click install? I don't know, but it seems to fails on the phone.

The new application should show up in the application list (sometimes it doesn't and refresh by pulling down at the top of the application list is required).The app icon should launch the application but as the application itself only outputs a line of text on standard output there is nothing to be seen.

It is possible to run the application manually on the phone, either using the Terminal application on the phone, or via phablet-shell. All click packages are installed in /opt/click.ubuntu.com/$PackageName/$PackageVersion. So the hello world can be launched via the command /opt/click.ubuntu.com/zub.hello-world/1.0/hello-world.

1 comment:

  1. Wow, this is very well written. And it addresses many issues finding an answer to on the internet is often impossible.

    ReplyDelete