Как создавать только автоматически сгенерированный код при изменении генератора или входа в CMake?
Я работаю над репозиторием исходного кода, который генерирует некоторый код на С++, запуская заголовки и реализацию python script. Этот код впоследствии компилируется и привязывается к моим библиотекам и исполняемым файлам. Я знаю, что сгенерированный код будет изменяться только в том случае, если выполнено одно из двух условий:
- Сам код генератора меняет
- Вход (файл XML) в генератор меняет
Я хочу использовать cmake для управления процессом сборки. В настоящий момент я использую execute_process
для сжигания генератора. Тем не менее, это выполняется каждый раз, когда я запускаю cmake и затрагивает файлы, в результате чего сгенерированный код перекомпилируется и добавляется к моему общему времени компиляции.
Я также хочу убедиться, что сгенерированный код всегда запускается перед моими библиотеками. Другими словами, я хочу, чтобы библиотеки зависели от запуска генератора.
Каков правильный способ справиться с такой ситуацией в cmake? Я видел этот предыдущий ответ: "Получить CMake для выполнения цели в проекте до создания библиотеки". Но это зависит от того, какой вывод генератора кода известен заранее. Мой генератор кода будет генерировать переменное количество файлов.
Ответы
Ответ 1
Используйте ADD_CUSTOM_COMMAND
для запуска генератора. Он позволяет определять зависимости ввода и вывода и будет работать только в том случае, если выходы более старые, чем входы.
ADD_CUSTOM_COMMAND( OUTPUT generatedfile1 generatedfile2
COMMAND python generateSources.py xmlfile1 xmlfile2
DEPENDS xmlfile1 xmlfile2 generateSources.py
COMMENT "Generating source code from XML" )
Убедитесь, что сгенерированные файлы не используются в нескольких независимых объектах, которые могут быть скомпилированы параллельно, или вы можете (будете) столкнуться во время сборки. Чтобы обеспечить это, следующее должно сделать трюк:
ADD_CUSTOM_TARGET( RunGenerator DEPENDS generatedfile1 generatedfile2
COMMENT "Checking if re-generation is required" )
Затем заставьте ваши другие цели зависеть от этого:
ADD_DEPENDENCY( MyTarget RunGenerator )
Примечание. Цель RunGenerator
всегда будет считаться устаревшей и, таким образом, всегда запускаться. Однако, поскольку он ничего не делает (помимо печати комментария и проверки зависимостей), в этом случае это не имеет значения. Пользовательская команда позаботится о необходимости регенерации.
Обновление после комментариев:
Если вы не знаете имя файла, вы можете использовать
ADD_CUSTOM_COMMAND( OUTPUT generated.timestamp
COMMAND python generateSources.py xmlfile1 xmlfile2
COMMAND ${CMAKE_COMMAND} -E touch generated.timestamp
DEPENDS xmlfile1 xmlfile2 generateSources.py
COMMENT "Generating source code from XML" )
Однако: использование GLOB требует явного запуска CMake для обновления списков файлов.
Интеграция этого в пользовательскую команду, вероятно, испортит ваш процесс сборки (если несколько проектов строятся параллельно, а один проект перезапускает конфигурацию CMake). IIRC, вам нормально запускать CMake вручную, когда вы знаете, что изменились либо python script, либо файлы XML, но ваша проблема в том, что эти файлы затрагиваются, когда что-то еще требует повторного запуска CMake.
Если python script не запускается слишком долго, вы можете позволить ему запускать каждый запуск CMake (как и сейчас), но убедитесь, что неизменяемые файлы не затрагиваются, вы можете попробовать следующее ( непроверенные):
# generated sources files into a temporary directory (adjust your current execute_process)
EXECUTE_PROCESS( COMMAND python ../generateSources.py ../xmlfile1 ../xmlfile2
WORKING_DIRECTORY tmp )
# get the filenames
FILE( GLOB GENERATED_TEMP_FILES tmp/* )
# copy to the "expected" directory, but only if content CHANGED
FOREACH( F ${GENERATED_TEMP_FILES} )
GET_FILENAME_COMPONENT( "${F}" FN NAME)
CONFIGURE_FILE( "${F}" "./generated/${FN}" COPY_ONLY )
ENDFOREACH()
# use your current globbing command
FILE( GLOB GENERATED_SOURCES ./generated/* )
Ответ 2
Решение на низком уровне генерирует ваши файлы в другом каталоге, сопоставляет файл с текущими файлами и копирует только в том случае, если они разные, никакая копия не перекомпилируется.
Это, конечно, работает только в том случае, если генератор не заполняет некоторые случайные шумы, такие как версия и даты. В этом случае вы можете попытаться отфильтровать их.
Ответ 3
У меня была более или менее эта проблема сегодня. Я внедряю двоичные ресурсы в исполняемый файл С++ (это встроенный веб-сервер).
Решил это следующим образом:
#get the file timestamp of the 'source' resource file
FILE(TIMESTAMP "${CMAKE_CURRENT_SOURCE_DIR}/${_resource}" RESOURCE_TIME)
#get the file time of the 'template' file
FILE(TIMESTAMP ${TEMPLATE_FILE} TEMPLATE_TIME)
#get the timestamp (if any) of the generated file
FILE(TIMESTAMP "${CMAKE_CURRENT_BINARY_DIR}/${_filename}" TARGET_TIME)
....
#only configure the file if the target is older than either the
#source or the template
IF((RESOURCE_TIME > TARGET_TIME) OR (TEMPLATE_TIME > TARGET_TIME) OR (NOT TARGET_TIME))
MESSAGE(STATUS "configuring ${...}")
FILE(READ ${_resource} RESOURCE_CONTENT HEX)
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," RESOURCE_CONTENT ${RESOURCE_CONTENT})
configure_file("${TEMPLATE_FILE}"
"${CMAKE_CURRENT_BINARY_DIR}/${_filename}"
@ONLY)
ELSE()
MESSAGE(STATUS "already configured ${...}")
ENDIF()
Ответ 4
У нас были схожие проблемы, где я работаю. Только мы не работаем с cmake, а с другой системой сборки.
Я не знаю cmake, но это может дать вам несколько идей:
Наше решение включало обработку xmls и генератора в качестве кода, который необходимо скомпилировать, рассматривая генератор как зависимость и xml файлы как источник. Если файл генератора или xml изменился, система сборки запустила генератор (который сгенерировал все, даже если был изменен только один файл, что означает, что он касается всех сгенерированных файлов).
Система сборки, конечно, не запускала генератор напрямую, но вместо этого мы написали небольшой python script, который решил, как правильно запускать генератор - например, улучшение, которое мы можем добавить, генерирует все файлы в /tmp и только перемещение измененных файлов (сравнение с использованием diff), поэтому будут затронуты только измененные файлы. (нам это не нужно в данный момент, так как наши файлы не так часто меняются)
Мы также дважды запускали систему сборки с двумя разными графиками, одним графом для генераторов и другим графиком для других файлов. Мы разработали его, чтобы позволить несколько уровней генерируемых генератором зависимостей, чтобы один генератор мог полагаться на сгенерированные продукты другого.
Еще два трюка, которые следует учитывать, если ваша система сборки позволяет использовать регулярные выражения для сборки файлов, вы можете использовать ее. Кроме того, вы можете создавать файлы конфигурации для вашей системы сборки в процессе генерации.