Qt
June 2015 30

Статическая сборка dll-библиотеки с модулями QtQuick

From Sandbox
Доброго времени суток, читатели. В этой статье я постараюсь максимально доступно рассказать о том, как собирал статически dll-библиотеку с модулями QtQuick. Облазив все интернеты, мне так и не удалось найти решения своей проблемы, а оно, как оказалось, было под носом.

Начну с самого начала, а именно: с отучения Qt от библиотек *.dll. Оперируемый: Qt-windows-x86-MinGW-5.5.0-rc, снапшот 95 от 2015.06.17. Рабочее окружение: Windows 8.1

Знаю, что подобных статей уже набралась приличная кучка, но что поделать… Мне пришлось четыре раза пересобрать разные версии Qt, прежде чем собранные им dll-ки стали загружаться как надо. Сразу предупрежу, что в этой сборке будет вырезан QtWebKit. Если у вас уже имеется статическая сборка Qt, то смело пролистывайте этот мануал до самого интересного.

Итак, приступим!
  1. Скачиваем необходимый набор софта:
    • собственно, сам 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 вовсе не понадобятся, но т.к. во всех мануалах они оба фигурируют как «обязательные» и нам совершенно не нужны сюрпризы, установим их.

  2. Устанавливаем/распаковываем в любую директорию. У меня это были 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 — распакованный исходник


  3. Правим конфигурацию qmake:
    • открываем файл qt-everywhere-opensource-src-5.5.0-rc/qtbase/mkspecs/win32-g++/qmake.conf
    • находим строку
      QMAKE_LFLAGS =
      

      и заменяем её на:
      QMAKE_LFLAGS = -static -static-libgcc
      
      (у 5.5.0-rc это была 70 строка)

  4. Загадочный баг 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
    



  5. Запускаем терминал 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% и собираем всё, что там есть.

  6. Запускаем конфигуратор:
    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, чтобы узнать какой параметр нужно добавить к конфигуратору.


  7. Запускаем сборку:
    mingw32-make -k
    

    p.s. если у вас многоядерный процессор, то следует указать сборщику их количество параметром -j[количество_ядер — 1]. Т.е. для своего 4-ядерного процессора я запустил сборщик так:
    mingw32-make -k -j3
    

    параметр -k вроде приказывает сборщику остановится, если вдруг «что-то пошло не так». Меня эта малоприятная ситуация не коснулась, поэтому утверждать не буду.

  8. Запускаем установку:
    mingw32-make install
    

    после этого у нас должна появится директория %QTPREFIX% с собранными библиотеками.

  9. Добавляем комплект для сборки статики:
    1. запускаем QtCreator, который был установлен вместе с Qt5.5.0-rc
    2. переходим во вкладку Инструменты > Параметры… > Сборка и запуск > Qt Versions, добавляем новый qmake из директории %QTPREFIX%\5.5.0\bin, обзываем его как-нибудь так: "Qt %{Qt:Version} (%{Qt:Version}_Static)" и жмём кнопку "Применить"
      Qt Versions


    3. переходим во вкладку "Комплекты", жмём "Добавить", обзываем как-нибудь так: "Qt5.5.0-Static", компилятор: MinGW, профиль: "Qt %{Qt:Version} (%{Qt:Version}_Static)", жмём кнопку "ОК"
      Комплекты
      image



Вот, собственно, и всё. А теперь, наконец, самое интересное.


Статическая сборка 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 и видим два файлика:
    1. %project_name%_plugin_import.cpp
    2. %project_name%_qml_plugin_import.cpp

    в них-то и содержится список всех подгружаемых для обычного Qml-приложения плагинов, а именно:
    #include <QtPlugin>
    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-библиотеки. Что вы будете с ней делать я не знаю, но надеюсь кому-то эта информация пригодится :)

.pro-файл моего проекта 'MyDll'
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.5.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



mydll.h
#ifndef MYDLL_H
#define MYDLL_H

#include <QtPlugin>
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 <QGuiApplication>
#include <QQuickView>

class MyDll
{
...
};

#endif


+16
11.9k 84
Comments 4
Top of the day