Qt Graphs vs Qt Charts. Objective Take on the New Data Visualization Solution
The Qt Graphs is a powerful module of the Qt framework that enables developers to create interactive, both static and […]
Development is not an only part of product delivering – deployment and maintenance are both equally important parts of the product lifecycle. That is why Qt lend us a hand by providing Installer Framework. It is a set of tools that allows not only to create good-looking and functional installers but also update the app, provide tools for maintaining it, and much more. In this tutorial, you will learn the basics of Qt Installer Framework and find out how to generate your first offline installer for Windows. Let’s go!
First, if you do not know Qt framework and what features it contains, check out our blog post about Qt and how to create GUIs using it.
Qt Installer framework can be easily installed via Qt Maintenance tool or downloaded from Qt repository. When using a maintenance tool, it can be found in the Developer and Designer Tools section:
After download it should be stored in [QtDirectory]/Tools/QtInstallerFramework
Before generating an installer, you have to prepare your app for deployment. The first step is to create a deployment folder where all necessary files for app execution and installer generation will be stored. Inside it, you should create two directories: “config” and “packages”.
As the name suggests, the first directory will contain configuration files for your installer – in this case a single XML file. What should this file contain?
The configuration file consists of some general information about the app: installation directory, name of the installer, etc. You can make some interesting things by modifying this file – for example, add a “run after installation” checkbox, modify installer UI or even add remote repositories for fetching app updates. You can learn about all the available tags in the Qt Installer documentation.
Let’s create such a file. The basic one should look like this:
<?xml version="1.0" encoding="UTF-8"?> <Installer> <Name>APP NAME</Name> <Version>1.0.0</Version> <Title>INSTALLER WINDOW TITLE</Title> <Publisher>Scythe Studio</Publisher> <StartMenuDir>START_MENU_DIR</StartMenuDir> <TargetDir>@HomeDir@/APP_DIR</TargetDir> </Installer>
Although it is minimalistic, for now, this would be enough.
Before you go any further, you should learn what the package actually is. The package is a module that contains a certain version or some parts of the app. If you include many packages, the installation can be customized – users can select what they want to install. Qt itself is a great example of such approach. Let’s take a look at the tree structure in the Qt installer:
As you see, the framework is divided into several packages containing kits for different platforms and modules with optionals functionality like a web engine.
According to the documentation, the packages directory should have following structure:
-packages - com.vendor.root - data - meta - com.vendor.root.component1 - data - meta - com.vendor.root.component1.subcomponent1 - data - meta - com.vendor.root.component2 - data - meta
With this knowledge, you can now start creating the first packages for our app. To make things clear and simple, for now, we’ll roll on with only two packages: a 64-bit app version and a 32-bit one.
Begin by creating proper directories for the packages. We will stick to the “com.developername.shortpackagename” naming pattern:
What about packages content? Let’s begin populating them!
Each package should contain two main directories inside: “data” and “meta”. First will contain executables, libraries, and other files necessary for running the app, while second hold information about the package. The “Meta” folder will contain only two files, in this case, so start with it.
Metadata should consist of “license.txt” and “package.xml” files. The text file is obvious – it provides the installer with license content. It doesn’t have any required format, but HTML tags are supported if you need some styling.
Now let’s talk about “package.xml” – this one is more interesting. This file contains information about the package, which will be presented in the installer window. By putting additional tags you can add custom installer pages, translations, dependencies between other packages and many more. We will talk about this file again, in another part of the tutorial, which will be published in the future. The “package.xml” content should look like this for now:
<?xml version="1.0" encoding="UTF-8"?> <Package> <DisplayName>PACKAGE NAME</DisplayName> <Description>This is going to install APP NAME on your machine</Description> <Version>1.0.0</Version> <ReleaseDate>2020-06-17</ReleaseDate> <Licenses> <License name="LICENSE TITLE" file="license.txt" /> </Licenses> <Default>true</Default> </Package>
Most of the tags are self-explanatory. If you want to learn more about other tags see documentation.
When you add necessary files to the “meta” directory, move one to the “data” folder. This directory contains all the app files that the package holds. When installing the package, its contents will be unpacked to the target directory. In order to keep the installation folder clear, we suggest creating an additional subfolder in the data directory.
As there is already a large number of folders we talked about, we’ll call this one “package files directory”. With that keeping track would be simpler.
Now the crucial part of deployment preparation begins. Inside the package files directory, you need to add a substantial amount of files needed to run an app outside the Qt Creator IDE: all the executables, Qt and external libraries, dll files, etc. Fortunately, the Qt Framework will help us achieve this by providing a set of tools for deployment.
Now add the executable file to the package files directory. Simply copy it from the build folder. Now open the command line terminal in the package files directory. It is needed to run “windeployqt.exe” – a tool created for automatic loading of all the Qt dll files needed for running the app. To run this tool use this command:
[QtFolder]\[QtVersionThatYouUse]\[SelectedKitDirectory]\bin\windeployqt.exe APPNAME.exe --qmldir [DirectoryContainingProjectQMLSourcecode] --compiler-runtime
This command adds (almost) all necessary binaries and files that you need to launch a QML based app. The -qmldir flag is used to scan your QML code and add all the needed runtime libraries from the Qt Quick module. After this flag put your project directory – don’t worry it will be scanned recursively. If your app is not QML based, you can skip this flag.
In the tutorial case the full command should look like this:
C:\Qt\5.15.0\mingw81_64\bin\windeployqt.exe QtInstallerTutorial.exe --qmldir C:\Projects\QtInstallerTutorial --compiler-runtime
When the tool finishes its work you can see that now the package files directory is getting crowded.
Currently, windeployqt tool tends to ship a lot of unnecessary files here, but even now there is no every file you need. Not yet…
If you try to run the application now you’ll get an error pop-up showing info about missing files. Depending on the configuration, compiler-specific and some other libraries must be redistributed along with your application. According to the documentation, the basic ones are:
VC++ 14.0 (2015)
VCCORLIB140.DLL, VCRUNTIME140D.DLL – The C runtime
MSVCP140.DLL – The C++ runtime
LIBWINPTHREAD-1.DLL
LIBGCC_S_DW2-1.DLL
LIBSTDC++-6.DLL
However, to make sure that you didn’t miss anything, following Qt docs advice, we suggest using the Dependency Walker tool. After running it you can easily see what dependencies are missing.
In this case, the LIBGCC_S_SEH-1.DLL and LIBSTDC++-6.DLL are missing. You can find them in the Qt folder, inside the kit directory:
Just copy them to your package files directory. Before finishing the deployment preparation, you should try running the .exe file, as a good practice. This way you will make sure that the app has all the dependencies in place. Currently, the app is still unable to run. We skipped copying LIBWINPTHREAD-1.DLL on purpose, to show that checking if everything is fine, before ending this step is essential. To end this step simply copy LIBWINPTHREAD-1.DLL from the kit directory and your package should be ready for distribution!
Note that the dll files we mentioned before may differ depending on the compiler you use. Also do not forget about applying steps 4 to 7 for all packages you created: in this case, we repeated everything for the 32-bit package.
When all of the packages are prepared for deployment you can finally generate an installer. To do it, the “binarycreator” tool from Qt Installer Framework will be needed.
First, open the terminal inside the deployment folder.
Now launch binarycreator.exe using the following command:
[Qt folder]\Tools\QtInstallerFramework\[QtInstallerVersion]\bin\binarycreator.exe -c config/config.xml -p packages -f NAMEOFTHEINSTALLER
The -c flag tells binarycreator where to look for the config file, the -p flag informs where the packages are located, while -f shows binarycreator that you are creating an offline installer. To learn more about available flags look at the documentation.
In the case of the tutorial full command looks like this:
C:\Qt\Tools\QtInstallerFramework\4.0\bin\binarycreator.exe -c config/config.xml -p packages -f QtInstallerTutorial
Don’t worry after hitting enter – your terminal is not frozen. Installer generator can take some time to finish its work. After a while a fresh installer should appear in your deployment directory:
The functional installer should look as follows:
Notice that the installer language was automatically changed to system language – Polish, in this case.
Congratulations – you have generated your first installer! Now feel free to test it out and make sure that everything was set up properly. Installer generated this way can be handled directly to the end-user. The only thing that users need to do is to launch it and proceed with the provided instructions.
It is also worth mentioning that after installing an app with your new installer the maintenance tool for your app is automatically provided. It allows users to add or delete packages, update app (if you set up repositories) or uninstall the whole app.
A good application for the installer generator is definitely using it with the CI/CD system on your repository. With that you can automate the whole process, so the client would always have the newest version of the installer. Every time you merge any changes to the master, proper commands would be executed, without the need of doing anything manually.
Another interesting fact is that with the newest version of the Qt Installer Framework 4.0 the new CLI has been added, allowing you to do some interesting things. The main functionality it provides is interacting with installers and maintenance tools: installing, updating, and modifying the app with command lines.
It can come in handy in cases like auto-updating – your app can simply run a maintenance tool when it gets information from the API that the new version is available or even check for updates directly from the maintenance tool. You can also call functions like uninstaller from the app. If you want to learn more about it, check out the CLI documentation page.
That is all for this entry. Now you know how to generate an offline installer for the Windows Qt app using Qt Installer Framework. Make sure to check out other entries on the Scythe-Studio blog and like our LinkedIn profile to make sure you won’t miss future Qt tutorials.
Let's face it? It is a challenge to get top Qt QML developers on board. Help yourself and start the collaboration with Scythe Studio - real experts in Qt C++ framework.
Discover our capabilitiesThe Qt Graphs is a powerful module of the Qt framework that enables developers to create interactive, both static and […]
Embedded systems are everywhere, from household appliances to industrial machinery. At the heart of these systems is embedded firmware, the […]
Outsourcing embedded systems development has become an increasingly popular strategy for businesses looking to enhance efficiency, reduce costs, and leverage […]