Skip to content
EndrII edited this page Sep 12, 2020 · 2 revisions

Release of C++/Qt and QML application deployment utility CQtDeployer v1.4.0 (Binary Box)

Almost half a year later, a major update of the CQtDeployer deployment utility was released. This update has many new futures, but the main emphasis is on creating packages.

CQtDeployer 1.4.0

Fixes

  • Fixed The help output in the console, now the actual size of the console is recounted before the output, which allows you to correctly transfer text.
  • Fixed work with the deployment of Qt plugins. Now plug-ins do not extract all system dependencies, but only qt. Extract system dependencies caused applications to crash due to incompatible plugin libraries.
  • Minor bug fixes and improvements.

New features

  • Added support for qmake search from the system environment.
  • Added the ability to initialize the repository for further packaging, similar to git init.
  • Added support for Qt Install Framework packages. Now you can pack the distribution into the installer.
  • Added the ability to split the final distribution into several packages.
  • Added the ability to unify the creation of packages for the final distribution.
  • Added support for adding custom scripts to application launch scripts.
  • Added support for extracting system dependencies for Windows.
  • Added support for RPATH for Linux. Now cqtdeployer can independently determine the necessary qmake to deploy the application.
  • Added support for finding the required dependency by library name.
  • Added support for Qt libraries from Linux distributions repositories.
  • Added new alias for the run command (cqt and cqtdeployer.cqt) for fast deploy of applications.
  • Added support the native name of command for windows. Now you can run a cqtdeployer from cqtdeployer commnad in cmd and powershell.

New options

  • init - will initialize cqtdeployer.json file (configuration file). For example: "cqtdeployer init" - to initialize the configuration of a base package. "cqtdeployer -init multi" - to initialize the configuration of several packages.
  • noCheckRPATH - disables the automatic search for paths to qmake in executable files (Linux only).
  • noCheckPATH - disables the automatic search for paths to qmake in the system environment.
  • extractPlugins - forces to extract all plug-in dependencies.
  • qif - creates an installer at the end of the deployment.
  • extraLibs - adds a template for an additional library, which should be included in the distribution.
  • customScript - adds a custom script to the startup script of the application.
  • -targetPackage [package; tar1, package; tar2] - used to form packages, denotes lists of target files for specific packages.
  • recOut - indicates in which folder the resources will be added after deployment.
  • name - sets the name of the package.
  • description - sets the package description
  • deployVersion - sets the package version
  • releaseDate - sets the release date of the package.
  • icon - sets the package icon.
  • publisher - sets the publisher of the package.
  • qifStyle - Sets the path to the CSS style file or sets the default style. Available styles: quasar
  • qifBanner - Sets path to the banner png file.
  • qifLogo - Sets path to the logo png file.

A detailed analysis of the most interesting changes.

The first thing you should pay attention to is that CQtDeployer has learned to work with RPATH (Linux only) and PATH. This means that if your application is built with RPATH support (and RPATH in qt is enabled by default) or your qmake is registered in PATH, then you do not need to specify the path to qmake. CQtDeployer will find qmake for itself.

Let's test it in practice. I created a simple console application using Qt.

#include <QString>
#include <QDebug>
int main(int, char *[])
{
    QString str = "hello CQtDeployer 1.4";
    qInfo() << str;
    return 0;
}

I will use the cmake build system, as it is more relevant than qmake.

andrei@HP:~/Hello$ tree 
.
├── CMakeLists.txt
├── CMakeLists.txt.user
└── main.cpp

0 directories, 3 files

Create a folder for the assembly.

andrei@HP:~/Hello$ mkdir build 

Run the cmake in the created folder.

andrei@HP:~/Hello/build$ cmake .. -DCMAKE_PREFIX_PATH=~/Qt/5.14.1/gcc_64
-- Configuring done
-- Generating done
-- Build files have been written to: /home/andrei/Hello/build

building a project

andrei@HP:~/Hello/build$ make 
Scanning dependencies of target Hello_autogen
[ 25%] Automatic MOC and UIC for target Hello
[ 25%] Built target Hello_autogen
Scanning dependencies of target Hello
[ 50%] Building CXX object CMakeFiles/Hello.dir/Hello_autogen/mocs_compilation.cpp.o
[ 75%] Building CXX object CMakeFiles/Hello.dir/main.cpp.o
[100%] Linking CXX executable Hello
[100%] Built target Hello

Checking our program.

andrei@HP:~/Hello/build$ ls
CMakeCache.txt  CMakeFiles  cmake_install.cmake  Hello  Hello_autogen  Makefile

