On your development machine, press Ctrl+R (Run) in QtCreator to create and run your Qt application. If you want to run an application on an embedded system, then you must complete four tasks:
• Create a Qt cross-application for the target embedded system in a Docker container.
• Stop the application on the target system.
• Copy the application from the development PC to the target system using scp.
• Run the application on the target system.
Would you like to press Ctrl+R in QtCreator and have QtCreator do the above four steps for you? Of course they did! This article will show you how to do it. Launching an application on an embedded system will be similar to launching an application on a PC.
Setup
Industrial terminals (such as computer displays) run applications for monitoring and controlling machines. They often run on Intel or AMD processors with x86_64 architecture. Ubuntu is a pretty natural choice for an operating system. The Ubuntu desktop user interface may be disabled as the terminal only launches the main application, and there may be one or two sub-applications.
Setting up a development environment for industrial terminals is quite simple. Your development PC is running some version of Ubuntu (developer installed: Ubuntu 16.04). The target system is running a different version of Ubuntu (from the developer: Ubuntu 18.04). A cross build environment in a Docker container is nothing but an Ubuntu 18.04 environment with all packages installed to build the Qt libraries and the application.
You don't even need a custom-made terminal. You only need two Linux PCs that are connected via (W)LAN and can communicate via OpenSSH with each other.
If the target system is running on an ARM SoC, setting up a cross-build environment in a Docker container will be a bit trickier. You must build the Qt SDK (for example, by creating a meta-toolchain-qt5 Yocto (target) target) and install the Qt SDK in a Docker container. The container hides the cross-build environment from QtCreator. QtCreator does not know if the container is calling the cross compiler for ARM targets or the native compiler for Intel targets.
If you want to follow the sample project or your own project, the following prerequisites must be met.
You install Docker on your development machine as described here.
You create a working directory (for a developer: /public/Work ) on your development PC. You clone the repository with the example project or your project into your working directory.
$ cd /public/Work $ git clone https://github.com/bstubert/qtcreator-with-docker.git $ cd qtcreator-with-docker
The project directory contains a Dockerfile. Set WORKDIR to the last line of your working directory. This is important, as you will see in the following sections. For a developer, the last line looks like this:
WORKDIR /public/Work
You then follow this description to create the qt-ubuntu-18.04-ryzen Docker image and use this image to build Qt relocatable libraries (Qt 5.14 or later). Building Qt gives you the qt-5.14.1-ubuntu-18.04-ryzen.tgz tarball, which you unpack in your working directory.
$ cd /public/Work $ tar xf /path/to/qt-5.14.1-ubuntu-18.04-ryzen.tgz
Unpacking installs the Qt libraries in the /public/Work/qt-5.14.1 directory.
You have a working OpenSSH connection between your PC and the target system. Password authentication works fine.
Docker Wrapper for CMake
When you build and install a Qt application, QtCreator calls CMake:
• create Makefiles (Make-files). It causes:
cmake /public/Work/qtcreator-with-docker '-GCodeBlocks - Unix Makefiles
in a temporary directory, for example
/tmp/QtCreator-jZQYdh/qtc-cmake-caIYzSxO
.
• Compile and link the Qt application. It causes:
cmake --build . --target all
in the build directory, for example
/public/Work/build-RelocatableQt-Qt_5_14_1-Debug
.
• install the Qt application and its supporting files. It causes:
cmake --build . --target install
in the build directory.
The idea is to call CMake inside a Docker container. Before QtCreator calls CMake, it switches to a specific directory on the development machine or host. This directory must be passed to the container so that CMake can be called at the right place inside the container. The CMake shell script dr-cmake does the following.
#!/bin/bash docker run --rm -v /public/Work:/public/Work -v/tmp:/tmp -w $(pwd) qt-ubuntu-18.04-ryzen cmake $@
The -v /public/Work:/public/Work and -v/tmp:/tmp options reflect the working and temporary directory tree from host to container. The w $(pwd) option passes the host directory where QtCreator calls CMake as the current working directory to the Docker container. Docker runs cmake with the $@ arguments passed to the dr-cmak e script.
The dr-cmake script translates QtCreator actions on the host machine into the same actions in the Docker container. This translation only works because the host machine and the Docker container have the same directory structure.
Copy the dr-cmake wrapper script to a directory contained in $PATH (eg ~/bin) and make sure the script is executable.
SSH access to the target system (Target System)
QtCreator uses OpenSSH to copy files from the development PC to the target system. So, OpenSSH must be installed on both your machine and the target machine. QtCreator does not work with Dropbear, a lightweight OpenSSH alternative for embedded systems.
For SSH login, QtCreator offers password authentication and public key authentication. Password authentication requires you to enter a password each time you deploy an application to a target system. This quickly becomes tiresome. So you want to use public key authentication.
To do this, you create private (private) and public (public) keys on your PC using ssh-keygen .
$ ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/home/burkhard/.ssh/id_rsa): /home/burkhard/.ssh/touch21-id_rsa Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/burkhard/.ssh/touch21-id_rsa. Your public key has been saved in /home/burkhard/.ssh/touch21-id_rsa.pub. ...
You leave the passphrase blank by pressing Return and notifying the SSH agent of the new key.
$ ssh-add ~/.ssh/touch21-id_rsa
Copy the public key to the target system.
$ scp ~/.ssh/touch21-id_rsa.pub benutzer@192.168.1.82:/home/benutzer/.ssh
Replace benutzer and 192.168.1.82 with the IP address of the target system with your username on the target system.
Log in to the target system and add the public key to the ~/.ssh/authorized_keys file.
On host: $ ssh benutzer@192.168.1.82 => Enter your password On target: # cat ~/.ssh/touch21-id_rsa.pub > ~/.ssh/authorized_keys
The next time you log on to the target system, you no longer need to enter a password. SSH checks if the user logging in has the private key of one of the public keys stored in ~/.ssh/authorized_keys . QtCreator will use the same mechanism to deploy application files.
Building a Docker Qt Kit
You need to define a suite that uses the dr-cmake script instead of cmake and knows how to login to the target system with SSH, deploys Qt application files and libraries to the target system.
Setup: CMake
Open the Tools > Options > Kits > CMake dialog box (Tools > Options > Kits > CMake) * and click the Add button. Fill in the fields as shown in the screenshot and click the Apply* button.
Setting: Qt Version
Go to the single-level dialog Tools > Options > Kits > Qt Versions (Tools > Options > Kits > Qt Versions) * and click the * Add (Add) * button. Navigate to the QMake binary of the version of Qt you have installed in your working directory. For example, a developer's QMake is located at /public/Work/qt-5.14.1/bin/qmake . Prefix Version name with Docker to make this version of Qt easy to recognize. Click the Apply button to save the configuration.
Setting: Device
Go to the Tools> Options> Devices dialog (Tools> Options> Devices) * to add information for logging into SSH. On the Devices tab, click the Add button, select Generic Linux Device in a separate pop-up dialog box, and click the Start Wizard* button. For a developer, the first page of the wizard looks like this:
You will have different values for name, IP address and username. Click the Next button to go to the second page of the wizard. You navigate to the SSH private key you created earlier.
Since you have already deployed the public key, you are taken to the third and final page of the wizard by clicking the Next button. The last page looks like this.
Click the Finish button. The dialog for a successful connection test looks like this.
A fully completed form for a new Touch21 device looks something like this.
Setup: Kit
You have defined all the parts: CMake, Qt Version and Device to configure the Kit. Return to the Tools> Options> Kits> Kits dialog box (Tools> Options> Kits> Kits) * and click the button * Add (Add) . Fill out the form as shown in the following screenshot.
Click the Change button for CMake Configuration at the bottom and replace the contents of the dialog with the following three lines.
CMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} CMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} CMAKE_PREFIX_PATH:STRING=/public/Work/qt-5.14.1
Don't forget to replace the developer's working directory /public/Work with yours. This is what the dialog should look like before clicking the OK button.
Project configuration
If you open the project file /public/Work/qtcreator-with-docker/CMakeLists.txt for the first time, QtCreator will ask you to set up the project (see the following screenshot). Select the Docker Qt 5.14.1 kit you just created and click the Configure Project button.
If you previously opened a project with a different configuration, then you click on the entry Docker Qt 5.14.1 in the list below Build & Run and on the subitem Build .
In both cases, you will see typical CMake messages in the General Messages output pane when creating Makefiles for the first time. Look at the first line: QtCreator calls the script dr-cmake instead of cmake . The Docker container is blocking the socket connection that the CMake option -E server is trying to establish. This causes the error messages QLocalSocket::connectToServer: Invalid name . However, calling CMake works.
Running "/home/burkhard/bin/dr-cmake -E server --pipe=/tmp/cmake-.JRorEM/socket --experimental" in /tmp/QtCreator-jZQYdh/qtc-cmake-cbJJnSrf. QLocalSocket::connectToServer: Invalid name ... Starting to parse CMake project, using: "-DCMAKE_BUILD_TYPE:STRING=Debug", "-DCMAKE_CXX_COMPILER:STRING=/usr/bin/g++", "-DCMAKE_C_COMPILER:STRING=/usr/bin/gcc", "-DCMAKE_PREFIX_PATH:STRING=/public/Work/qt-5.14.1". The C compiler identification is GNU 7.4.0 The CXX compiler identification is GNU 7.4.0 ... Check for working CXX compiler: /usr/bin/g++ Check for working CXX compiler: /usr/bin/g++ -- works Detecting CXX compiler ABI info Detecting CXX compiler ABI info - done Detecting CXX compile features Detecting CXX compile features - done Configuring done Generating done CMake Project was parsed successfully.
Open the build settings Projects > Build & Run > Docker Qt 5.14.1 > Build and click the Add Build Step > Build button. Check the install in the Targets field and remove all other targets. The following screenshot shows the final build settings. Note that dr-cmake is used in the build and cleanup steps instead of cmake .
Switch to launch settings Projects > Build & Run > Docker Qt 5.14.1 > Run . Delete the Install partition in the temporary host directory by hovering over the Details button and clicking the cross to the left of the Details button. The rest of the deployment section is fine.
Once you have built the project at least once, you will see all Files to deploy in a window with the same name. The file list tells QtCreator which remote directories it should copy local files to when it deploys. Here is an example entry:
/public/Work/qt-5.14.1/lib/libQt5Multimedia.so.5.14.1 -> /home/benutzer/MyComp/qt/lib/
QtCreator reads the mapping from local files to remote directories from QtCreatorDeployment.txt file. The add_deployment_file and add_deployment_directory macros write map entries to QtCreatorDeployment.txt .
The display contains two entries for the application's executable file.
/public/Work/build-qtcreator-with-docker-Docker_Qt_5_14_1-Debug/final/bin/SimpleApp -> /home/benutzer/MyComp/bin /public/Work/build-qtcreator-with-docker-Docker_Qt_5_14_1-Debug/SimpleApp -> /home/benutzer/MyComp/.
The second entry comes from a call to install(TARGETS). The executable is the result of a CMake build step. It contains a sequence of colons instead of rpath. It won't work on the target system because it won't find the Qt libraries. The CMake setup step replaces the colon sequence with relative rpaths. The result of the install step is the first entry executable that results from the call to add_deployment_file. The first entry's executable is the one that QtCreator will run on the target system.
If you are using QtCreator 4.11.0 or later and CMake 3.14 or later, you will no longer need the QtCreatorDeployment.txt workaround. QtCreator and CMake work together to create a mapping from setup commands. However, Ubuntu 18.04 ships with CMake 3.10 so you still need a workaround.
The Run section in Run configuration must be set to SimpleApp (on Touch21) . In the field under Run configuration , enter the following values.
• In the
Alternate executable on device
line, check
Use this command instead
and enter the full path to the executable on the target device (for developers:
/home/benutzer/MyComp /bin/SimpleApp)
.
• On the
Command line arguments
line, enter the arguments required by the application (developer:
-platform xcb -plugin evdevtouch)
.
In the Run Environment section, you add the DISPLAY variable with the value: 0.
Launching the application on the target system
Now comes the magic moment. You press
Ctrl+R (Run)
in QtCreator. QtCreator creates an application, deploys the application and Qt libraries to the target device, and runs it on the target device. All this in one step.
You see the QtCreator Docker-CMake calls and the deployment calls on the
Compile Output pane
. Here is a shortened version (excluding compiler and progress messages).
11:19:37: Running steps for project SimpleApp... 11:19:37: Persisting CMake state... 11:19:37: Starting: "/home/burkhard/bin/dr-cmake" --build . --target all ... 11:19:39: The process "/home/burkhard/bin/dr-cmake" exited normally. 11:19:39: Starting: "/home/burkhard/bin/dr-cmake" --build . --target install ... 11:19:43: The process "/home/burkhard/bin/dr-cmake" exited normally. 11:19:43: Connecting to device "Touch21" (192.168.1.82). 11:19:44: The remote file system has 985 megabytes of free space, going ahead. 11:19:44: Deploy step finished. 11:19:44: Trying to kill "/home/benutzer/MyComp/bin/SimpleApp" on remote device... 11:19:45: Remote application killed. 11:19:45: Deploy step finished. 11:19:45: sending incremental file list 11:19:45: SimpleApp ... total size is 751,048 speedup is 1.00 11:22:24: Deploy step finished. 11:22:24: Elapsed time: 02:47.
The first deployment takes a couple of minutes (it took the developer 2:47 minutes) because QtCreator copies parts of the Qt runtime from the developer's PC to the target system. As long as Qt doesn't change, you can skip deploying Qt. Go to the build settings
Projects > Build & Run > Docker Qt 5.14.1 > Build
, set the DEPLOY_QT variable to OFF in the
CMake
section, and click the
Apply Configuration Changes
button.
Your workflow is now the same as if you were running the application on your development PC. You change your code and build, deploy and run the application by pressing
Ctrl+R
in Qt Creator and then you can try your changes on the target system. You get immediate feedback on how your changes behave on the target system.