[Из песочницы] Статическая сборка dll-библиотеки с модулями QtQuick
Доброго времени суток, читатели. В этой статье я постараюсь максимально доступно рассказать о том, как собирал статически dll-библиотеку с модулями QtQuick. Облазив все интернеты, мне так и не удалось найти решения своей проблемы, а оно, как оказалось, было под носом.
Начну с самого начала, а именно: с отучения Qt от библиотек *.dll. Оперируемый: Qt-windows-x86-MinGW-5.5.0-rc, снапшот 95 от 2015.06.17. Рабочее окружение: Windows 8.1
Знаю, что подобных статей уже набралась приличная кучка, но что поделать… Мне пришлось четыре раза пересобрать разные версии Qt, прежде чем собранные им dll-ки стали загружаться как надо. Сразу предупрежу, что в этой сборке будет вырезан QtWebKit. Если у вас уже имеется статическая сборка Qt, то смело пролистывайте этот мануал до самого интересного.
Итак, приступим!
- Скачиваем необходимый набор софта:
- собственно, сам Qt 5.5.0-rc (не могу утверждать, что эта ссылка выживет после официального релиза версии 5.5.0)
- Qt-src-5.5.0-rc
- Perl (версия на момент сборки: 5.22.0.1)
- Python (3.4.3)
- Ruby (2.2.2)
Возможно, что Python и Ruby вовсе не понадобятся, но т.к. во всех мануалах они оба фигурируют как «обязательные» и нам совершенно не нужны сюрпризы, установим их. - Устанавливаем/распаковываем в любую директорию. У меня это были C:/Qt для Qt и его исходников, и С:/dev для остального софта. При установке Qt не забудьте отметить чекбокс Qt → Tools → MinGW.Конфигурация установки
Source Сomponents устанавливать не нужно, их мы скачали отдельно в архиве (уже, наверное, и распаковали?), с ними и будем работать.Дерево- C:/dev/StrawberryPerl
- C:/dev/Ruby22
- C:/dev/Python34
- C:/Qt/Qt5.5.0 — установленная версия Qt
- C:/Qt/qt-everywhere-opensource-src-5.5.0-rc — распакованный исходник
- Правим конфигурацию qmake:
-
Загадочный баг c qmldebugger
lib\libQt5Qml.a(qqmldebugserver.o):qqmldebugserver.cpp: undefined reference to `QTcpServerConnection::QTcpServerConnection()'
ИсправляемИз-за этого бага, собственно, мне и приходилось собирать две из четырёх версий Qt, пока не нашел в интернете описание самой проблемы. К сожалению, я так и не понял какое место должен был пропатчить этот патч (буду рад, если подскажете), поэтому поступил крайне радикально и некрасиво: вероломно вырезал проблемную строку из исходника. Я думаю практиковать такие деяния не стоит, но один раз можно =)
P.s.: я не совсем уверен, что именно это в итоге спасло моё время и нервы, потому что одновременно с этим изменением, я добавил параметр -qml-debug к сборщику. Если вы хотите рискнуть и пожертвовать своим временем, то не меняйте исходник, а сконфигурируйте сборку с параметром -qml-debug, авось проблема станет неактуальной.Заходим в директорию qt-everywhere-opensource-src-5.5.0-rc\qtdeclarative\src\qml\debugger, открываем злосчастный файл qqmldebugserver.cpp и комментируем неугодную строку:
#if defined(QT_STATIC) && ! defined(QT_NO_QML_DEBUGGER) // #include "../../plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.h" <---- комментируем это #endif
- Запускаем терминал MinGWПуск → Все программы → Qt 5.5.0 → 5.5 → MinGW
и скармливаем ему примерно такой код:# НЕ ЗАБУДЬТЕ ПОМЕНЯТЬ ПУТИ НА СВОИ! set QTSRC=C:\Qt\qt-everywhere-opensource-src-5.5.0-rc set MINGWPATH=C:\Qt\Qt5.5.0\Tools\mingw492_32\bin set QTPREFIX=C:\Qt\5.5.0_Static set PYPATH=C:\dev\Python34 set PERLPATH=C:\dev\Strawberry\perl\bin set RUBYPATH=C:\dev\Ruby22\bin
где:
QTSRC — распакованные исходники Qt
MINGWPATH — путь к MinGW, установленного вместе с Qt
QTPREFIX — место, куда будет будет установлена статическая сборка Qt
PYPATH — путь к установленному Питону
PERLPATH — путь к папке bin Перла
RUBYPATH — путь к папке bin Rubyдалее:
set QTDIR=%QTSRC%\qtbase set PATH=%PATH%;%MINGWPATH%;%PYPATH%;%PERLPATH%;%RUBYPATH%;%QTDIR%/bin cd %QTSRC% mkdir nomake move qtwebkit nomake move qtwebkit-examples nomake
здесь устанавливаются пути поиска бинарников, а последние три строки вырезают WebKit из сборки (флаг -no-webkit куда-то исчез из конфигурационного файла). Хочу отметить, что для подключения плагинов без бубна будет недостаточно собрать один лишь qtbase, поэтому мы переходим в папку с исходником %QTSRC% и собираем всё, что там есть. - Запускаем конфигуратор:
configure -static -release -opensource -confirm-license -opengl desktop -no-angle -qml-debug -c++11 -platform win32-g++ -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -qt-freetype -no-openssl -make libs -make tools -nomake examples -nomake tests -prefix %QTPREFIX%
шотакое?- -static — без комментариев;
- -release — указываем, что собирать будем только Release версию (если вам нужен и debug, то меняем на -debug);
- -opensource -confirm-license — соглашаемся с лицензией;
- -opengl desktop сборка для «настольников»;
- -no-angle — «позволит избавится от libEGL, libGLES», спасибо Taraflex: Toster;
- -qml-debug — разрешаем дебаг кумлы. Тот самый параметр, который возможно вылечивает бажиг с qmldebugger (пункт 4);
- -c++11 — … — разрешаем использовать;
- -platform win32-g++ — целевая платформа (да, только 32 бита);
- -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -qt-freetype — плюшки;
- -no-openssl — отключаем ssl (мне он был не нужен);
- -make libs — это нам пригодится для сборки dll-библиотеки;
- -make tools — утилиты сомнительной необходимости, которые я все-же решил оставить. Вроде это assistant, designer и linguist. Если вам это не нужно, замените на -nomake tools;
- -nomake examples -nomake tests — примеры и тесты. Собираются о-о-о-о-чень долго, так что лучше не включать;
- -prefix %QTPREFIX% — указываем, куда будет установлена будущая сборка. (%QTPREFIX% был задан в самом начале пятого пункта);
- если вам нужны еще какие-то модули, например, sqlite, то читайте README или воспользуйтесь командой configure -help, чтобы узнать какой параметр нужно добавить к конфигуратору.
- Запускаем сборку:
mingw32-make -k
p.s. если у вас многоядерный процессор, то следует указать сборщику их количество параметром -j[количество_ядер — 1]. Т.е. для своего 4-ядерного процессора я запустил сборщик так:mingw32-make -k -j3
параметр -k вроде приказывает сборщику остановится, если вдруг «что-то пошло не так». Меня эта малоприятная ситуация не коснулась, поэтому утверждать не буду. - Запускаем установку:
mingw32-make install
после этого у нас должна появится директория %QTPREFIX% с собранными библиотеками. - Добавляем комплект для сборки статики:
- запускаем QtCreator, который был установлен вместе с Qt5.5.0-rc
- переходим во вкладку Инструменты > Параметры… > Сборка и запуск > Qt Versions, добавляем новый qmake из директории %QTPREFIX%\5.5.0\bin, обзываем его как-нибудь так:»Qt %{Qt: Version} (%{Qt: Version}_Static)» и жмём кнопку »Применить»Qt Versions
- переходим во вкладку »Комплекты», жмём »Добавить», обзываем как-нибудь так:»Qt5.5.0-Static», компилятор: MinGW, профиль:»Qt %{Qt: Version} (%{Qt: Version}_Static)», жмём кнопку »ОК»Комплекты
Вот, собственно, и всё. А теперь, наконец, самое интересное.
Статическая сборка dll-библиотеки с модулями QtQuick
Создаём новый проект в QtCreator’e:
- Файл > Создать проект… > Библиотека > Библиотека C++ > Динамическая библиотека
- выбираем созданный нами комплект сборки (Qt5.5.0-Static)
- открываем файл %project-name%_global.h
Вся соль того, что dll-библиотеки не собирались статически (точнее собирались, но были неработоспособны) заключалась в том, что шаблон (TEMPLATE), применяемый для сборки DLL (lib) не включает в себя линковку плагинов. Поэтому все их нужно прописывать вручную в заголовочнике и .pro-файле.
Получить список всех необходимых плагинов не составило труда:
- создаём новый проект »Приложение Qt Quick»
- компилируем его тем же комплектом Qt5.5.0-Static (не забываем указать тип сборки Release)
- переходим в директорию с собранным проектом: build-%project_name%-Qt5.5.0_Static-Release и видим два файлика:
- %project_name%_plugin_import.cpp
- %project_name%_qml_plugin_import.cpp
в них-то и содержится список всех подгружаемых для обычного Qml-приложения плагинов, а именно:#include
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) Q_IMPORT_PLUGIN(QDDSPlugin) Q_IMPORT_PLUGIN(QICNSPlugin) Q_IMPORT_PLUGIN(QICOPlugin) Q_IMPORT_PLUGIN(QJp2Plugin) Q_IMPORT_PLUGIN(QMngPlugin) Q_IMPORT_PLUGIN(QTgaPlugin) Q_IMPORT_PLUGIN(QTiffPlugin) Q_IMPORT_PLUGIN(QWbmpPlugin) Q_IMPORT_PLUGIN(QWebpPlugin) Q_IMPORT_PLUGIN(QtQuick2Plugin) Q_IMPORT_PLUGIN(QTcpServerConnection) Q_IMPORT_PLUGIN(QGenericEnginePlugin) Q_IMPORT_PLUGIN(QNativeWifiEnginePlugin) Q_IMPORT_PLUGIN(QtQuick2WindowPlugin)
всю эту беду нужно добавить заголовочный файл библиотеки (%project-name%_global.h
в этой-же директории открываем файл Makefile.Release и достаем оттуда нужную информацию, а именно: список библиотек, которые должны быть подключены к проекту:
LIBS = -lmingw32 -LC:/Qt/5.5.0_Static/lib -lqtmain -LC:/Qt/5.5.0_Static/qml/QtQuick.2 -lqtquick2plugin -LC:/Qt/5.5.0_Static/qml/QtQuick/Window.2 -lwindowplugin -LC:/Qt/5.5.0_Static/plugins/platforms -lqwindows -lwinspool -lshlwapi -lQt5PlatformSupport -lqtfreetype -LC:/Qt/5.5.0_Static/plugins/imageformats -lqdds -lqicns -lqico -lqjp2 -lqmng -lqtga -lqtiff -lqwbmp -lqwebp -LC:/Qt/5.5.0_Static/plugins/qmltooling -lqmldbg_qtquick2 -lQt5Quick -lQt5Gui -lcomdlg32 -loleaut32 -limm32 -lwinmm -lglu32 -lopengl32 -lgdi32 -lqtharfbuzzng -lqmldbg_tcp -lQt5Qml -LC:/Qt/5.5.0_Static/plugins/bearer -lqgenericbearer -lqnativewifibearer -lQt5Network -ldnsapi -lQt5Core -lole32 -luuid -lws2_32 -ladvapi32 -lshell32 -luser32 -lkernel32 -lmpr -lqtpcre
всё это нужно будет добавить в .pro-файл вашего будущего проекта (список может отличаться, в зависимости от вашей версии и от того, какие модули вы включали/отключали при сборке Qt, так что лучше скопируйте этот список со своего ПК). Не помешает еще добавить путь к заголовочным файлам статической сборки Qt:
INCLUDEPATH += C:/Qt/Qt5.5.0_Static/include
После того, как вы внесёте эти изменения в проект, вы сможете собрать «mini-framework» QtQuick в виде одной DLL-библиотеки. Что вы будете с ней делать я не знаю, но надеюсь кому-то эта информация пригодится :)
QT += core widgets qml quick
CONFIG += dll
TEMPLATE = lib
TARGET = mydll
DEFINES += MYDLL_LIBRARY
SOURCES += mydll.cpp
HEADERS += mydll.h \
objectfactory.h
INCLUDEPATH += C:/Qt/Qt5.3.0_Static/include
LIBS += -lmingw32\
-LC:/Qt/5.5.0_Static/lib -lqtmain\
-LC:/Qt/5.5.0_Static/qml/QtQuick.2 -lqtquick2plugin\
-LC:/Qt/5.5.0_Static/qml/QtQuick/Window.2 -lwindowplugin\
-LC:/Qt/5.5.0_Static/plugins/platforms -lqwindows -lwinspool -lshlwapi -lQt5PlatformSupport -lqtfreetype\
-LC:/Qt/5.5.0_Static/plugins/imageformats -lqdds -lqicns -lqico -lqjp2 -lqmng -lqtga -lqtiff -lqwbmp -lqwebp\
-LC:/Qt/5.5.0_Static/plugins/qmltooling -lqmldbg_qtquick2 -lQt5Quick -lQt5Gui -lcomdlg32 -loleaut32 -limm32 -lwinmm -lglu32 -lopengl32 -lgdi32 -lqtharfbuzzng -lqmldbg_tcp -lQt5Qml\
-LC:/Qt/5.5.0_Static/plugins/bearer -lqgenericbearer -lqnativewifibearer -lQt5Network -ldnsapi -lQt5Core -lole32 -luuid -lws2_32 -ladvapi32 -lshell32 -luser32 -lkernel32 -lmpr -lqtpcre
#ifndef MYDLL_H
#define MYDLL_H
#include
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
Q_IMPORT_PLUGIN(QDDSPlugin)
Q_IMPORT_PLUGIN(QICNSPlugin)
Q_IMPORT_PLUGIN(QICOPlugin)
Q_IMPORT_PLUGIN(QJp2Plugin)
Q_IMPORT_PLUGIN(QMngPlugin)
Q_IMPORT_PLUGIN(QTgaPlugin)
Q_IMPORT_PLUGIN(QTiffPlugin)
Q_IMPORT_PLUGIN(QWbmpPlugin)
Q_IMPORT_PLUGIN(QWebpPlugin)
Q_IMPORT_PLUGIN(QtQuick2Plugin)
Q_IMPORT_PLUGIN(QTcpServerConnection)
Q_IMPORT_PLUGIN(QGenericEnginePlugin)
Q_IMPORT_PLUGIN(QNativeWifiEnginePlugin)
Q_IMPORT_PLUGIN(QtQuick2WindowPlugin)
#include
#include
class MyDll
{
...
};
#endif