Andrei Yankovich
Andrei YankovichMarch 24, 2020, 9:50 a.m.

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 innovations, 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.

Download links

Installer:

The installer can be downloaded on the social page github

Snap

Get it from the Snap Store

Attention

In the snap version, be sure to give the program access to read other processes. Since this is necessary to create an installer and a correct qt search.

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)

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

o
  • March 24, 2020, 2:51 p.m.

спасибо, за обзор!

Andrei Yankovich
  • March 24, 2020, 3:29 p.m.

Если будут вопросы, пишите. Найдем решение)

Ruslan Polupan
  • March 26, 2020, 7:47 a.m.

Супер. Спасибо!

D
  • March 26, 2020, 8:50 a.m.

Добрый день. У меня есть приложение с библиотеками собранными отдельно. Как мне сделать так, чтобы эти библиотеки помещались в установщик и при установке приложение были вместе с исполняемым файлом? И почему утилита при использовании -bin или -bindir создает ярылки для каких-то библиотек, но не для исполняемого файла? Я использую Windows.

Andrei Yankovich
  • March 26, 2020, 9:27 a.m.
  • (edited)
  1. Откройте cmd, перейдите в папку с вашими проектами, запустите cqtdeployer init.
  2. Должен появится файл CQtDeployer.json, откройте его.
{
    "bin": [
        "./path/to/my/file.exe",
    ],
    "qmake": "C:/path/to/my/qmake.exe",
    "clear": true,
    "libDir": "./",
    "recursiveDepth": 5,
    "qif": true
}

Если все ваши библиотеки требуются для работы ./path/to/my/file.exe т они автоматически попадут в инсталятор. если ваши библиотеки очень далеко запрятаны от расположения json файла то можете по больше выставить значение "recursiveDepth": 5,
путь "./path/to/my/file.exe", должен обязательно начинается с "." и быть относительным от json файла.

Обратите внимание на то что в Windows версии все еще нужно явно указывать путь к qmake которым был собран проект.
Исключение составляет тот факт что если qmake уже прописан у вас в окружении PATH, тогда cqtdeployer будет использовать значение из PATH.
затем сохраняете ваш файл и повторно запускаете cqtdeployer.

что касается ярлыков на библиотеки то звучит как ошибка. Собирите максимум информации о ваших дествиях и опишите ошибку на GitHub

Andrei Yankovich
  • March 26, 2020, 10:29 a.m.

можно еще так:

cqtdeployer -bin "path/to/my/file.exe" -qmake "C:/path/to/my/qmake.exe" -libDir "./" -recursiveDepth 5 qif

это тоже самое, просто гораздо удобнее когда инициализация будет у вас записана в файл, и вам не придется постоянно водить эту длинную строку для развертывания.

D
  • March 26, 2020, 11:56 a.m.

LibDir вообще не видит библиотеки. Пробовал разные попытки записи, исходя из вашего сообщения. Библиотеки лежат рядом с исполняемым файлом, json там же. Если использовать binDir вместо bin тогда библиотеки добавляются. А при установке создадутся ярлыки на все библиотеки, но не на исполняемый файл. Оно так и должно работать?

Andrei Yankovich
  • March 26, 2020, 12:39 p.m.
  • (edited)

нет, так не должно быть, вы уверены что ваш исполняемый файл зависит от ваших библиотек ? или он подключает их как плагины?
а с ярлыками это точно ошибка, уже завел задачу на это .

Andrei Yankovich
  • March 26, 2020, 12:42 p.m.

@Den125 можно взгялнуть на ваш проект ?

D
  • March 26, 2020, 12:51 p.m.

Мой исполняемый файл не зависит от библиотек. Библиотеки подключаются при необходимости в процессе работы программы.

Andrei Yankovich
  • March 26, 2020, 1:13 p.m.
  • (edited)

тогда это плагины )
для них есть отделный флаг extraPlugin

{
    "bin": [
        "./path/to/my/file.exe",
    ],
    "qmake": "C:/path/to/my/qmake.exe",
    "extraPlugin": [
        "./plugin1.dll",
        "./plugin2.dll",
    ],
    "clear": true,
    "libDir": "./",
    "recursiveDepth": 5,
    "qif": true
}
D
  • March 26, 2020, 1:34 p.m.

Это сработало, но библиотеки при установке находятся в папке plugins. А можно их вынести к исполняемому файлу? Можно ли в установщик закинуть файл не библиотеку и не исполняемый, например *.jar?

Andrei Yankovich
  • March 26, 2020, 1:37 p.m.

тогда переместите ваши библиотеки из extraPlugin в bin

{
    "bin": [
        "./path/to/my/file.exe",
        "./plugin1.dll",
        "./plugin2.dll",
    ],
    "qmake": "C:/path/to/my/qmake.exe",
    "clear": true,
    "libDir": "./",
    "recursiveDepth": 5,
    "qif": true
}
Andrei Yankovich
  • March 26, 2020, 1:37 p.m.

но появятся ярлыки, сейчас поправим

Andrei Yankovich
  • March 27, 2020, 2:47 a.m.

Ошибка с ярлыками в Windows исправлена в версиии 1.4.0.4

Comments

Only authorized users can post comments.
Please, Log in or Sign up
m

C++ - Тест 003. Условия и циклы

  • Result:85points,
  • Rating points6
в

C++ - Тест 003. Условия и циклы

  • Result:50points,
  • Rating points-4
l

C++ - Test 005. Structures and Classes

  • Result:91points,
  • Rating points8
Last comments
k
kmssrFeb. 9, 2024, 2:43 a.m.
Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVADec. 25, 2023, 6:30 p.m.
Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJoDec. 25, 2023, 4:38 p.m.
Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
GvozdikDec. 19, 2023, 5:01 a.m.
Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Now discuss on the forum
AC
Alexandru CodreanuJan. 19, 2024, 7:57 p.m.
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…
BlinCT
BlinCTDec. 27, 2023, 4:57 p.m.
Растягивать Image на парент по высоте Ну и само собою дял включения scrollbar надо чтобы был Flickable. Так что выходит как то так Flickable{ id: root anchors.fill: parent clip: true property url linkFile p…
Дмитрий
ДмитрийJan. 10, 2024, 12:18 p.m.
Qt Creator загружает всю оперативную память Проблема решена. Удалось разобраться с помощью утилиты strace. Запустил ее: strace ./qtcreator Начал выводиться весь лог работы креатора. В один момент он начал считывать фай…
Evgenii Legotckoi
Evgenii LegotckoiDec. 12, 2023, 2:48 p.m.
Побуквенное сравнение двух строк Добрый день. Там случайно не высылается этот сигнал textChanged ещё и при форматировани текста? Если решиать в лоб, то можно просто отключать сигнал/слотовое соединение внутри слота и …

Follow us in social networks