Deploying a real Qt app – understanding more of Qt

I’ve written about deploying a simple Qt app, but what about deploying a more complex, “real” Qt app. This means copying all the needed files: besides the main executable file there usually are some shared libraries, e.g. DLLs, and some other files like configuration or certificate files. In this blog post I’ll concentrate on getting the binary executable files setup correct; of course there can be other important files, like the qmldir declaration file in the QtQuick.2 subdirectory etc. but that’s a topic for another blog post.

First there’s the option of static linking i.e. one build that packs all of Qt’s DLLs and your code together in a single .exe file. This hugely simplifies deployment, but it’s a time consuming process that can be tricky; of course if you distribute large quantities of your software, static linking is definitely something you should try.

But if you’re not yet targeting a couple of millions of PCs (or a similar number 🙂 ) then you’re building your Qt app the normal, dynamic way. And that app structure in Qt depends heavily on shared or dynamically loaded libraries; on your deployment PC the Qt installer sets these up for you, but on other PCs you need to do it yourself. You can either use an installer, for example Qt has a good one, or copy the files manually.


A good idea here is to find a way to install your Qt app on a vanilla computer with minimal intrusion, because maybe you don’t have administrative or physical access to it, instead perhaps relying on a network share or FTP. For Windows this means no writing to the registry (once the app is installed, it can use the registry for preferences or similar stuff, but this is about the install process) or create files in “system” locations like C:\Windows etc. For Linux and Macs, it means no fiddling with .conf files in /etc and no file copying to /usr/lib or similar places.

First step anyway is to build your app in release mode and collect all the needed files together. If you’ve read my earlier post about distributing a bare-bones Qt app, the files and directory structures used in that post are a good start. Also while it is possible to compile and build a Windows Qt app on Mac, here I’m assuming you’re using a Windows PC for Windows deployment, a Mac for Mac deployment and a Linux installation for Linux stuff. (Re. Linux: I’m only using/testing Ubuntu and Debian, and on these the Qt installation files are the same, but other distros may differ here.)

