Как заставить qmake всегда пересобирать проект «с чистого листа» при изменении макросов

    Если вдруг кто не в курсе, то спешу сообщить, что qmake этого не делает. Этот печальный факт может привести к крайне неприятным багам сборки, если забыть сделать полный ребилд после изменения макросов проекта.

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

     # функция, в которой происходит вся работа
    defineReplace(checkDefinesForChanges) {
      old_def = $$cat($$OUT_PWD/defines.txt)
      curr_def = $$DEFINES
      curr_def -= $$old_def
      old_def -= $$DEFINES
      diff = $$old_def $$curr_def
      # если макросы поменялись, то удаляем все файлы в OUT_PWD
      !isEmpty(diff) {
        A = $$system(del /F /Q /S $$system_path($${OUT_PWD}/*.*))
        message(DEFINES WERE CHANGED)
      }
      write_file($$OUT_PWD/defines.txt, DEFINES);
      return(???)
    }
     
    # нагибаем QMAKE_EXTRA_COMPILERS, чтобы запустить
    # checkDefinesForChanges после всех фич
    _defines_check_ = ???
    defines_check.name = check on defines being changed
    defines_check.input = _defines_check_
    defines_check.CONFIG += no_link ignore_no_exist
    defines_check.depends = ???
    defines_check.commands = ???
    defines_check.output_function = checkDefinesForChanges
    QMAKE_EXTRA_COMPILERS += defines_check
     
    # цели в Makefile, чтобы при удалении defines.txt запускался qmake
    recompile_on_defines_txt_not_existsing.target = $(MAKEFILE)
    recompile_on_defines_txt_not_existsing.depends = $$OUT_PWD/defines.txt
    recompile_on_defines_txt_not_existsing2.target = $$OUT_PWD/defines.txt
    recompile_on_defines_txt_not_existsing2.depends = qmake
    QMAKE_EXTRA_TARGETS += recompile_on_defines_txt_not_existsing recompile_on_defines_txt_not_existsing2
    

    Суть происходящего: я нагибаю QMAKE_EXTRA_COMPILERS, чтобы вызвать свою функцию после обработки всех фич. Тем самым я могу получить финальное значение переменной DEFINES, которое использую для того, чтобы определить факт смены макросов. Если изменение было — удаляю все файлы в OUT_PWD (команда для Windows, под Linux поменяйте на что нужно).

    В качестве полезного иногда бонуса, в файле defines.txt всегда можно посмотреть макросы, с которыми компилировался проект.
    Метки:
    Поделиться публикацией
    Похожие публикации
    Комментарии 14
    • –3
      При всей полезности поста вторая акцентированная ссылка на личный блог (первая была в Вашей первой статье) начинает попахивать самопиаром.
      • –1
        Хм… а куда мне еще ссылку ставить? На предыдущий пост? На официальную доку, в которой ничего толком нет? На Гугл?
        • 0
          Во-первых, можно и надо было развернуть почему так происходит. И не говорите, что слишком много писать, по опыту своему и чужому могу смело утверждать, что если не можешь изложить вкратце, значит недостаточно четко все еще лежит в голове. Ну и во вторых сделать отдельный раздел Ссылки
          • –1
            Давайте не будем о том, что у кого как лежит в голове. А то вам, может, неприятно будет слушать.

            Вы по ссылке заходили? Видели объем? Даже если нужный материал скомпилировать, получится многократно превышающий объем поста оффтопик. Человек или знает, что такое QMAKE_EXTRA_COMPILERS с QMAKE_EXTRA_TARGETS, или нет. Если нет, то краткие выжимки не помогут. Также напоминаю вам, что перепост запрещен правилами Хабра. Вы ведь, вроде, знакомы с правилами? Поэтому — ссылка. Не ссылки, ибо других на тему qmake банально не очень есть.

            На этом предлагаю флуд закончить. Если есть что по делу — прошу.
            • 0
              Ну хорошо, коль сами не понимаете, то будем задавать вопросы:
              В QtCreator послее изменения pro-файла qmake перезпускается. Т.е. первая часть в таком случае всегда выполняется, зачем вторая?
              • 0
                Первая часть, я так понял, это recompile_on_defines_txt_not_existsing, вторая — recompile_on_defines_txt_not_existsing2.

                Суть такова: recompile_on_defines_txt_not_existsing не есть полноценный rule, он не содержит команды и не выполняется. Это просто задание зависимости Makefile от defines.txt. Можно добавить в этот rule команду по запуску qmake напрямую, но зачем этот гембель, если эта команда уже есть готовая для таргета qmake? Так что я просто подвязываюсь к готовому правилу.

                Получается такая последовательность: после запуска make проверит rule для Makefile, тот проверит defines.txt, и если он не существует (или менялся), то начнет проверять зависимости defines.txt — т.е. таргет qmake, что вызовет qmake, который создаст defines.txt и при следующем запуске make правило для Makefile на defines.txt уже не споткнется — поскольку при запуске qmake defines.txt гарантированно создастся раньше, чем Makefile.
                • 0
                  Нет, я про этот код:

                  old_def = $$cat($$OUT_PWD/defines.txt)
                  curr_def = $$DEFINES
                  curr_def -= $$old_def
                  old_def -= $$DEFINES
                  diff = $$old_def $$curr_def
                  # если макросы поменялись, то удаляем все файлы в OUT_PWD
                  !isEmpty(diff) {
                  A = $$system(del /F /Q /S $$system_path($${OUT_PWD}/*.*))
                  message(DEFINES WERE CHANGED)
                  }
                  write_file($$OUT_PWD/defines.txt, DEFINES);


                  Допустим мы его выдернули ипоставили в конец pro-файла, теперь, если мы что-то поменяли в файле проекта, то он будет автоматически выполняться.
                  • 0
                    Будет. Только вот переменная DEFINES еще не будет содержать нужного значения — фичи (features) отрабатывают после конца файла. Проверьте сами — вставьте message(DEFINES=$$DEFINES) в конец файла, а потом посмотрите переменную DEFINES в Makefile.

                    Вся суть описанного мною извращения состоит в том, что я ловлю финальное значение DEFINES, после того как все фичи сделают свое дело.

                    Ну и кроме того, просто кусок кода не решает той проблемы, что если defines.txt будет удален, то qmake не перезапустится.
                    • 0
                      Раз пункт, который нужно написать в статье.

                      Да, вначале qmakte парсит и вполняет файл-проекта, а потом выполняет код фич. Но стоит заметить, что если фичи меняют что-то из DEFINE-ов, то что-то не так в консерватории. Ибо можем нарваться на неожиданное поведение.

                      QMAKE_EXTRA_COMPILERS в каки-то года не гарантировал, что скрипт запустится раньше компилятора, т.е. возможна ситуация (по крайне мере была), что вы два раза отправите проект на полную пересборку. И вот тут вопрос, а как сейчас? Возможен ли такой сценарий?
                      • 0
                        1. Обратите внимание, об этом написано. Если вы читали.
                        2. Фичи очень даже могут менять макросы, и это очень в духе этой конкретной консерватории. Стандартные фичи этим тоже занимаются.
                        3. Вы любите пользоваться понятными только вам терминами. По крайней мере, мне не понятно, что такое «скрипт» и что такое «компилятор» в вашем вопросе. Так что затрудняюсь ответить.

                        • 0
                          1) Да, пропустил, т.к. не ожидал формулировку задачи опосля ее решения…
                          2) Стандартные фичи это те, что прописали в начале проекта или масштабного рефакторинга и забыли.
                          3) Компилятор это и на Тау-Кита компилятор, в нашем случае это то, что сидит в QMAKE_CXX, а скрипт это в нашем случае… defineReplace.
                          4) А на кой нам повторно парсить проект?
                          • 0
                            1) Это не формулировка, по задумке.
                            2) Стандартные фичи — это те, которые с Qt поставляются. При чем тут проекты?
                            3) То, что сидит в QMAKE_CXX не имеет никакого отношение ко всему вышеизложенному. И выполняется он (компилятор) не в qmake, а в make — уж заведомо после любых скриптов qmake.
                            4) Приведенный в посте скрипт не делает лишних билдов.
                          • 0
                            Ладно, уже засыпаю и не могу правильно поставить вопрос. Меня Смущает вот
                            этот кусок:
                            recompile_on_defines_txt_not_existsing.target = $(MAKEFILE)
                            recompile_on_defines_txt_not_existsing.depends = $$OUT_PWD/defines.txt
                            recompile_on_defines_txt_not_existsing2.target = $$OUT_PWD/defines.txt
                            recompile_on_defines_txt_not_existsing2.depends = qmake

                            Есть ощущение, что вы тут намудрили
                            • 0
                              Если вы дадите себе труд прочитать ответ, которы я написал выше на ваш вопрос о «первой и второй части», то там все расписано по шагам.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.