MyTetra Share
Делитесь знаниями!
Переменные qmake для настройки сборки
Время создания: 07.11.2019 08:46
Автор: Сергей Скобликов
Текстовые метки: c++, qt, *.pro, переменная, опция, настройка, сборка, проект, версионирование
Раздел: Компьютер - Программирование - Язык C++ (Си++) - Библиотека Qt - Инсталляция Qt и сборка проектов
Запись: xintrea/mytetra_syncro/master/base/1573105562za957l87ni/text.html на raw.github.com

DEFINES


Список макросов. Передается не только компилятору С/C++, но и компилятору ресурсов Windows (который обрабатывает .rc файл). Можно задать макросы со значениями:


DEFINES со значениями:



# просто определяет макрос

DEFINES += MYDEF

 

# макрос с значением.

# Обратите внимание: рядом с = пробелов нет.

DEFINES += WINVER=0x0500

 

# можно и так, для числовых констант результат одинаковый

DEFINES += WINVER='0x0500'

 

# макрос со строковой константой в качестве значения.

# Обратите внимание, раскрытие переменной - работает.

# А вот что, к сожалению, не работает, так это пробелы в строковом значении;

# как вариант, их можно заменить на \040 (с эскейпом бэкслеша).

# Обрамление значения с помощью ' - обязательно, иначе " уберутся.

DEFINES += SOME_FILE_PATH='\\"$${PWD}/path\\040with\\040spaces/filename.ext\\"'  

 

# так тоже можно, значение макроса есть просто кусок исходного кода.

# Пробелы недопустимы.

DEFINES += TEST=1.2.3.4  



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



INCLUDEPATH


Список каталогов для включаемых (заголовочных) файлов.


Каталог проекта (где расположен .pro файл) по умолчанию включается в INCLUDEPATH. Если вдруг нужно, то эту фичу можно отключить с помощью CONFIG += no_include_pwd.


В Qt 5 заголовочные файлы, найденные в INCLUDEPATH, создают зависимости для файлов, в которые они включаются, а в Qt 4 — нет. Читайте ниже сагу о DEPENDPATH.



DEPENDPATH


Документация: «Эта переменная содержит список всех директорий для поиска в них зависимостей. Она используется при прохождении по включенным (include) файлам». Хрен поймешь, что это такое и нафиг оно нужно. Как я выяснил, это еще и не совсем верная и изрядно устаревшая информация.


Задумка этой переменной в следующем: быть подмножеством INCLUDEPATH. Только включенные файлы из каталогов этого подмножества создают зависимости (т.е. прописываются как предпосылки к файлам, их включающим прямо или косвенно). Таким образом можно было избежать включения в список зависимостей стабильных файлов (заголовочные файлы стандартной библиотеки C++ и Qt, к примеру). Такая оптимизация значительно сокращает работу для make, т.к. гораздо меньше файлов нужно проверять на «свежесть».


Но практика показала, что от такой оптимизации больше вреда, чем пользы. Люди должны были руками за этой переменной следить, чтобы не напороться на трудноотслеживаемые глюки. Учитывая, что любую документацию никто не читает, а в доке по qmake к тому же ничего толком и не написано, то именно после такого столкновения с глюками люди об этой переменной и узнавали. Полнейшее безобразие. Так что, в конце концов, разработчики решили убрать этот механизм под ковер. В Qt 4 этого сделать было нельзя, не поломав старый код, поэтому ограничились введением способа сделать так, чтобы DEPENDPATH всегда был равен INCLUDEPATH. Делается это с помощью CONFIG += depend_includepath. В Qt 5 эта настройка включена по умолчанию. Мазохисты могут ее отключить (CONFIG -= depend_includepath), но я не советую. Жизнь слишком коротка, чтобы тратить ее на всякие глупости. Из глупостей: до 5 версии Qt DEPENDPATH по неведомым причинам включался в VPATH, что приводило к хитрым глюкам.


Резюме: если включена опция depend_includepath, то все включенные файлы будут создавать зависимости, причем рекурсивно. Таким образом, автоматически обеспечивается, что изменение любого заголовочного файла, включенного откуда угодно каким угодно путем, приведет к правильной перекомпиляции при вызове make. Аминь.



LIBS


Список статических библиотек, которые будут прилинкованы к результирующему файлу.


Включение библиотеки в этот список не сделает результирующий файл от нее зависимым, т.е. изменение библиотеки само по себе не приведет к перелинковке при вызове make. Для того, чтобы библиотека стала зависимостью, добавьте путь к ней в переменную PRE_TARGETDEPS или POST_TARGETDEPS. О них я еще расскажу подробнее в одном из следующих постов.


Библиотеки можно задать с помощью двух различных синтаксисов: как принято в Unix (флаги -L для путей и -l для имен библиотек) и как принято в Windows (просто путь к библиотеке без дополнительных извращений).



# Unix синтаксис

LIBS += -LD:/src/lib -lmylib


# Windows синтаксис, MSVC

LIBS += D:/src/lib/mylib.lib


# Windows синтаксис, MinGW

LIBS += D:/src/lib/libmylib.a

 

