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:
For Windows, say a Qt widgets app, they are at least
Qt5Core.dll
,Qt5Gui.dll
andQt5Widgets.dll
. For Linux, they’relibQt5Core.so.5
,libQt5Gui.so.5
andlibQt5Widgets.so.5
and on the Mac they’reQtCore.framework
,QtGui.framework
andQtWidgets.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 thatrpath
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 thatrpath
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
(orpatchelf
) 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 therpath
can not be changed easily, you’ll have to run Apple’sinstall_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:
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
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-inlsof
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 pluginqwindows.dll
needslibEGL.dll
, and in Linuxlibqxcb.so
depends onlibQt5DBus.so.5
andlibxcb.so
for the X server stuff. Also an additional gotcha: for non-OpenGL MSVC-flavored deployments, thatlibEGL.dll
file has be placed not together with theqwindows.dll
but together with the other Qt dlls (because while Qt loads the pluginqwindows.dll
, it’s the OS that loads other files it depends on, likelibEGL.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 likesetWindowState(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 loadQt5Core.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 subdirectoryplatforms
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 isplugins
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):
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 editedQt5Core.dll
as above and copied it into \CompanyApps as well, then Qt expects to findqwindows.dll
in the directoryC:\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, notQt5Core.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
inC:\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
qwindows.dll
inC:\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 forqwindows.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 inC:\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 forqwindows.dll
inC:\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 theplatforms
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 linePlugins=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 separateplugins
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 calledPlugIns
.
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 inqt.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 changeplatforms
to some other, even an absolute pathname. Note that this is different from the previous 5 choices, since they are for specifying theplugins
main directory (where the subdirectories are), while this setting is for changing only theplatforms
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 toplugins
, then iftest
is a subdirectory inplugins
, 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.
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?
than you so much …
i am looking for something like this 🙂
Your posts on deployment are extremely helpful! Any chance you would write the one about Qt Quick deployment?
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.
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.
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?
Why I could not find “qt_plugpath” string in Qt5Core.dll? my Qt version is 5.9.7 .
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”.