And we start cqtdeployer passing it the program without qmake.

andrei@HP:~/Hello/build$ cqtdeployer -bin Hello 
Deploy ...
flag targetDir not  used. use default target dir : "/home/andrei/Hello/build/DistributionKit"
target deploy started!!
copy : "/home/andrei/Hello/build/Hello"
extract lib : "/home/andrei/Hello/build/DistributionKit//bin//Hello"
copy : "/home/andrei/Qt/5.14.1/gcc_64/lib/libQt5Core.so.5"
copy : "/home/andrei/Qt/5.14.1/gcc_64/lib/libicuuc.so.56"
copy : "/home/andrei/Qt/5.14.1/gcc_64/lib/libicui18n.so.56"
copy : "/home/andrei/Qt/5.14.1/gcc_64/lib/libicudata.so.56"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_ar.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_bg.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_ca.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_cs.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_da.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_de.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_en.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_es.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_fi.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_fr.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_gd.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_he.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_hu.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_it.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_ja.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_ko.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_lv.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_pl.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_ru.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_sk.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_uk.qm"
copy : "/home/andrei/Qt/5.14.1/gcc_64/translations/qtbase_zh_TW.qm"
try deploy msvc
deploy done!

Oh miracle, now our application is completely autonomous. Check it out.

andrei@HP:~/Hello/build$ cd DistributionKit/
andrei@HP:~/Hello/build/DistributionKit$ tree 
.
├── bin
│   ├── Hello
│   └── qt.conf
├── Hello.sh
├── lib
│   ├── libicudata.so.56
│   ├── libicui18n.so.56
│   ├── libicuuc.so.56
│   └── libQt5Core.so.5
└── translations
    ├── qtbase_ar.qm
    ├── qtbase_bg.qm
    ├── qtbase_ca.qm
    ├── qtbase_cs.qm
    ├── qtbase_da.qm
    ├── qtbase_de.qm
    ├── qtbase_en.qm
    ├── qtbase_es.qm
    ├── qtbase_fi.qm
    ├── qtbase_fr.qm
    ├── qtbase_gd.qm
    ├── qtbase_he.qm
    ├── qtbase_hu.qm
    ├── qtbase_it.qm
    ├── qtbase_ja.qm
    ├── qtbase_ko.qm
    ├── qtbase_lv.qm
    ├── qtbase_pl.qm
    ├── qtbase_ru.qm
    ├── qtbase_sk.qm
    ├── qtbase_uk.qm
    └── qtbase_zh_TW.qm

3 directories, 29 files
andrei@HP:~/Hello/build/DistributionKit$ 

The root of the program:

image

Libraries needed for the program to work:

image

As you can see from the example, the application is fully assembled.

Qt Installer Framework

The second innovation worth knowing is the ability to form QIF installers out of the box. All that is needed for our example is to add the qif option to the packaging command.

Usage example.

andrei@HP:~/Hello/build$ cqtdeployer -bin Hello qif 

Just one simple command and the program gets a presentable look.

image

This installer supports minimal integration of Linux distributions and Windows. Namely: creating shortcuts, and registering the application in the OS. If for some reason you are not satisfied with the appearance of this installer, you can change it using the qifStyle flag. At the time of version 1.4, cqtdeployer supports only 2 styles (native and quasar).

Example quasar style:

image

You can also use your own qss stylesheet. To do this, pass the path to your qss or css file instead of the style name. For example, consider the following qss stylesheet.

Style.qss:

QWidget
{
    color: white;
    background-color: rgb(65, 65, 65);
}

QPushButton
{
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(150, 150, 150, 60%), stop:1 rgba(50, 50, 50, 60%));
    border-color: rgb(60, 60, 60);
    border-style: solid;
    border-width: 2px;
    border-radius: 9px;
    min-height: 20px;
    max-height: 20px;
    min-width: 60px;
    max-width: 60px;
    padding-left: 15px;
    padding-right: 15px;
}

QPushButton:pressed, QPushButton:checked
{
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(50, 50, 50, 60%), stop:1 rgba(150, 150, 150, 60%));
}

Let's check what we get in this case.

cqtdeployer -bin Hello qif -qifStyle ./../style.qss

image

Here, in fact, is the dark theme of the installer.

Splitting into packages

And, probably, the last important update worth knowing is the ability to split a large multi-binar project into subprojects.

This feature is the most difficult of all listed, since it requires a lot of text to use it. So I recommend using the configuration file.

To begin with, we will complicate our project by adding 2 more executable files to it. I did not bother and just made 2 copies of my Hello utility.

