Статическая сборка 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, то смело пролистывайте этот мануал до самого интересного.

Итак, приступим!

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

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 

ссылка на оригинал статьи http://habrahabr.ru/post/261479/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *