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.)
Qt specific DLLs:
For Windows, say a Qt widgets app, they are at least
Qt5Widgets.dll. For Linux, they’re
libQt5Widgets.so.5and on the Mac they’re
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.batin 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
rpathon 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
rpathsetting 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
rpathsetting, 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
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
rpathcan not be changed easily, you’ll have to run Apple’s
install_name_toolon 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:
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:
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
lddon 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
lsofprogram 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
libEGL.dll, and in Linux
libxcb.sofor the X server stuff. Also an additional gotcha: for non-OpenGL MSVC-flavored deployments, that
libEGL.dllfile has be placed not together with the
qwindows.dllbut 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
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
platformssubdirectory. 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:
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
platformscannot 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
pluginswhen you install Qt) and that the real McCoys, the plugin DLLs themselves, are stored in the subdirectories below it.
Binary editing of Qt5Core.dll (or libQt5Core.so.5 or QtCore.framework/QtCore):
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:
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.dllas above and copied it into \CompanyApps as well, then Qt expects to find
qwindows.dllin the directory
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.dllwill apply, not
Setting the environment variable
Using an environment variable might be an easier option than hex editing Qt5Core.dll, and the effect will be the same, for example:
will perform the same feat as above, Qt will look for
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:
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
C:\CompanyApps\plugins\platforms(using the same example as above).
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:
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
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\pluginsbut this default setting tells Qt to look for plugins in
C:\CompanyApps. I.e. an equivalent env. variable setting would be:
or an equivalent
qt.conffile would look like this:
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
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
platformsdirectory directly below the main directory.
Q: “Then why does Qt Creator go through the trouble of creating a
qt.conffile 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.conffile, 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
Another reason for creating a qt.conf file:
macdeployqtcreates one, because on the Mac there’s an app bundle standard that plugins should reside in their own directory called
Remarks: if you use a
qt.conffile, 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.confto 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_PLUGINSto a nonzero value, like this:
(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,imageformatsare hardwired in Qt’s plugin loader? I lied, you can actually change
platformsto some other, even an absolute pathname. Note that this is different from the previous 5 choices, since they are for specifying the
pluginsmain directory (where the subdirectories are), while this setting is for changing only the
platformsname 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:
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.conffile that overrides the default plugins directory
.(where your .exe file is located), instead setting it to
plugins, then if
testis 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
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
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
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.