qmake: как обязать make всегда вызывать qmake и зачем это нужно

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

Проблемы с генерацией исходных файлов в Makefile возникают следующие:

  • Генерируемые файлы могут не существовать на момент запуска qmake. Поэтому их добавление в SOURCES или HEADERS приведет к выводу предупреждения. Кроме того, следующий наивный способ добавить генерируемые файлы в переменную обречен на провал:
    Эту проблему можно попытаться решить созданием пустых файлов с нужными именами или запуском генерилки файлов из qmake, но…
  • qmake не имеет доступа к исходным файлам, соответственно, не может их обработать. Что вы будете делать, если вам нужно, чтобы сгенерированные вами файлы обработал moc? А что, если вам нужно, чтобы qmake распарсил зависимости в ваших сгенерированных файлах? Даже хуже получается, если файлы существуют как результат предыдущего запуска генерилки. Предположим, по сгенерированному в прошлый заход файлу fucked_up.h moc создал свой moc_fucked_up.cpp, а потом уже в make fucked_up.h файл поменялся? Может получиться рассогласование, и ищи теперь ошибки, программист. И хорошо еще, если эта ошибка вылезет на этапе компиляции…

Чем бороться со всеми этими граблями, лучше убрать их первоисточник. Если qmake будет иметь доступ ко всем исходным файлам во время обработки .pro файла, то никаких проблем из вышеперечисленных не возникнет. Итого, если вам нужно генерировать исходники, делайте это в qmake. Можно или написать свою программу/скрипт, вызывать ее в самом начале .pro файла и добавлять получившиеся файлы в нужные переменные, или сгенерировать правила в Makefile для генерации файлов, подвязав их нужным образом к правилам, создаваемым qmake (для этого правильнее всего воспользоваться механизмом QMAKE_EXTRA_COMPILERS, который будет рассмотрен в следующих постах).

Так или иначе, при таком подходе для любого построения qmake должен быть вызван перед запуском make, чтобы обновить Makefile и/или генерируемые файлы. И это — проблема, поскольку для построения вызывается make, а не qmake (Qt Creator тоже так поступает), и make в общем случае не обязан вызывать qmake. Обязать make к этому есть имхо самый простой и надежный способ решить проблему.

Простой способ

В Makefile уже есть правило для запуска qmake — для самого Makefile. Можно просто сделать это правило выполняющимся всегда:

make специальным образом обрабатывает ситуацию, когда правила в нем меняют сам Makefile, автоматически перечитывая его и выполняя заново уже обновленный Makefile. По идее, приведенное выше правило должно было привести к бесконечной рекурсии (постоянный перезапуск make из-за постоянно меняющегося Makefile) — но не приводит. Буду рад, если кто-нибудь объяснить мне, почему.

Способ посложнее

Предыдущий способ нехорош тем, что qmake может выполняться несколько раз за время одного построения. Нехорошо. И время занимает, и глюки могут теоретически возникнуть из-за того, например, что наши генерилки запускаются несколько раз кряду. Хорошо бы сделать так, чтобы make запускал qmake только в том случае, если он еще не был запущен.

Идея следующая: при запуске qmake создавать маркерный файл, а при запуске make запускать qmake только в том случае, если этого маркерного файла не существует. Также, этот маркерный файл будет всегда удаляться в конце работы make. Тем самым обеспечивается, что запуск qmake будет пропущен только один раз, при первом запуске make — который, предположительно, произойдет сразу после запуска qmake. Этот алгоритм может выстрелить в ногу только в том случае, если будет запущен qmake, но make сразу после него запущен не будет. Учитывая, что Qt Creator всегда запускает make после qmake — мне подходит. Жаль только, что при завершении make по ошибке маркерный файл не удалится.

Нет в жизни счастья

Добавлять несколько секунд к каждому билду на запуск qmake — оно все же накладно. Я не уверен, что хуже: не доверять корректности билда или тратить это время. Лично мне больше жалко времени, поэтому я все же редко когда использую приведенный выше скрипт для debug конфигурации; если я знаю, что что-то сильно поменялось, я делаю полный ребилд вручную. Но для release я делаю вызов qmake обязательным. Иногда паранойя полезна для здоровья.

ОБНОВЛЕНИЕ

К сожалению, моя практика показала, что описанные выше выкрутасы, равно как и другие опробованные мною уловки, не всегда работают, или имеют неприятные побочные последствия. Особенно это касается MSVC. Так что в конце концов я решил лечить больную голову посредством ее отсечения: я удаляю Makefile после построения проекта. Радикально, зато этот способ просто реализуется и на 98.5% надежен.

Цикл: qmake

  4 comments for “qmake: как обязать make всегда вызывать qmake и зачем это нужно

  1. Сергей Никонов
    08.04.2014 at 11:07

    Вышеприведённый «простой способ» таки приводит к бесконечной рекурсии (Qt 5.2.1 на Linux / gcc 4.8.1 и Windows MinGW 4.8.0). Как там ведёт себя MSVC — не знаю, не пользуюсь.

    • mgsxx
      08.04.2014 at 11:16

      Я ее как-то лечил. А потом забил и стал тупо удалять makefile, чтобы гарантировать запуск qmake.

      Вообще, явно пора уже на qbs уходить, там этого маразма нет по определению.

      • dismine
        04.12.2014 at 23:34

        А как же быть с make install? Или я что то не понимаю?

        • mgsxx
          04.12.2014 at 23:42

          В пролете make install. Ну или обязательный вызов qmake в пролете. Или qmake в пролете, если не вся Qt вместе с C++ заодно.

          Честно говоря, утомило меня бороться с инструментом вместо того чтобы собственно программировать. Ушел на другие хлеба до поры до времени.

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