To simplify working with packages, you need to initialize the directory.

cqtdeployer init

This is another new function that creates a CQtDeployer.json file, in which we will write our configurations, instead of passing options to the utility.

{
    "binDir": ".",
    "clear": true,
    "libDir": "./",
    "recursiveDepth": 5
}

Now let's make 2 packages of 3 of our programs. To do this, specify:

{
    "binDir": ".",
    "clear": true,
    "libDir": "./",
    "recursiveDepth": 5,
    "targetPackage": [
        ["Dstro1", "Hello1"],
        ["Dstro2", "Hello2"],
        ["Dstro2", "Hello3"]
    ]
}

Please note that I had to explicitly specify the binding for Dstro2 to Hello2 Hello3. Unfortunately, at the time of version 1.4 cqtdeployer was not able to parse target enumerations. Please note that if I write Hello1 Hello instead, then the selection will be made for all matches and all 3 programs will be selected. So, let's see what happened.

cqtdeployer

.
├── Dstro1
│   ├── bin
│   │   ├── Hello1
│   │   └── qt.conf
│   ├── Hello1.sh
│   ├── lib
│   │   ├── libicudata.so.56
│   │   ├── libicui18n.so.56
│   │   ├── libicuuc.so.56
│   │   └── libQt5Core.so.5
│   └── translations
│       ├── qtbase_ar.qm
│       ├── qtbase_bg.qm
│       ├── qtbase_ca.qm
│       ├── qtbase_cs.qm
│       ├── qtbase_da.qm
│       ├── qtbase_de.qm
│       ├── qtbase_en.qm
│       ├── qtbase_es.qm
│       ├── qtbase_fi.qm
│       ├── qtbase_fr.qm
│       ├── qtbase_gd.qm
│       ├── qtbase_he.qm
│       ├── qtbase_hu.qm
│       ├── qtbase_it.qm
│       ├── qtbase_ja.qm
│       ├── qtbase_ko.qm
│       ├── qtbase_lv.qm
│       ├── qtbase_pl.qm
│       ├── qtbase_ru.qm
│       ├── qtbase_sk.qm
│       ├── qtbase_uk.qm
│       └── qtbase_zh_TW.qm
└── Dstro2
    ├── bin
    │   ├── Hello2
    │   ├── Hello3
    │   └── qt.conf
    ├── Hello2.sh
    ├── Hello3.sh
    ├── lib
    │   ├── libicudata.so.56
    │   ├── libicui18n.so.56
    │   ├── libicuuc.so.56
    │   └── libQt5Core.so.5
    └── translations
        ├── qtbase_ar.qm
        ├── qtbase_bg.qm
        ├── qtbase_ca.qm
        ├── qtbase_cs.qm
        ├── qtbase_da.qm
        ├── qtbase_de.qm
        ├── qtbase_en.qm
        ├── qtbase_es.qm
        ├── qtbase_fi.qm
        ├── qtbase_fr.qm
        ├── qtbase_gd.qm
        ├── qtbase_he.qm
        ├── qtbase_hu.qm
        ├── qtbase_it.qm
        ├── qtbase_ja.qm
        ├── qtbase_ko.qm
        ├── qtbase_lv.qm
        ├── qtbase_pl.qm
        ├── qtbase_ru.qm
        ├── qtbase_sk.qm
        ├── qtbase_uk.qm
        └── qtbase_zh_TW.qm

8 directories, 60 files

As you can see from the result tree, we got 2 distributions.

  1. Dstro1 - contains the application Hello1
  2. Dstro2 - contains the remaining 2.

Now let's check what happens if all this is packaged by the installer. Add the qif option to true in CQtDeployer.json: ** qif: true, **.

{
    "binDir": ".",
    "clear": true,
    "qif": true,
    "libDir": "./",
    "recursiveDepth": 5,
    "targetPackage": [
        ["Dstro1", "Hello1"],
        ["Dstro2", "Hello2"],
        ["Dstro2", "Hello3"]
    ]

image

As can be seen from the screenshot, now we have 2 packages during installation.

New aliases

And the last small but nice addition: now new commands have been added to cqtdeployer.

  • сqt — is a quick way to deploy your application. It simplifies the deployment call.
    • Example: cqt myApp — this is the same as cqtdeployer -bin myApp.
  • cqtdeployer.cqt - same as cqt but for snap package.
  • В windows-версии теперь не нужно добовлять знак % для вызова утилиты.
  • In the windows version, now there is no need to add the% sign to call the utility. Now the call looks like in Linux. (cqtdeployer)