Компиляция сгенерированных файлов CMake
У меня есть список файлов, которые генерируются во время процесса сборки CMake. Я хочу скомпилировать эти файлы с помощью "add_library" после этого, но я не буду знать, какие файлы будут генерироваться до тех пор, пока они не будут сгенерированы. Есть ли способ построить это в CMake script?
Ответы
Ответ 1
Ну, я думаю, что это возможно, поэтому я поделюсь тем, что я сделал. Моя проблема заключалась в том, что мне пришлось скомпилировать несколько простоя CORBA для использования как часть источника проекта, и я не хотел вручную перечислить каждый файл. Я думал, что лучше найти файлы. Поэтому я сделал это следующим образом:
file(GLOB IDLS "idls/*.idl")
set(ACE_ROOT ${CMAKE_FIND_ROOT_PATH}/ace/ACE-${ACE_VERSION})
foreach(GENERATE_IDL ${IDLS})
get_filename_component(IDLNAME ${GENERATE_IDL} NAME_WE)
set(OUT_NAME ${CMAKE_CURRENT_SOURCE_DIR}/idls_out/${IDLNAME})
list(APPEND IDL_COMPILED_FILES ${OUT_NAME}C.h ${OUT_NAME}C.cpp ${OUT_NAME}S.h ${OUT_NAME}S.cpp)
add_custom_command(OUTPUT ${OUT_NAME}C.h ${OUT_NAME}C.cpp ${OUT_NAME}S.h ${OUT_NAME}S.cpp
COMMAND ${ACE_ROOT}/bin/tao_idl -g ${ACE_ROOT}/bin/ace_gperf -Sci -Ssi -Wb,export_macro=TAO_Export -Wb,export_include=${ACE_ROOT}/include/tao/TAO_Export.h -Wb,pre_include=${ACE_ROOT}/include/ace/pre.h -Wb,post_include=${ACE_ROOT}/include/ace/post.h -I${ACE_ROOT}/include/tao -I${CMAKE_CURRENT_SOURCE_DIR} ${GENERATE_IDL} -o ${CMAKE_CURRENT_SOURCE_DIR}/idls_out/
COMMENT "Compiling ${GENERATE_IDL}")
endforeach(GENERATE_IDL)
set_source_files_properties(${IDL_COMPILED_FILES}
PROPERTIES GENERATED TRUE)
set(TARGET_NAME ${PROJECT_NAME}${DEBUG_SUFFIX})
add_executable(
${TARGET_NAME}
${SOURCE}
${IDL_COMPILED_FILES}
)
Свойства GENERATED полезны, если один из моих компиляций idl (* C.cpp, * Ch, * S.cpp и * Sh) не создан, поэтому команда сборки не жалуется, что файл не работает 't существует.
Ответ 2
Ну, это можно сделать с помощью двух сценариев CMake с использованием функции CMake ExternalProject.
Простое решение
В главном CMake script вам нужно добавить настраиваемую цель, которая генерирует исходные файлы следующим образом и ссылку на второй (внешний) CMake
проект:
# Main CMakeLists.txt
add_custom_target(
code_generation
COMMAND your_code_generation_tool -o ${CMAKE_CURRENT_BINARY_DIR}/libgenerated/
)
include(ExternalProject)
ExternalProject_Add(
libgenerated
DEPENDS code_generation
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/path/to/external/project/
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/libgenerated-build
CMAKE_ARGS -DGENERATED_SOURCE_DIR=${CMAKE_CURRENT_BINARY_DIR}/libgenerated
BUILD_ALWAYS 1
INSTALL_CMD ""
)
add_executable(
${PROJECT_NAME}
...
)
target_link_libraries(
${PROJECT_NAME}
${CMAKE_CURRENT_BINARY_DIR}/libgenerated-build/libgenerated.a
)
add_dependencies(${PROJECT_NAME} libgenerated)
Теперь вы можете выполнить флеширование файлов во втором (внешнем) CMake script и связать все файлы, найденные в статической библиотеке:
# External project CMakeLists.txt
project(libgenerated)
file(GLOB_RECURSE SOURCES ${GENERATED_SOURCE_DIR}/*)
add_library(${PROJECT_NAME} ${SOURCES})
В этом простом решении ваши файлы генерируются и создаются каждый раз, когда вы запускаете шаг сборки, даже если ничего не изменилось. Если вы хотите этого избежать, вы можете добавить файл штампа в свою целевую цель, как в следующем улучшенном решении:
Решение файла штампа
# Main CMakeLists.txt
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/libgenerated-stamp
COMMAND your_code_generation_tool -o ${CMAKE_CURRENT_BINARY_DIR}/libgenerated/
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/libgenerated-stamp
DEPENDS the_input_file(s)_you_generate_your_code_from
)
add_custom_target(
code_generation
DEPENDS ${CMAKE_CURRENT_BUILD_DIR}/libgenerated-stamp
)
...
Если изменение входных файлов для вашего генератора кода не обязательно приводит к изменению всех сгенерированных файлов, вы можете улучшить решение дальше, используя команду CMake copy_if_different
во внешнем проекте, например, в следующем расширенном решении:
Расширенное решение
# External project CMakeLists.txt
project(libgenerated)
file(GLOB_RECURSE SOURCES ${GENERATED_SOURCE_DIR}/*)
add_custom_target(
make_directory
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/generated
)
foreach(FILE ${SOURCES})
get_filename_component(BASENAME ${FILE} NAME)
list(APPEND ACTUAL_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/generated/${BASENAME}")
add_custom_target(
copy_${BASENAME}_if_different
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${FILE} ${CMAKE_CURRENT_BINARY_DIR}/generated
)
add_dependencies(make_directory copy_${BASENAME}_if_different)
endforeach()
add_library(${PROJECT_NAME} ${ACTUAL_SOURCES})
add_dependencies(${PROJECT_NAME} make_directory)
В этом решении все сгенерированные файлы копируются в другое место (${CMAKE_CURRENT_BINARY_DIR}/generated
), если они были изменены или добавлены и построены оттуда. Этот подход приводит к сборке только измененных файлов (но требует очистки, когда файлы были удалены).
Ответ 3
Если вы не знаете имя файлов, которые будут сгенерированы, вы можете "glob" папки, в которых они находятся.
file( GLOB_RECURSE MY_SRC dest_folder/*.cpp )
add_library( libname SHARED ${MY_SRC} )
Теперь я не уверен, что вызывает генерации этих файлов. "Глобус" произойдет только при ручном запуске cmake: он не сможет автоматически обнаружить, что новые файлы присутствуют.
Ответ 4
Рассматривайте это как не-ответ, просто больше информации:
Недавно мне пришлось что-то сделать для одного случая, когда у меня был файл .cpp, который был сгенерирован автоматически, но я не мог понять, как заставить CMake создать файл проекта Visual Studio, который затем скомпилировал его. Мне приходилось прибегать к чему-то довольно вонючим: мне пришлось #include <the_generated.cpp>
файл из другого файла, который находился в каталоге ${CMAKE_CURRENT_SOURCE}
. Это не поможет вам в вашем случае, потому что я подозреваю, что у вас есть несколько файлов .cpp, поэтому этот подход не масштабируется.
Кроме того, я обнаружил, что свойство исходного файла GENERATED
при добавлении в файл вообще не помогло.
Я рассматриваю это условие как ошибку в Visual Studio (в моем случае это был VS2008 SP1), либо в том, как CMake генерирует файлы .vcproj или оба.