Bob builds Qt for the development team on a fast compute server. He packs Qt into a tarball and hands the tarball to his teammates. Alice installs the Qt tarball on her PC in a directory that differs from the installation directory used by Bob. As the target embedded system runs on an AMD Ryzen SoC with x86_64 architecture like the build server and the development PC, Alice installs Qt on the target system – yet in another directory. She can then try out the latest changes of her app directly on the target system. As Qt is relocatable since version 5.14, Alice’s and Bob’s jobs have become quite a bit easier.
Building Qt on a Compute Server
Bob builds Qt 5.14 on a fast compute server running Ubuntu 16.04 on an Intel x86_64 CPU. As the target system runs Ubuntu 18.04 on an AMD Ryzen CPU with x86_64 architecture, he uses Docker for the build.
Bob builds a Docker container based on Ubuntu 18.04 (see my post Using Docker Containers for Yocto Builds for how to install Docker and how to create a suitable Dockerfile). Bob creates a working directory, say /public/Work/RelocatableQt, on the compute server and downloads the Dockerfile.
$ mkdir -p /public/Work/RelocatableQt
$ cd /public/Work/RelocatableQt
$ wget https://raw.githubusercontent.com/bstubert/embeddeduse/master/BlogPosts/RelocatableQt/Dockerfile
Bob builds the container with the command
$ docker build --no-cache --tag "qt-ubuntu-18.04-ryzen" .
Bob downloads the source tarball of Qt 5.14.1 and unpacks it in the work directory.
$ wget http://download.qt.io/official_releases/qt/5.14/5.14.1/single/qt-everywhere-src-5.14.1.tar.xz
$ tar xf qt-everywhere-src-5.14.1.tar.xz
The source tarball is unpacked into the sub-directory qt-everywhere-src-5.14.1. Bob is ready to run the container, in which he will build Qt 5.14.1 from sources.
$ docker run -it --rm -v $PWD:/RelocatableQt qt-ubuntu-18.04-ryzen
The contents of the host work directory /public/Work/RelocatableQt are visible in the container at the location /RelocatableQt. The bash prompt inside the container is denoted by @
.
Bob performs a shadow build of Qt in the container.
@ mkdir build-qt-5.14.1
@ cd build-qt-5.14.1
@ ../qt-everywhere-src-5.14.1/configure \
-opensource -confirm-license -release -strip \
-feature-relocatable -prefix /RelocatableQt/qt-5.14.1 \
-qt-xcb -qpa xcb -icu -no-gstreamer -rpath \
-no-libudev -no-xcb-xinput -nomake examples -nomake tests -v \
-skip qtcharts -skip qtgamepad -skip qtlottie -skip qtwayland \
-skip qt3d -skip qtspeech -skip qtlocation -skip qtpurchasing \
-skip virtualkeyboard -skip qtwebengine -skip qtwebchannel \
-skip qtwebglplugin -skip qtwebsockets -skip qtwebview
@ make -j 32
@ make install
The option -feature-relocatable
enables the relocation of Qt. The option is enabled on Linux by default. The command configure -list-features
lists the feature as
relocatable ............. Enable the Qt installation to be relocated.
(Note: I couldn’t find this entry in the configure log of Qt 5.14.1. A reader of my newsletter, Pablo Rogina, confirmed that the entry shows up in the log of Qt 5.14.2. Thanks, Pablo ????)
The build installs Qt in the directory /RelocatableQt/qt-5.14.1 as given by the option -prefix
. For other development PCs and target systems, Bob will build a different set of Qt modules.
Back on the host system, Bob packs Qt into the tarball qt-5.14.1-ubuntu-18.04-ryzen.tgz and hands this tarball over to Alice.
$ cd /public/Work/RelocatableQt
$ tar czf qt-5.14.1-ubuntu-18.04-ryzen.tgz qt-5.14.1
When Bob automates the Qt build, he will move the commands for downloading the Qt source tarball and for building Qt into the Dockerfile. Then, Qt is built when the container is built. The resulting container could be the basis for automated builds of Qt applications.
Relocating Qt to Developers’ PCs
Alice has set up a PC running Ubuntu 18.04 on an Intel x86_64 CPU. She has installed the same packages as given in Bob’s Dockerfile and probably some more. Alice has cloned the project repository into ~/Work. She works on the application in the directory ~/Work/embeddeduse/BlogPosts/RelocatableQt.
$ mkdir ~/Work
$ cd ~/Work
$ git clone https://github.com/bstubert/embeddeduse.git
Alice unpacks the tarball from Bob in the directory ~/Qt.
$ mkdir ~/Qt
$ cd ~/Qt
$ tar xf /media/alice/usb5/qt-5.14.1-ubuntu-18.04-ryzen.tgz
Alice’s installation path /home/alice/Qt/qt-5.14.1 is totally different to Bob’s installation path /RelocatableQt/qt-5.14.1. This doesn’t matter any more, because Qt is relocatable.
In QtCreator, Alice opens the dialog Tools > Options > Qt Versions. She presses the button Add… and selects the executable /home/alice/Qt/qt-5.14.1/bin/qmake. She calls the Qt version Relocatable Qt 5.14.1. On the neighbouring dialog Tools > Options > Kits, she selects Relocatable Qt 5.14.1 for the field Qt version.
When Alice opens the project ~/Work/embeddeduse/BlogPosts/RelocatableQt/CMakeLists.txt for the first time, QtCreator asks her to configure the project. She selects the kit Relocatable Qt 5.14.1. When she has configured the project with another Qt version before, she goes to Projects > Build & Run > Build Settings and selects the kit Relocatable Qt 5.14.1 there. She switches from the old kit to the new one. She can now build and run the application SimpleApp.
Alice need not specify an LD_LIBRARY_PATH
when running the application. CMake automatically adds the path /home/alice/Qt/qt-5.14.1/lib
as a run-time link path.
Relocating a version older than Qt 5.14 fails. If Alice added qmake from an older version, QtCreator would flag this Qt version with a white exclamation mark on a red circle and with the error Invalid Qt version. CMake would emit the error message Cannot read /RelocatableQt/qt-5.12.7/mkdspecs/linux-g++/qmake.conf: No such file or directory.
Relocating Qt to the Target System
Alice wants to try out her changes on the target system, which is a Ubuntu 18.04 system running on an AMD Ryzen SoC with x86_64 architecture. The directory structure on the target system looks like this, where kea is the company or project name.
/home/user/kea
bin
SimpleApp
qt
lib
phrasebooks
plugins
qml
translations
The other Qt subdirectories bin, doc, include and mkspecs are only required for building the application. They need not be deployed to the target system.
Alice adds the following installation commands to CMakeLists.txt – after the add_executable
command.
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/../staging/kea)
install(TARGETS ${PROJECT_NAME} RUNTIME
DESTINATION bin)
set(qt_base_dir ${Qt5Core_DIR}/../../..)
install(DIRECTORY ${qt_base_dir}/lib ${qt_base_dir}/phrasebooks
${qt_base_dir}/plugins ${qt_base_dir}/qml
${qt_base_dir}/translations
DESTINATION qt USE_SOURCE_PERMISSIONS)
The install
commands install files and directories relative to ${CMAKE_INSTALL_PREFIX
}, which evaluates to ~/Work/embeddeduse/BlogPosts/staging/kea. The first install
copies the executable from the build directory to ${CMAKE_INSTALL_PREFIX}/bin. The second install
copies the directory trees starting at /home/alice/Qt/qt-5.14.1/lib, /home/alice/Qt/qt-5.14.1/plugins and so on to ${CMAKE_INSTALL_PREFIX}/qt/lib, ${CMAKE_INSTALL_PREFIX}/qt/plugins and so on, respectively. The staging directory structure below ${CMAKE_INSTALL_PREFIX} looks the same as the target directory structure below /home/user/kea.
It is important that the DESTINATION directories in the install
commands are relative (here: bin and qt). Absolute destination directories don’t work.
The above install
commands are only executed, if the install target is built. Alice goes to the page Projects > Build & Run > Build Settings. She clones the Release configuration and renames it into Final Release. She clicks on the button Add Build Step > Build and checks install in the Targets list. Then she builds the application. The staged files and directories can be found in ~/Work/embeddeduse/BlogPosts/staging/kea.
When Alice looks at the link command, she notices that the run-time link path still points to the Qt location on her PC /home/alice/Qt/qt-5.14.1 and not to the Qt location on the target system /home/user/kea/qt/lib. So, the application wouldn’t run on the target system without setting the LD_LIBRARY_PATH. Fortunately, Alice knows how to set the CMake variables related to rpath. These four set
commands must be placed before add_executable
. Otherwise, they have no effect.
set(CMAKE_SKIP_BUILD_RPATH true)
set(CMAKE_BUILD_WITH_INSTALL_RPATH false)
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH false)
set(CMAKE_INSTALL_RPATH /home/user/kea/qt/lib)
If CMAKE_SKIP_BUILD_RPATH is set to true, the linker doesn’t add the buid rpath (here: /home/alice/Qt/qt-5.14.1/lib). As the directory structure for the build and installation differ, the variables CMAKE_BUILD_WITH_INSTALL_RPATH and CMAKE_INSTALL_RPATH_USE_LINK_PATH must be set to false. The variable CMAKE_INSTALL_RPATH specifies the run-time paths used by the executable when it is run on the target system.
The linker doesn’t write the run-time path into the executable. Instead, it reserves enough space in the executable with the option -Wl,-rpath,:::::::
. The install step replaces the colons in the executable with the actual run-time path (here: /home/user/kea/qt/lib).
Alice packs the directory structure below kea into a tarball.
$ cd ~/Work/embeddeduse/BlogPosts/staging
$ tar czf kea.tgz kea
She copies the tarball kea.tgz onto the target system and unpacks it there.
# cd /home/user
# tar xf kea.tgz
Finally, Alice runs her QML application on the target system.
# DISPLAY=:0 ./SimpleApp -platform xcb -plugin evdevtouch
Also Interesting
The trigger for my post was the official announcement by The Qt Company that Qt is relocatable from version 5.14. Before Qt 5.14, your best chance to relocate Qt seemed to be qt.conf. As I haven’t tried it, I don’t know whether this works.
My post Using Docker Containers for Yocto Builds describes how to install Docker and how to write a Dockerfile to build embedded Linux systems. The Dockerfile is very similar to the one used in this post.
My knowledge about configuring run-time paths comes from Chapter 25.2.2 RPATH of the book Professional CMake – A Practical Guide (5th edition) by Craig Scott.