qmake: переменные путей и «теневые» сборки

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

PWD

Переменная PWD (она же IN_PWD) содержит полный путь к каталогу, содержащему текущий обрабатываемый .pro файл. Это может быть как собственно файл проекта, так и включенный .pri файл, файл фичи или даже файл кэша (.qmake.cache). В абсолютном большинстве случаев относительные пути в qmake отсчитываются именно от $$PWD, так что эту переменную можно использовать для преобразования относительных путей в абсолютные, когда это нужно.

Помимо PWD существует переменная _PRO_FILE_, которая содержит полный путь к текущему файлу.

Для некоторых применений полезно знать не только путь к текущему каталогу, но также путь к каталогу .pro файла, для которого запускался qmake. Этот путь содержится в переменной _PRO_FILE_PWD_. Пример ее применения есть в одном из предыдущих постов.

OUT_PWD и теневые сборки

qmake всегда создает файлы в каталоге, в котором он был запущен. Классическим вариантом является запуск qmake в каталоге, содержащим .pro файл, т.е. в $$PWD, но это не обязательно. Узнать каталог, в котором будут созданы файлы, можно из переменной OUT_PWD. Прошу заметить, что изменение этой переменной не изменит текущего каталога qmake. Единственный способ указать путь, в котором будут созданы файлы — это запустить в нем qmake.

Именно так и поступает Qt Creator для реализации «теневых сборок» (shadow builds). Другой термин для этой методы — out-of-source builds (сборка вне исходников), в противовес in-source builds. Рассмотренный выше запуск qmake в каталоге $$PWD — это in-source build, т.е. промежуточные файлы создаются в том же каталоге, где находятся исходники. Такой подход обладает только одним преимуществом — выглядит проще. Но за эту простоту приходится дорого платить. Во-первых, если проект имеет несколько конфигураций (а он обычно имеет по крайней мере две, debug и release; а еще могут быть разные киты (Kits), разные версии Qt, разные наборы макросов (defines) и т.д.), то необходимо предпринимать какие-то специальные меры, чтобы промежуточные файлы от разных версий не перемешались друг с другом и не возникло ошибок из-за этого. Во-вторых, с системами контроля версий гораздо удобнее работать, если между файлами, которые нужно хранить в системе (исходники) и файлами, которые в ней хранить обычно не нужно (промежуточные файлы) есть четкое различие. Так что использование out-of-source builds всячески рекомендуется.

Qt Creator хорошо поддерживает механизм «теневых сборок». Для каждой конфигурации создается отдельный каталог; его имя задается пользователем в настройках проекта либо генерируется автоматически на основании конфигурации проекта. Этот каталог создается рядом с каталогом исходников (т.е. $$PWD). При построении проекта в выбранной конфигурации Qt Creator запускает qmake в соответствующем каталоге.

Почему каталог для теневой сборки создается именно рядом? Потому что тогда относительные пути, ведущие к файлам вне каталога построения и вне каталога исходников, будут выглядеть одинаково. Если некая утилита тулчейна ошибочным образом отсчитывает пути от каталога исходников, а не от каталога построения (или если qmake по ошибке передаст утилите путь таким образом), то она все равно отработает без ошибки.

Раскладываем файлы по каталогам

По умолчанию все создаваемые qmake и make файлы складываются в $$OUT_PWD, но их можно разложить по каталогам с помощью нескольких переменных. Все эти переменные содержат пути, относительные $$OUT_PWD:

Путь к Makefile не настраивается, он всегда создается в OUT_PWD.

Поскольку OUT_PWD и PWD могут отличаться, при написании своих скриптов в qmake это нужно учитывать. Некоторую помощь может предоставить встроенная функция shadowed, которая получает как параметр путь в PWD и преобразует его в путь в OUT_PWD. Удобство функции заключается в том, что передаваемый путь может быть как относительным, так и абсолютным (возвращается всегда абсолютный).

debug_and_release

Для тех, кто нежно любит in-source build, в qmake есть фича debug_and_release. Она позволяет иметь две конфигурации сразу, debug и release, в одном каталоге исходников. Включается этот режим с помощью CONFIG += debug_and_release. Если добавить CONFIG += build_all, то строиться при запуске make будут сразу обе конфигурации.

Помимо того, что, in-source build есть идея сама по себе не очень удачная, этот режим обладает рядом больших минусов:

  • .pro файл обрабатывается три раза: при генерации главного Makefile и при генерации подчиненных Makefile.Debug и Makefile.Release. Соответственно, все действия выполняются по три раза. Например, message выведут свои сообщения трижды. Это утроение можно побороть следующим образом:
  • Нужно следить за тем, чтобы файлы от разных конфигураций не перемешались. Руками за этим следить есть рецепт к ошибкам. Пример из документации:
    Можно использовать режим CONFIG += debug_and_release_target, при котором файлы автоматически раскладываются по каталогам debug и release. Но этот режим не сочетается с рассмотренными выше переменными для указания путей в OUT_PWD для разных типов файлов.

Вопрос: зачем? Ради чего такой такой гемор? Ради in-source build? Нафик. Зачем я все это тогда рассказал? Помимо общего образования — чтобы вы понимали, что отключать. Дело в том, что испокон веков под Windows режим debug_and_release, равно как и debug_and_release_target, включен. Даже под Qt Creator. Выглядит это, мягко говоря, излишне. Зачем мне каталоги debug и release в моем теневом каталоге, ведь там может быть только одна конфигурация, debug или release? Зачем мне там три Makefile? Так что я эту ненужность отключаю:

Цикл: qmake

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