# Unix синтаксис

LIBS += -LD:/src/lib -lmylib

 

# Windows синтаксис, MSVC

LIBS += D:/src/lib/mylib.lib

 

# Windows синтаксис, MinGW

LIBS += D:/src/lib/libmylib.a



Большое неудобство синтаксиса Windows заключается в том, что нужно знать расширение в имени файла библиотеки, а оно зависит от тулчейна (в MSVC это .lib, в MinGW .a; в MinGW еще и префикс lib добавляется к имени библиотеки). Но, тем не менее, под Windows надежнее использовать виндовый синтаксис, поскольку юниксовый преобразуется в виндовый для виндовых линкеров, и это преобразование происходит не без нюансов:


  • На момент вызова qmake библиотека должна существовать, иначе qmake не сможет определить, в каком каталоге из указанных она находится, а значит, не сможет выполнить преобразование.

  • В рамках вышеуказанного примера: если в указанных в LIBS каталогах будут найдены библиотеки, скажем, mylib.lib и mylib2.lib, то вместо mylib в проект будет прилинкована mylib2! Число в конце имени библиотеки интерпретируется как ее версия, и выбирается библиотека с максимальной версией… вот нахрена такое счастье на Windows-то? Проблему можно обойти, добавив расширение явно, но тогда теряется вся идея кроссплатформенности синтаксиса.


Еще предостережение: в случае использования синтаксиcа Windows, т.е. указания пути к файлу библиотеки, лучше используйте полные пути, не относительные. Это связано с тем, что относительный путь будет относительным относительно каталога, в котором запущен линкер, а не каталога .pro файла, и теоретически он может быть совсем другим. Для преобразования относительных путей в абсолютные можно использовать переменную PWD, о которой я еще расскажу.


В результате всего перечисленного, добавить статическую библиотеку в проект на qmake есть задача не самая тривиальная, если не сказать резче. В одном из следующих постов я расскажу, как лично я выхожу из положения.



TEMPLATE


Значение переменной задает тип проекта:


  • app: исполняемый файл. Значение по умолчанию.
  • lib: статическая или динамическая библиотека.
  • subdirs: специальный вариант .pro файла для разбиения проекта на несколько отдельных частей (обычно в подкаталогах), каждая со своим отдельным .pro файлом. Подробности — в одном из дальнейших постов.


В случае библиотеки ее тип (статическая или динамическая) задается CONFIG (CONFIG += staticlib или CONFIG += dll). Также есть специфический конфиг для плагинов (CONFIG += plugin), который влючает в себя dll.


Для приложений под Windows можно указать, какое оно будет: GUI (CONFIG += windows, по умолчанию) или консольное (CONFIG += console). Эти два флага работают как переключатели, поэтому если нужно проверять на тип приложения, то нужно использовать функцию CONFIG:


Проверка на console:



CONFIG(console, windows|console) {

 # консольное

}



TARGET


Имя проекта. По умолчанию равно имени .pro файла (без расширения .pro). Имя результирующего файла получается на основании значения переменных TARGET, TEMPLATE, VERSION, некоторых других переменных, а также платформы и тулчейна, и может обрасти разными префиксами и суффиксами. Поскольку генерация имени происходит в фичах, надежного способа узнать это имя в .pro файле нет. Хотя в Makefile можно; подробности позже.



CONFIG


Некоторые настройки компилятора задаются в CONFIG, кроссплатформенным и высокоуровневым образом.



# режим debug или release выбирается через CONFIG

# и хотя нужное значение обычно передается через

# командную строку, ничто не мешает указать его и в .pro файле

CONFIG += debug

 

# В Qt 5 поддержка C++ 11 включается без лишних телодвижений

CONFIG += c++11

 

# можно включить использование компиляторами

# наборов инструкций mmx, sse, sse2 (это то, что

# задокументировано, есть еще подобные опции в фичах/mkspec)

CONFIG += mmx

CONFIG += sse

CONFIG += sse2

 

# Для редких извращений может понадобиться отключить

# поддержку исключений или RTTI. Для это есть по паре переключателей,

# по семантике аналогичных паре debug|release

CONFIG += exception_off  

CONFIG += exception      

CONFIG += rtti_off  

CONFIG += rtti

# к этим же извращениям относится включение/выключение

# опций компилятора, необходимых для поддержки STL

CONFIG += stl_off

CONFIG += stl

 

# Еще можно варнинги компилятора приглушить.

# Уж не знаю, чем они мешают.

CONFIG += warn_off

CONFIG += warn_on



QT


Переменная для указания модулей Qt, которые подключаются к проекту. Список всех доступных модулей можно посмотреть в подкаталоге modules каталога mkspecs. Каждый файл в нем описывает свой модуль, и в нем без труда обнаруживается имя модуля (например, QtQmlDevTools) и имя, которое нужно добавить в переменную Qt (qmldevtools) для его подключения в проект.


По умолчанию добавляются два модуля, core и gui. Если пишется консольное Qt приложение, то нужно выключить GUI. А если выключить все модули, то проект не будет использовать Qt вообще. Так что с помощью Qt Creator + qmake можно разрабатывать C/C++ программы, Qt не использующие.