The DLL (or .so and .dylib) files that a Qt app needs for successful launch can be divided into 3 flavors:

  • Qt specific DLLs:

    Qt dll For Windows, say a Qt widgets app, they are at least Qt5Core.dll, Qt5Gui.dll and Qt5Widgets.dll. For Linux, they’re libQt5Core.so.5, libQt5Gui.so.5 and libQt5Widgets.so.5 and on the Mac they’re QtCore.framework, QtGui.framework and QtWidgets.framework. A “real” Qt app most likely requires some more.

    Note: for a typical Qt development installation, the library files/DLLs can be found in two different places, one is in your compiler directory, for example …\mingw48_32\bin, and the other is a subdirectory of QtCreator. Don’t use the files from the latter place, those DLLs in QtCreator’s subdirectories are for QtCtreator’s own use, it’s a Qt app (of course!). In some cases, like in Linux, the DLLs are identical to the ones in the compiler directory; in other cases, like for a MinGW-flavored installation of Qt on Windows, they are not (QtCreator is built using Visual Studio and those .dlls do not work for a Qt app built with MinGW!). Instead, always use the library files in your compiler’s …\bin directory.

    A Qt app, like other apps, relies on the OS to load those library files, which means that they must be placed where the OS can find them. On Windows, this means either next to the .exe file in the same directory or in a directory specified in the search path, for example C:\Windows\System32. On a PC with Qt installed, the DLLs are located by adding the directory to the search path, either by QtCreator when you run your app from it, or by manually invoking qtenv2.bat in Qt’s compiler’s bin directory.
    When copying the DLLs to another PC without Qt installed, usually you don’t want to mess with the search path on other computers, instead placing the DLLs together with the .exe file is probably the best solution.

    On Linux, placing the .so files next to the .elf file in the same directory does not cause them to be found/loaded automatically like in Windows; you can add directories to the search paths or ld.so’s .conf files, but a less intrusive way is using the rpath setting in the .elf file, and that’s what Qt does. On your development PC with Qt installed, when building your app, QtCreator sets the rpath on your .elf file so that it points to the lib subdirectory inside Qt’s installation directory (where the .so files for Qt are located).
    This is quite handy when you want to launch your app from Nautilus or Terminal (don’t need any .bat files like in Windows!) but when you later deploy your app to another Linux computer without Qt installed, then that rpath setting is guaranteed to be wrong 🙁 and needs to be fixed.
    Note: you can get lucky, for example on Ubuntu, there usually is a Qt installed in /usr/lib/i686-linux-gnu or /usr/lib/x86_64-linux-gnu, so your app might run anyway (ld.so, the dynamic linker/loader, gets those paths from the .conf files in /etc/ld.so.conf.d). But beware of the versioning problem, the Qt supplied by Ubuntu might be too old for your app to launch ok.
    For how to fix that rpath setting, look at my my previous post about deploying a bare bones app for Linux.

    On Macs also there’s no default loading of .dylibs when they’re placed together with the .elf file. Apple in Leopard (10.5) introduced an rpath setting similar to the one in Linux. One minor problem is that there’s no chrpath (or patchelf) available for OSX to set the rpath; i.e. it can only be set at link time from QtCreator. And another complication is that the library files are (mostly) placed separately, each in their own directory structure.
    All this means, while it’s no problem launching your Qt app from Finder or from Terminal on your dev. Mac with Qt installed, you’re up the creek without a paddle when deploying your Qt app to another Mac with no Qt on it. Since the rpath can not be changed easily, you’ll have to run Apple’s install_name_tool on your .elf file multiple times, specifying the new path to each .dylib and framework of Qt it depends on. And for every Qt .dylib/framework that has a dependency on another .dylib/framework, e.g. QtGui.framework depends on QtCore.framework, those needs a whacking with install_name_tool as well.
    Thankfully, someone with a brain has written a utility for this called macdeployqt. You run it on your development Mac, I’ve written about in my previous post.
     

  • C++ compiler specific DLLs:

    Visual C++ redist installs

    Some MSVC redist installs on a typical Windows PC

    While all 3 platforms depends on these kinds of .DLLs, on Linux and Macs it’s presumed that those files (like libstdc++.so.6 or libstdc++.6.dylib) are present and recent enough to work, but on Windows, mostly because of the variety of compilers, this needs to be addressed when deploying to another PC. The easiest is to copy them together with the Qt specific DLLs, and in my previous post I show you how.

    However, for the Microsoft compiler DLLs, they recommend not just simple copying them (although that works just fine). Instead you should copy and install a vcredist package (can be found in the Qt’s vcredist subdirectory), thereby installing the MSVC dlls not together with your app, but somewhere within the bowels of C:\Windows.

    The reason for this extra step is to give Microsoft an opportunity to update those .dlls if needed, in case they find a bug or security problem in them. This is in theory a good idea, but for me, it also has during the years meant a couple of early Monday morning calls from customers complaining about my broken app. And discovering that the reason was Microsoft updating their dlls. To be fair, this was not the Visual C++ dlls, two that I can remember were: ATL security update and an ADO update.
     

  • DLLs loaded by Qt itself a.k.a. plugins:

    Qt plugin dlls Welcome to an interesting nook and cranny of Qt deployment, the DLLs that Qt loads by itself! The DLLs mentioned above, they are all standard DLLs loaded by the OS which means you have to know how Windows, Mac and Linux loads library files. But also if a file is missing, you will get an error, a message box or some other diagnostic message, like “This application has failed to start because Qt5Core.dll was not found.” then you have something to google on.

    Qt plugins on the other hand are more sinister in this regard, for many of them when they are missing/not found in the expected place, your Qt app will fail silently 🙁 No error message or diagnostic output, just an app that doesn’t start or exhibits reduced functionality. For example, a Qt Quick app that cannot find the needed QML/QtQuick plugins will instead display an empty white window. Also when trying to find the DLLs your app depends on, when running a tool like Dependency Walker or ldd on Linux, the Qt plugin DLLs will not show up because these tools only display the normal DLLs requested/imported by the app, and not the plugin DLLs that Qt only knows about. To see them, you need to first launch your app (so that they are loaded into memory) and then use a tool like ListDLLs for Windows or the built-in lsof program in Mac and Linux.

    Another gotcha re. these plugin DLLs: even though you are super sure you’ve placed them in the correct place, you can still be greeted with “… could not find or load the Qt platform plugin…” This can happen because a DLL that these guys depend is missing. Since Qt is the loader (and not the OS) you won’t get the usual diagnostic “not found” message. Running Dependency Walker or ldd for Linux on the DLL in question, can be helpful for debugging these kind of errors.
    For example, the Visual Studio-flavored non-OpenGL platform plugin qwindows.dll needs libEGL.dll, and in Linux libqxcb.so depends on libQt5DBus.so.5 and libxcb.so for the X server stuff. Also an additional gotcha: for non-OpenGL MSVC-flavored deployments, that libEGL.dll file has be placed not together with the qwindows.dll but together with the other Qt dlls (because while Qt loads the plugin qwindows.dll, it’s the OS that loads other files it depends on, like libEGL.dll).

     
    So how does Qt load these plugins, what are the rules? At start, Qt makes a list of directories where it hopes to find the plugin DLLs. For such a plugin directory to be valid, it has to contain some subdirectories with predetermined names like: platforms,sqldrivers,imageformats. These subdirectories are where Qt expects to find the plugins DLLs. It does so by enumerating all files in the subdirectory, looking for matching plugin keys. When you install Qt, it creates such a plugin directory directly below the compiler, for example .../clang_64/plugins .../gcc_64/plugins or ...\mingw48_32\plugins. If you look at them, they contain something like 15 different subdirectories. For deploying your app, most likely you only need to copy one or two of those.

    The most important plugin type is the platforms subdirectory. Those DLLs are called platform plugins and are needed by all Qt apps (even a minuscule Qt app like the bare bones one in my previous post), and the default one is qwindows.dll (libqxcb.so for Linux or libqcocoa.dylib for the Mac). This is Qt’s QPA (Quarter Pounder Advanced™ just kidding, it stands for Qt Platform Abstraction layer), it’s responsible for many OS-specific things, for example translating calls like setWindowState(Qt::WindowMaximized) to Windows/Linux/Mac specific system calls.

    I mentioned Qt doesn’t care about the plugin filenames, only about the subdirectory names, let me give you an example:

    Filename doesn't matter

    The filenames for plugins are immaterial


    If I rename qwindows.dll to grapefruit.jpg, Qt will still happily load it as a DLL. Contrast that with for example when Windows wants to load Qt5Core.dll, it can be in any directory along the PATH or next to the .exe file for the app, Windows doesn’t really care, as long as the filename is correct. Qt instead fubars if the subdirectory platforms cannot be found.

    Thankfully, for these platform plugins, Qt do display an error message in case they cannot be found. In Windows Qt displays a message box, for Macs and Linux these errors usually can be seen when you launch your app from Terminal. (True also for the normal DLLs.) So if Qt’s plugins are not deployed correctly, that error message is a good diagnostic, my previous post shows these errors in more glorious detail.

    Let’s look at how Qt’s list of plugin directories are determined at app start, and how you can use these rules for successful app deployment.
    Remember that this list specifies the main plugin directory (which is plugins when you install Qt) and that the real McCoys, the plugin DLLs themselves, are stored in the subdirectories below it.

    Actually there are 5 (!) different choices for establishing the plugin directory:

    • Binary editing of Qt5Core.dll (or libQt5Core.so.5 or QtCore.framework/QtCore):

      Hexediting Qt's plugin path

      Hexediting Qt’s plugin path


      This is the big wheel approach to plugin lookup, favored by Qt’s installer. When you install Qt on your development PC, the installer patches Qt5Core.dll with the path you specified for your convenience. This means whenever you start Qt apps on your development machine, the plugins will always be located fine and dandy. (From the point of view of deployment to another PC, this instead could be considered a disservice. Nevermind.)

      To see the current path setting:

      Windows: find "qt_plugpath" Qt5Core.dll
      Linux: strings libQt5Core.so.5 | grep qt_plugpath
      Macs: strings QtCore.framework/QtCore | grep qt_plugpath
      

       
      Then if you’re into hex editing yourself, you can apply a new setting for deployment:

      Hexediting Qt's plugin path

      Changing to a path more suitable for deployment


      You can change to another absolute path, but a relative path works too, it means Qt will use the directory where the .exe file is and append the plugin path to it.

      For example, assume you’ve placed your Qt app in the directory C:\CompanyApps. Then if you’ve hex edited Qt5Core.dll as above and copied it into \CompanyApps as well, then Qt expects to find qwindows.dll in the directory C:\CompanyApps\plugins\platforms.

      When I tested on Windows with a relative path I had to prefix it with C: which obviously doesn’t work on Linux and Macs. So while this is a viable method for plugin deployment, I suggest you leave this option to the big wheels 🙂

      Note: remember if you’re running your app in Debug mode then the settings in Qt5Cored.dll will apply, not Qt5Core.dll.
       

    • Setting the environment variable QT_PLUGIN_PATH:

      Using an environment variable might be an easier option than hex editing Qt5Core.dll, and the effect will be the same, for example:

      set QT_PLUGIN_PATH=plugins
      

      will perform the same feat as above, Qt will look for qwindows.dll in C:\CompanyApps\plugins\platforms. Of course you can also set an absolute path this way. And if you want a bonanza of plugins directories, you can append additional paths separated with ; (or : in Linux and Macs).
       

    • Specifying the plugin path in your code:

      Adding a plugin path in main()

      Adding the plugin path in main()

      You can also specify a plugin path in your code, but since Qt loads the plugins when QApplication is constructed, your window of opportunity is to do it before QApplication’s constructor is called. The call above is a 3rd alternative for telling Qt to look for qwindows.dll in C:\CompanyApps\plugins\platforms (using the same example as above).
       

    • Creating a qt.conf file:

      This is a popular method of setting up a path to the plugins, used for example by the QtCreator app and the macdeployqt utility on Macs. Create a text file, like this:

      [Paths]
      Plugins=plugins
      

      If such a file exists in the same directory as the app’s .exe file, Qt will read it and add the plugins= path to its list of plugin directories. The example above accomplishes the same thing as in the previous examples, it causes Qt look for qwindows.dll in ...\plugins\platforms.

      One caveat here if you’re on Windows: backslashes in the Plugins= setting don’t work, you should use Linux-style forward slashes.
       

    • Using the default directory:

      This is by far the favorite method for setting up the plugin directory, since it’s provided for free by Qt 🙂
      How does this work? Well, when your app starts, Qt as a freebie adds the directory where your app’s .exe file is located to its list of plugin directories.

      Note that this is not the same as the examples above, in those we instructed Qt to look for plugins in C:\CompanyApps\plugins but this default setting tells Qt to look for plugins in C:\CompanyApps. I.e. an equivalent env. variable setting would be:

      set QT_PLUGIN_PATH=.
      

      or an equivalent qt.conf file would look like this:

      [Paths]
      Plugins=.
      

      So if we take the example above again; you’ve deployed your app to the directory C:\CompanyApps. This means that Qt automatically will look for qwindows.dll in C:\CompanyApps\platforms. For example, if you look at my previous post you’ll see that for deployment of that bare bones app, I always go for this option, i.e. placing the platforms directory directly below the main directory.

      Q: “Then why does Qt Creator go through the trouble of creating a qt.conf file for itself, when it suffices just to copy the plugin subdirectories into the app’s directory?”

      One reason could be to reduce litter in the app’s main directory, if we look at Qt Creator’s qt.conf file, it has the line Plugins=plugins (same as in our examples above). Since it has 7 different plugin subdirectories (9 in Linux) it makes sense to stash away all those subdirectories in a separate plugins directory.

      Another reason for creating a qt.conf file: macdeployqt creates one, because on the Mac there’s an app bundle standard that plugins should reside in their own directory called PlugIns.

    Remarks: if you use a qt.conf file, it supersedes the other settings, so if you make a spelling error in it, that can cause plugin loading to fail. Also the last two methods (the most popular ones) are currently slightly more brittle due to an obscure bug, for example if you call QStyleFactory::keys() before QApplication a(argc, argv) in your main.cpp, this preempts the path to the .exe file and a path in qt.conf to be setup as eligible plugin directories.

    Finally, if you’re still running into plugin loading problems, you can turn on a trace of the plugin loading process, by setting the environment variable QT_DEBUG_PLUGINS to a nonzero value, like this:

    SET QT_DEBUG_PLUGINS=1
    

    (for Linux and Macs instead use export), and then launch your app from the CMD/Terminal window. For Linux and Macs you’ll see lines something like “QFactoryLoader::QFactoryLoader() checking….” displayed in Terminal, but in Windows Qt routes the output to the OutputDebugString() API, so nothing will be displayed in the CMD window. The output can be seen using Visual Studio or Qt Creator, which isn’t very helpful if you’re experiencing the plugin problems on a non-development PC. An alternative is to download a utility: DbgView from Sysinternals and open it before you launch your app you’ll be fine.

     

    Bonus contents if you are crazy and not happy with *only* 5 choices:

    Remember I said the Qt plugin subdirectory names like platforms,sqldrivers,imageformats are hardwired in Qt’s plugin loader? I lied, you can actually change platforms to some other, even an absolute pathname. Note that this is different from the previous 5 choices, since they are for specifying the plugins main directory (where the subdirectories are), while this setting is for changing only the platforms name or location (the settings are independent from each other). Why this is useful? Really don’t know, but I’m guessing for debugging/testing. Also note that this option is not available for Qt Console apps, only Quick- and Widgets-flavored ones.

    Say you want to change to instead use the subdirectory test, then you can either set/export an environment variable:

    set QT_QPA_PLATFORM_PLUGIN_PATH=test
    

    or give a command line argument when launching your app:

    YourQtApp -platformpluginpath test
    

    Note that if this is a relative path (like in my example) then it’s relative to the app’s .exe file, so for a more convoluted case, if you’re using a qt.conf file that overrides the default plugins directory . (where your .exe file is located), instead setting it to plugins, then if test is a subdirectory in plugins, you would instead have to type:

    YourQtApp -platformpluginpath plugins/test
    

 

