Доброе утро!
Для некоторых целей бывает удобно сгенерировать код автоматически и использовать его с далее. Возникает вопрос: как подружить такую штуку с CMake? Попробуем на него ответить.
В моём случае возникла необходимость генерировать парсеры и классы из описания некоторых тегов. На этом примере и рассмотрим решение данной задачи.
Был выставлен ряд требований:
- возможность обновления каталога сборки при изменении описания;
- возможность обновления каталога сборки при изменении генератора;
- интегрируемость с QtCreator.
Последнее требование выставлено в связи с тем, что я пользуюсь данной средой разработки, а она, в свою очередь, разбирает каталог сборки, созданный CMake.
\\
Прикинем, как это всё реализовать. Очевидно, что нужен генератор кода. В моём случае он будет называться tag_data_gen.pl
. Интерфейс у него следующий: первым аргументом командной строки передаётся путь к каталогу для сохранения, остальными — пути к файлам с описанием, по которому генерировать. Вывод его выглядит следующим образом:
set(generated_files
tag1.td.hpp
tag1.td.cpp
tag2.td.hpp
tag2.td.hpp
)
Конкретная реализация генератора и формата описания в рамках данной задачи не принципиальна.
\\
Пусть сами исходники проекта находятся в src/
. Тогда создадим каталог src/tag_data/
, в котором будут располагаться наши файлы с описанием для генератора.
Добавим в CMakeLists.txt следующие строки:
set(src src)
include_directories(${src})
set(tag_data
${src}/tag_data/tag1.td
${src}/tag_data/tag2.td
)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
${tag_data}
tag_data_gen.pl
)
Таким образом мы научили CMake воспринимать эти файлы как часть структуры проекта. Обновление их должно вести к пересборке зависимостей.
Следующим этапом необходимо вызвать генератор. Для этого создадим каталог сгенерированных файлов и вызовем генератор, перенаправив его вывод (см. пример выше) в файл. Смысл сего действия будет пояснён далее. Достигается это парой нехитрых команд:
make_directory("${CMAKE_BINARY_DIR}/generated")
execute_process(
COMMAND perl tag_data_gen.pl ${CMAKE_BINARY_DIR}/generated ${tag_data}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_FILE generated_files.cmake
)
Теперь вывод вида set(generated_files …)
будет находиться в файле generated_files.cmake
. Это даёт нам возможность получить список сгенерированных файлов в виде листовой переменной в одну команду:
include(generated_files.cmake)
Ну и можем сделать что-нибудь, например, такое:
add_library("generated" SHARED ${generated_files})
get_filename_component(gendirpath ${CMAKE_BINARY_DIR}/generated ABSOLUTE)
include_directories(${gendirpath})
Теперь мы получили отдельную единицу трансляцию (в данном случае — динамическую библиотеку) из наших сгенерированных файлов, заодно добавив их в структуру проекта, что позволит их безболезненно #include "…"
и видеть их в QtCreator.
\\
Как оказалось, не так уж и сложно.