Выключение модулей:



# консольное приложение

QT -= gui

 

# Приложение без Qt

QT =



В Qt 5 из модуля gui виджеты были вынесены в отдельный модуль widgets. Теперь, если интерфейс реализован сугубо на QtQuick, то тащить за собой виджеты не нужно. Но, как следствие, если программа использует виджеты, то теперь их надо явно включать. Для совместимости с Qt 4 можно сделать это следующим образом:


Подключение модуля widgets:



greaterThan(QT_MAJOR_VERSION, 4): QT += widgets



Кстати, о переменных версии Qt. Они существуют как раз для такого рода задач, так что полезно о них знать.


Подключение модуля widgets:



message($$QT_VERSION) # 5.0.2

message($$QT_MAJOR_VERSION) # 5

message($$QT_MINOR_VERSION) # 0

message($$QT_PATCH_VERSION) # 2



VERSION


Версия результирующего файла.



Информация о версии в Explorer


Задумка была хорошая, но реализация капитально подкачала. Настолько, что я не могу рекомендовать этой переменной пользоваться. Проблема в том, что эта версия лезет куда только можно, и совершенно ненастраиваемым образом. И при этом — глюкавит.


Самая приятная фича VERSION заключается в том, что под Windows указание версии приводит к генерации .rc файла с информацией о версии. Причем можно указать дополнительную информацию, не только номер.


Информация о версии в ресурсе Windows:



VERSION = 1.20.30.40

QMAKE_TARGET_COMPANY = Sergey Skoblikov

QMAKE_TARGET_PRODUCT = qmake demo

QMAKE_TARGET_DESCRIPTION = Long thoughtful description

QMAKE_TARGET_COPYRIGHT = (c) Sergey Skoblikov



Результат на картинке. В чем подвох? В том, что все это не работает, если указано значение переменной RC_FILE, а создание своего .rc (и указание его в этой переменной) было единственным способом указать иконку для приложения Windows. В версии Qt 5.0.2 добавили переменную RC_ICONS, которая содержит список иконок, добавляемых в этот же генеримый файл. Так что до совсем недавнего времени эта фича представляла собой сугубо академический интерес. В последней версии вроде как можно пользоваться…. но VERSION не дает.


Не столь приятная фича — старший номер версии стремится прибавиться к имени результирующего файла. Если вы билдите dll и определили, например, VERSION = 2.1.3, то результирующий файл будет не mylib.dll, а mylib2.dll. Может, кому-то это и полезно. Но явно не мне. Можно полечить с помощью TARGET_EXT = .dll, но чем лечить — лучше не болеть, имхо.


Совсем неприятная фича, которую я не знаю как лечить, проявляет себя под MSVC. Если определена переменная VERSION, то для линкера будет сгенерирован параметр /VERSION. Проблема в том, что формат для параметра имеет вид MAJOR.MINOR, где MINOR < 65K. qmake в припадке мудрости MINOR получает конкатенацией всего, что идет после первой точки в значении VERSION. Если, например, прописать VERSION = 1.71.238, то поимеем следующую радость:



LNK1147: invalid number specified with option '/VERSION:1.71238'



За что мне это?


После этого известия я поставил крест на переменной VERSION.



RC_INCLUDEPATH


В тему о ресурсах. Под Windows компилятор виндовых ресурсов запускается для файла .rc, если он есть у проекта (указан в переменной RC_FILE или сгенерирован автоматически). При этом компилятору передаются макросы проекта (DEFINES) и пути из RC_INCLUDEPATH. Эти пути служат для нахождения включенных файлов, а также для файлов, включаемых в ресурс.



Низкоуровневые переменные


Если припечет нужда, то можно полезть руками ковырять настройки конкретных утилит тулчейна. Желающие могут покопаться с переменными с именами, начинающимися на QMAKE_*.


Наибольший интерес представляют собой переменные с опциями для компиляторов C и C++ и линкера. Например, QMAKE_CXXFLAGS — это базовые опции компилятора C++, а QMAKE_CXXFLAGS_DEBUG — это опции, которые будут добавлены к ним, если выбран debug; аналогично есть переменная QMAKE_CXXFLAGS_RELEASE и переменные для других выбров, например QMAKE_CXXFLAGS_WARN_OFF и QMAKE_CXXFLAGS_WARN_ON. Для линкера существует устроенный по аналогичному принципу набор переменных QMAKE_LFLAGS*.


Игры с этими переменными позволяют реализовать вещи, которые другими путями в qmake сделать нельзя.


Пример. Мухлеж с опциями утилит тулчейна:



# создавать отладочную информацию для релиза, MSVC

win32-msvc* {

  QMAKE_CXXFLAGS_RELEASE += /Zi /Od

  QMAKE_LFLAGS_RELEASE += /DEBUG

}

 

# настройка Delayed Loading для своих dll, MSVC

win32-msvc* {

  QMAKE_LFLAGS_RELEASE += /DELAYLOAD:MyDll.dll

}


Так же в этом разделе:
 
MyTetra Share v.0.65
Яндекс индекс цитирования