Looking at other Qt apps:

Finally, to understand more of the deployment process, I think it’s useful to look at other (than your own) Qt apps; I’m thinking of one Qt app which is installed in your Qt folder: QtCreator. Besides creating the version specific (like 5.3) directory structure where the compiler files are, Qt’s installer also creates the Tools directory which contains QtCreator. On the Mac there is no Tools subdirectory, instead you’ll find a Qt Creator.app (which is a directory, you open it with a rightclick and Show Package Contents in Finder).

QtCreator is packaged ready to deploy, i.e. you can copy it (the directory or in the Mac case, the app) to another computer and it will start just fine. (For Windows, you might need to install the vcredist_msvc2010 runtime located in the QtCreator’s lib subdirectory.) If you copy it to a PC without a Qt installation, it might not compile and build apps, but it would still function well as a text/code editor 🙂

So let’s look on how it solves its deployment, first the normal DLLs:
In Windows, all the needed DLLs are placed together with qtcreator.exe in the same directory (except for the compiler specific DLLs, which is installed using the vcredist package I mentioned earlier).
In Linux, the .so files are in a neighboring subdirectory: ../lib/qtcreator, and for the .so files to be found by the ld.so loader, the .elf file has a custom RPATH/chrpath setting: "$ORIGIN/../lib/qtcreator".
In Macs, all the .dylib and framework files QtCreator needs are included in the app bundle (if you do otool -L on QtCreator’s .elf file in the MacOS folder, you’ll see for example @loader_path/../Frameworks/QtCore.framework....). It’s look quite similar (but not exactly the same) as when you run the utility macdeployqt on your own Qt app’s folder.

