HowTo: Building and testing QtMir in LXD

I’ve recently been wanting to ensure my Mir changes don’t break qtmir. Unfortunately, QtMir is actually rather hard to build in my Cosmic Cuttlefish development environment due to all the missing dependencies.

So I wanted to try testing in an LXD container; it’s easy to spin up a 18.04 container, which UBports supports. This mostly works, and for anyone else wanting a relatively easy development environment, here’s the process:

Spin up a 18.04 LXD container: lxc launch ubuntu:18.04 qtmir-builder
Enable the UBports repositories, and install the necessary dependencies:

  • echo "deb bionic main" | lxc exec qtmir-builder -- tee /etc/apt/sources.list.d/ubports-bionic.list
  • echo "deb-src bionic main" | lxc exec qtmir-builder -- tee /etc/apt/sources.list.d/ubports-bionic.list
  • wget -O - | lxc exec qtmir-builder -- apt-key add -
  • lxc exec qtmir-builder -- apt update
  • lxc exec qtmir-builder -- apt build-dep unity8

Now we need to map in the QtMir and Mir sources, like so:

lxc config device add qtmir-builder qtmir-source disk source=$PATH_TO_QTMIR_SOURCE path=/qtmir-source
lxc config device add qtmir-builder mir-source disk source=$PATH_TO_MIR_SOURCE path=/mir-source

And we now have QtMir and Mir sources available in the container.

Unfortunately, that’s not quite enough; the Mir build generates files in the source tree, which won’t work - the container users do not have (usually) write access to the source tree. We can “fix” this with chmod -R a+w $PATH_TO_MIR_SOURCE to make the Mir source tree world-writable.

We can now build in the container!
First Mir:

lxc exec qtmir-builder -- cmake -DCMAKE_BUILD_TYPE=Debug  -DCMAKE_INSTALL_PREFIX=/usr -B/mir-build -H/mir-source
lxc exec qtmir-builder -- make -C /mir-build -j9
lxc exec qtmir-builder -- make -C /mir-build -j install

And now QtMir (using ninja, because it’s faster):

lxc exec qtmir-builder -- apt install ninja-build
lxc exec qtmir-builder -- cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja  -DCMAKE_INSTALL_PREFIX=/usr -B/qtmir-build -H/qtmir-source
lxc exec qtmir-builder -- ninja -C /qtmir-build
lxc exec qtmir-builder -- ninja -C /qtmir-build install

We now have a full build of the Mir tree and QtMir built against it in the container; how do we test it?

Conveniently, LXD has the option to map GPU devices into containers:

lxc config device add qtmir-builder gpu gpu

Now qtmir-builder has access to all the GPUs on the system!.

However: the root user in the container isn’t privileged and doesn’t have access to /dev/tty*, so --console-provider=vt won’t work. Likewise, the container doesn’t have access to the host’s DBus bus, and so --console-provider=logind won’t work either. The new MinimalConsoleServices implementation will work, though.

Although drmSetMaster will fail (as the container user doesn’t have real root privileges), opening the drm device when there is no current DRM master will implicitly gain DRM master. Yay!

There’s one final problem - input. Although you can pass through arbitrary character devices (such as /dev/input/event3), it doesn’t seem like the libinput platform can access them correctly, possibly because they don’t show up correctly in udev? Anyway, we can test without input by loading the stub input platform.

So now, we can switch to a VT (so that any existing display server drops their DRM master) and run

lxc exec qtmir-builder -- sh -c 'MIR_SERVER_PLATFORM_INPUT_LIB=/usr/lib/x86_64-linux-gnu/mir/server-platform/ MIR_SERVER_PLATFORM_GRAPHICS_LIB=/usr/lib/x86_64-linux-gnu/mir/server-platform/ /qtmir-build/demos/qml-demo-shell/qtmir-demo-shell'

to get the QtMir demo shell running on the new Mir stack!

CLion Bonus Round!

One of the new features of the CLion IDE is remote build support. This means you can get full refactoring and source navigation support even if your local machine can’t build the project. Such as here, where 18.10 can’t build qtmir!

For this you’ll want a passphrase-less SSH key so CLion can access the LXD container without user interaction, and enable root logins in the container, like so:

ssh-keygen -t ed25519 -f ~/.ssh/local_lxd
ssh-add ~/.ssh/local_lxd
cat ~/.ssh/ | lxc exec qtmir-builder -- tee /root/.ssh/authorized_keys

And then you need to edit sshd_config:

lxc exec qtmir-builder -- editor /etc/ssh/sshd_config
lxc exec qtmir-builder -- systemctl restart ssh

and enable root logins.

In CLion you can now go to File→Settings→Build, Execution, Deployment→Toolchains and add a new “QtMir LXD” toolchain. Select “Remote Host”, configure the credentials to be the IP address of your lxd container, user: root, and use the OpenSSH config and authentication agent auth type.

Boom! You can now build QtMir in the LXD container from the comfort of your own IDE, and CLion will have a buildsystem to parse and generate all the code-completion goodness!


IMO the CLion integration could use its own topic. But a useful post nonetheless.