В один прекрасный вечер, понадобилось мне написать небольшое приложение, требования к которому, на первый взгляд, выглядели не такими уж и сложными:
- работа с кое-какими железяками;
- наличие GUI;
- умение работать в Windows XP и выше (не спрашивайте, зачем);
- один исполняемый файл (для Windows);
- крайне желательна версия под macOS;
- проверка наличия обновлений на удалённом сервере по HTTPS.
С учётом того, что всю предыдущую сознательную жизнь с С++ я сталкивался случайно и мимоходом, решение данной задачи (а если быть точнее, то настройка окружения для этого) — оказалось неплохим таким квестом, в конце которого ждал тортик решил поделиться с Вами наработанным опытом, вдруг кому пригодится.
Люблю эту картинку.
Disclaimer: статья от чайника для чайников. За задетые чувства профессионалов C++ и магистров жизненного цикла продуктов я не отвечаю.
Вообще, с начала разработки приложения, особо не задумывался над тем, как именно я буду его собирать. Ну хотя бы потому, что с самого начала в требованиях не было ни Windows XP, ни наличия только одного исполняемого файла в дистрибутиве. Совсем недолго поразмышляв, решил писать на С++ с использованием Qt в качестве библиотеки для GUI. Была у меня ещё мыслишка писать на чистом C с использованием libui (как-то он мне больше импонирует), но поставленные временные рамки такой роскоши не позволяли, да и трудное отрочество на PHP не даёт так быстро и просто отказаться от использования классов. А если ко всему вышеизложенному добавить то, что последние несколько лет я заядлый хакинтошер, то начал и долгое время продолжал разрабатывать всё это дело в Sublime Text из-под macOS, совершенно не думая о Windows: "Ну это же C++, это же полная кроссплатформенность," — думал я. Ошибался. Не всё так просто оказалось.
Итак, долго ли коротко, продукт начал приобретать рабочие очертания и пришло время демонстрировать его заказчику. Устанавливаю, значит, виртуалку с Windows 10, туда — Visual Studio 2017 и последний Qt Creator, быстренько компилирую проект, докидываю ему необходимых Qt-библиотечек, отправляю заказчику и довольный потираю ручки от радости завершённого этапа разработки. Но через пять минут получаю: "не запускается."
И маленький скриншотик из Windows XP, на котором маленький месседжбокс радостно возвещает о начале больших проблем:
После небольшой перепалки в попытках объяснить нецелесообразность поддержки XP, и получения указания о её обязательной поддержке, начинаю выяснять, как заставить вот это вот всё там работать. Примерный список пройденных граблей такой:
- нашёл в установщике Visual Studio галочку "Windows XP support for Visual C++", установил, ничего не поменялось;
- погуглил про поддержку операционных систем со стороны Qt, выяснил, что максимальная версия, поддерживающая Windows XP — 5.6;
- там же выясняется, что 5.6 из коробки работает только с MSVC 2013 и 2015.
Сроки горели, заказчик лютовал, пришлось на всё плюнуть и после некоторых мытарств с разными версиями, остановиться на среде из Windows XP + VS2010 + Qt 5.0. Немало прослезился от неподдерживаемости многих современных штук из C++, но делать было нечего, некогда пилу точить — переписал эти ваши удобные битсеты на самодельные костыли над вектором, enum classes на обычные enum, разрулил пару сотен выползших warning'ов, понаблюдал очередной рассвет, скомпилировал, сдал, — да так и провёл большую часть оставшейся разработки, разрабатывая в macOS и периодически внося необходимые изменения для работы с компилятором MSVC 2010.
Близился конец разработки, вроде бы заказчику уже более-менее всё нравится, но вот приходит небольшое сообщение: "слушай, сделай один экзешник, чото как-то много дллок." Далее снова следует небольшая перепалка, предложение сделать инсталлятор, который намажет все нужные файлы во все нужные места, отрицание предложений и указания о необходимости одного абсолютно портабельного исполнимого файла.
Ну, надо так надо. Убедив, что на сборку необходимого мне понадобится какое-то время, начал разбираться с данным вопросом. Ну и дабы не нагружать тебя, читатель, моими злоключениями и отвратительной манерой письма и далее, лишь опишу тебе то, что получилось в текущем виде. Впрочем, и тут есть куда совершенствоваться.
Итак, дано:
- Виртуалка Windows 7;
- Устанавливаем Visual Studio 2015 (Update 2 и выше), не забываем про Windows XP support при установке;
- Устанавливаем исходники Qt 5.6.3;
- Устанавливаем Active Perl (в интернетах по схожим вопросам писали, что и Strawberry срабатывал), Python, Ruby, jom. Не забываем добавить всё это добро в %PATH%;
- Качаем и распаковываем исходники OpenSSL (1.0.2o на момент написания), допустим, в C:\OpenSSL-src.
1. Компилируем OpenSSL.
Открываем cmd.exe и меняем директорию на корень распакованных исходников с OpenSSL:
cd C:\OpenSSL-src
Инициализируем нужное окружение (в моих примерах jom и Python не добавлены в глобальный %PATH%):
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
set PATH=C:\jom;C:\Python27;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin;%PATH%
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include;%INCLUDE%
set LIB=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib;%LIB%
set CL=/D_USING_V110_SDK71_
Конфигурируем будущую сборку:
perl Configure VC-WIN32 no-asm no-async --prefix=C:\openssl --openssldir=C:\openssl
OpenSSL рекомендует использовать одинаковые значения --prefix и --openssldir, их значения по умолчанию у разных версий разные, поэтому лучше указать их явно. При компиляции OpenSSL может использовать ассемблерные алгоритмы (поддерживается только NASM), и я пару дюжин раз пытался компилировать с ними, но у меня так ничего не вышло, потому используется ключ no-asm. Также, параметр no-async рекомендован для WinXP (а также CentOS 5, BSD 5, Vista и прочих старых операционных систем) при компиляции OpenSSL версии 1.1.0 и выше, но у меня он тоже присутствует, хотя, может быть, он тут и не нужен на самом деле.
Генерируем мейкфайл:
ms\do_nt
И вот теперь, чтобы результат собрался с поддержкой Windows XP, необходимо внести некоторые изменения в ms\nt.mak (статическая версия) или ms\nt-dll.mak (динамическая версия). Открываем его в любимом текстовом редакторе и добавляем в начало файла инициализацию окружения:
PATH=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin;$(PATH)
INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include;$(INCLUDE)
LIB=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib;$(LIB)
Далее, добавляем к CFLAG:
-D_USING_V110_SDK71_ -I"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include" -DPSAPI_VERSION=1
Не уверен насчёт необходимости -DPSAPI_VERSION=1, но лишним не будет.
Изменяем/меняем/добавляем ключ /subsystem в переменных LFLAGS, MKLIB и MLFLAGS на следующий:
/subsystem:console,5.01
Компилируем и устанавливаем результат в конечную папку:
nmake -f ms\nt.mak
nmake -f ms\nt.mak install
Чтобы убедиться, что собранные библиотеки запустятся на WinXP, надо открыть их (в случае динамической сборки с .dll) в любом hex-реадкторе и посмотреть в районе байтов 130..150 на наличие байтов 05 00 01:
2. Компилируем Qt.
Запускаем новую консоль, снова инициализируем окружение:
cd C:\Qt\Qt5.6.3\5.6.3\Src
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
set PATH=C:\jom;C:\Python27;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin;%PATH%
set INCLUDE=C:\openssl\include;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include;%INCLUDE%
set LIB=C:\openssl\lib;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib;%LIB%
set OPENSSL_LIBS="-LC:/openssl/lib -llibeay32 -lssleay32 -lgdi32"
set CL=/D_USING_V110_SDK71_
Обратите внимание на наличие и правильность путей к библиотекам и хэдерам OpenSSL.
Конфигурирем Qt:
configure -prefix C:\Qt\Qt5.6.3\5.6.3\msvc2015-static -static -static-runtime -target xp -platform win32-msvc2015 -debug-and-release -qmake -opensource -nomake examples -no-compile-examples -nomake tests -qt-pcre -no-icu -no-sql-sqlite -no-nis -no-cups -no-iconv -no-dbus -qt-zlib -qt-libpng -opengl desktop -no-directwrite -I "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include" -L "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib" -l Gdi32 -I C:\openssl\include -L C:\openssl\lib -openssl -openssl-linked OPENSSL_LIBS="-lssleay32 -llibeay32 -lgdi32"
Важные ключи для статической сборки: -static, -static-runtime и -openssl-linked.
А для нормальной работы с Windows XP: -target xp, -opengl desktop и -no-directwrite.
Опять же, не уверен насчёт нужности и важности -l Gdi32 в данном списке, ибо компилировал всё это дело не менее двадцати раз, но дела оно не испортит.
Компилируем:
jom
jom install
Можно и nmake пробовать, но jom чуточку быстрее, да и с nmake у меня так ни разу ничего и не вышло.
3. Добавляем поддержку Windows XP в проект Qt
Редактируем .pro:
DEFINES += _ATL_XP_TARGETING
DEFINES += PSAPI_VERSION=1
QMAKE_LFLAGS_WINDOWS = /SUBSYSTEM:WINDOWS,5.01
На этом, собственно, всё. Кажется конечно, что ничего особенного, но смею Вас заверить, что проходить этот путь с нуля и без подготовки — очень даже весело.
Комментариев нет:
Отправить комментарий