Then the plugin DLLs, how are they located? Well, if we start by looking at Qt5Core.dll, a strings search of it finds "qt_plugpath=c:/work/build/PADDING/plugins" (on Linux and Mac it’s more or less the same setting).

Obviously there’s no “/work/build” directory, so clearly the option of using Qt5Core.dll for specifying the plugin directory is unused by QtCreator.
(BTW, this is one of the reasons why this Qt5Core.dll is not replaceable with the Qt5Core.dll in the compiler directory, because the qt_plugpath in that one is used and expected to be valid.)

So, QtCreator does not set any environment variables and does not call addLibraryPath() in main.cpp, nor is the default directory option used. Instead (as I’ve already mentioned above) QtCreator has a qt.conf file to specify its plugin directory, using the relative path plugins.

If you want to experiment, momentarily rename or move away the qt.conf file, and then try to start QtCreator. You’ll see that infamous message “… could not find or load the Qt plugin …. “, same for any other Qt app 🙂 Don’t forget to restore the qt.conf file!
 
Next up: deploying Qt Quick apps.

Warning: get_comment(): Argument #1 ($comment) must be passed by reference, value given in /customers/4/5/3/tripleboot.org/httpd.www/wp-includes/class-wp-comment-query.php on line 484 Warning: get_comment(): Argument #1 ($comment) must be passed by reference, value given in /customers/4/5/3/tripleboot.org/httpd.www/wp-includes/class-wp-comment-query.php on line 484 Warning: get_comment(): Argument #1 ($comment) must be passed by reference, value given in /customers/4/5/3/tripleboot.org/httpd.www/wp-includes/class-wp-comment-query.php on line 484 Warning: get_comment(): Argument #1 ($comment) must be passed by reference, value given in /customers/4/5/3/tripleboot.org/httpd.www/wp-includes/class-wp-comment-query.php on line 484 Warning: get_comment(): Argument #1 ($comment) must be passed by reference, value given in /customers/4/5/3/tripleboot.org/httpd.www/wp-includes/class-wp-comment-query.php on line 484 Warning: get_comment(): Argument #1 ($comment) must be passed by reference, value given in /customers/4/5/3/tripleboot.org/httpd.www/wp-includes/class-wp-comment-query.php on line 484 Warning: get_comment(): Argument #1 ($comment) must be passed by reference, value given in /customers/4/5/3/tripleboot.org/httpd.www/wp-includes/class-wp-comment-query.php on line 484 Warning: get_comment(): Argument #1 ($comment) must be passed by reference, value given in /customers/4/5/3/tripleboot.org/httpd.www/wp-includes/class-wp-comment-query.php on line 484 Warning: get_comment(): Argument #1 ($comment) must be passed by reference, value given in /customers/4/5/3/tripleboot.org/httpd.www/wp-includes/class-wp-comment-query.php on line 484

9 thoughts on “Deploying a real Qt app – understanding more of Qt

  1. Thank you.
    Can you show me how to build Qt 5.3 statically with working QtQuick?
    Is there any utility for deploying Qt on Windows (with dynamic library) automatically?

    1. Hi, it’s coming but laziness etc 🙂
      Actually right now I’m working on my first Qt Quick project (a customized picture viewer for healthcare) , once that is up and running. definitely next thing on my list is that blog post. CU.

  2. I spent a couple hours this afternoon trying to figure out what dll’s I needed to include for a simple Qt deploy. Other sites are cryptic at best. Your description was a godsend … very clear and complete. Thank you.

  3. Great post but still not working for me. I have migrated code from an old Clipper app to Harbour with QT. It will run on an old XP machine fine. I have a new Windows 10 PC and I’m unable to get it to work. I’ve tried the environment variables and the qt.conf suggestion with forward slashes. None of these worked. Any other suggestion?

    1. Hi, starting with version 5.7 all but one of these qt_xxx strings are gone from Qt5Core.dll, I think the reason was to simplify. Only the main one “qt_prfxpath” is still present, and is used to set up the others like “qt_plugpath”.

Leave a Reply

Your email address will not be published. Required fields are marked *

Prove you're human * Time limit is exhausted. Please reload CAPTCHA.