CMake add_custom_command выполняется каждый раз

#cmake

#cmake

Вопрос:

Я использую пользовательскую команду для генерации лексеров и синтаксических анализаторов C из грамматик ANTLR4. Прямо сейчас у меня есть следующее:

 set(MY_PARSER_INC
        ${PROJECT_SOURCE_DIR}/Headers/MyParser/MyLexer.h
        ${PROJECT_SOURCE_DIR}/Headers/MyParser/MyParser.h
        ${PROJECT_SOURCE_DIR}/Headers/MyParser/MyParserBaseVisitor.h
        ${PROJECT_SOURCE_DIR}/Headers/MyParser/MyParserVisitor.h
)
set(MY_PARSER_SRC
        ${PROJECT_SOURCE_DIR}/Sources/MyParser/MyLexer.cpp
        ${PROJECT_SOURCE_DIR}/Sources/MyParser/MyParser.cpp
)
add_custom_command(
        OUTPUT ${MY_PARSER_INC} ${MY_PARSER_SRC}
        DEPENDS ${PROJECT_SOURCE_DIR}/Grammars/MyGrammar.g4
        COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_SOURCE_DIR}/Headers/MyParser/
        COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_SOURCE_DIR}/Sources/MyParser/
        COMMAND java -cp "${ANTLR_CLASSPATH}" "org.antlr.v4.Tool" -Dlanguage=Cpp -visitor -no-listener -package MY::NESTED::NAMESPACE -encoding iso-8859-1 -o ${PROJECT_SOURCE_DIR}/Sources/MyParser/ ${PROJECT_SOURCE_DIR}/Grammars/MyGrammar.g4
        COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/Sources/MyParser/*.h ${PROJECT_SOURCE_DIR}/Headers/MyParser/
        COMMAND ${CMAKE_COMMAND} -E remove -f ${PROJECT_SOURCE_DIR}/Sources/MyParser/*.h
        COMMENT "generating sources for My Parser"
)
  

и затем я использую выходные файлы в своей add_library команде для поддержания зависимости.

Это делает именно то, что я ожидаю от него. Он правильно создает лексеры и синтаксические анализаторы. Он также правильно поддерживает зависимость между этими источниками и целевой библиотекой. Только одна проблема: он запускается каждый раз! Даже если файл грамматики не был изменен (я проверил дату файла в грамматике и сгенерировал лексеры / синтаксические анализаторы, чтобы быть уверенным)! Я видел несколько похожих вопросов в Интернете, но до сих пор не могу понять, почему это происходит.

Есть какие-нибудь подсказки ?!

ПРАВКА1:

Добавляю больше информации, поскольку она все еще может быть неясной. После у add_custom_command меня есть следующее:

 include_directories(${PROJECT_SOURCE_DIR}/Headers/MyParser/)

add_library(MyLibrary SHARED
        ${MY_PARSER_INC} ${MY_PARSER_SRC}
        other_files.hpp other_files.cpp)
  

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

Комментарии:

1. Признаки того, что некоторые ВЫХОДНЫЕ файлы не будут восстановлены пользовательской командой. На самом деле, *.h не работает в cmake -E copy command, см. Документацию . Поскольку вы знаете все файлы, созданные antlr, вы можете просто перечислить их для этой команды.

2. @Tsyvarev Я не совсем понимаю первое утверждение. Однако команда копирования работает нормально. Файлы есть, они обновляются правильно, если я обновляю грамматику, и я включаю их в другие файлы, я бы обязательно заметил, если копия не удалась.

3.Вы можете попытаться самостоятельно устранить причину перестройки файлов. Например, если вы используете make утилиту для сборки проекта, затем передайте ей -d option.

4. Вы создали пользовательскую цель?

5. Как указывалось, cmake -E copy не поддерживает подстановочные знаки и cmake -E remove устарел. Я предполагаю, что ваш скрипт завершается некорректно и не обновляет файл временных меток, который некоторые генераторы используют при отслеживании пользовательских команд. Вероятно, это зависимость, которая вышла из строя и вызывает повторные запуски, которые вы видите. Но без подробной информации об используемом генераторе и инструментах я не могу сказать наверняка.

Ответ №1:

Вот мое предположение.

Вам нужно создать пользовательскую цель.

https://cmake.org/cmake/help/latest/command/add_custom_target.html ?выделите =custom_target

 add_custom_target(custom_target_create_parser_code DEPENDS
  ${MY_PARSER_INC} ${MY_PARSER_SRC}
)
  

Это создаст цель, которая зависит от пользовательской команды, которую вы написали.

Теперь cmake есть к чему присоединить вашу команду.

Теперь вам нужно добавить зависимость в вашу статическую библиотеку, о которой вы упомянули. https://cmake.org/cmake/help/latest/command/add_dependencies.html

 add_dependency(your_static_library custom_target_create_parser_code)
  

Теперь cmake не должен каждый раз повторно запускать ваш пользовательский код команды.

РЕДАКТИРОВАТЬ КСТАТИ:

В книге Крейга Скотта на самом деле есть подраздел, посвященный этой теме:

https://crascit.com/professional-cmake/

Глава 18 Пользовательские цели

Глава 18.3 Команды, генерирующие файлы

РЕДАКТИРОВАТЬ #2:

Если все остальное не удается, попробуйте официальный дискурс cmake: https://discourse.cmake.org /

Ваш вопрос правильно сформулирован, и разработчики cmake ищут их вопросы. Итак, вы должны получить ответ.

Комментарии:

1. Но чем это отличается от включения сгенерированных источников и заголовков в строку add_library моей общей библиотеки?

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

3. Попробовав ваше предложение сейчас, вы вернетесь с результатами

4. К сожалению, ваше предложение ничего не изменило, я все же взгляну на эту ссылку.

5. Вы не обязаны использовать add_custom_target() при использовании add_custom_command() . Но есть определенные условия, при которых использование add_custom_target() является предпочтительным. Например, когда add_custom_command() генерируется вывод, который будет использоваться в нескольких целях.