如何通过CMake自动复制依赖库的资源至可执行文件?
问题描述
现有项目结构如下:
root/ Library1/ ... .cpp & .h files ... resources/ textures/... strings/... Library2/ ... .cpp & .h files ... resources/ html/... ...Other Libraries.../... Product1/ main.cpp Product2/ main.cpp ...Other Products.../...
假设Product1依赖Library1(无论直接还是间接依赖),product1.exe正常运行时,其所在目录需包含resources文件夹,且该文件夹需包含root/Library1/resources下的所有资源。
需求:如何通过CMake确保当可执行文件依赖某库时,该库的资源会被自动复制?
注意事项:
- 避免让Product1直接依赖
COPY_LIBRARY1_RESOURCES这类目标,因为Library1可能是间接依赖,容易遗漏。 - 仅针对可执行文件执行复制操作,不要在库之间进行资源复制。
解决方案
核心思路是利用CMake的目标属性传递机制,让库把自身资源的复制规则自动关联到依赖它的可执行文件上,无需手动在可执行文件配置中额外添加依赖。
1. 定义资源复制的通用辅助函数
可以在项目根目录的CMakeLists.txt中定义这个函数(或者每个库的CMakeLists.txt中单独定义),用来统一处理资源复制逻辑:
# 函数功能:给指定目标添加资源目录复制规则 function(target_copy_resources TARGET RESOURCE_DIR) # 获取可执行文件的输出目录 get_target_property(TARGET_OUTPUT_DIR ${TARGET} RUNTIME_OUTPUT_DIRECTORY) if(NOT TARGET_OUTPUT_DIR) set(TARGET_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) endif() # 目标资源存放路径:可执行文件所在目录下的resources文件夹 set(DEST_RESOURCE_DIR "${TARGET_OUTPUT_DIR}/resources") # 执行目录复制操作,POST_BUILD表示编译完成后执行 add_custom_command( TARGET ${TARGET} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory "${RESOURCE_DIR}" "${DEST_RESOURCE_DIR}" COMMENT "Copying resources from ${RESOURCE_DIR} to ${DEST_RESOURCE_DIR}" ) endfunction()
2. 为库配置资源传递规则
以Library1为例,在其Library1/CMakeLists.txt中,创建库目标后,通过INTERFACE属性把资源复制规则传递给所有依赖它的目标:
# 创建Library1库目标(STATIC/SHARED根据实际需求选择) add_library(Library1 STATIC # 此处添加Library1的cpp/h源文件 ... ) # 通过INTERFACE_LINK_OPTIONS传递资源复制命令,仅对可执行文件生效 target_link_options(Library1 INTERFACE "$<IF:$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>,$<COMMAND:target_copy_resources,$<TARGET_NAME>,${CMAKE_CURRENT_SOURCE_DIR}/resources>,>" )
这里的条件判断$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>确保只有当依赖目标是可执行文件时,才会触发资源复制,避免库之间互相复制资源。
3. 可执行文件无需额外配置
在Product1的Product1/CMakeLists.txt中,只需要正常链接依赖的库即可,CMake会自动沿着依赖链触发资源复制:
add_executable(Product1 main.cpp) # 即使是间接依赖Library1(比如LibraryX依赖Library1),也会自动复制Library1的资源 target_link_libraries(Product1 PRIVATE LibraryX)
补充优化:避免多库资源冲突
如果多个库都有resources目录,直接复制会导致文件覆盖,可以修改复制逻辑,把每个库的资源放到resources/${库名}子目录下,同时调整代码中的资源访问路径。
修改后的辅助函数:
function(target_copy_resources TARGET RESOURCE_DIR LIBRARY_NAME) get_target_property(TARGET_OUTPUT_DIR ${TARGET} RUNTIME_OUTPUT_DIRECTORY) if(NOT TARGET_OUTPUT_DIR) set(TARGET_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) endif() set(DEST_RESOURCE_DIR "${TARGET_OUTPUT_DIR}/resources/${LIBRARY_NAME}") add_custom_command( TARGET ${TARGET} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory "${RESOURCE_DIR}" "${DEST_RESOURCE_DIR}" COMMENT "Copying ${LIBRARY_NAME} resources from ${RESOURCE_DIR} to ${DEST_RESOURCE_DIR}" ) endfunction()
对应的库配置修改为:
target_link_options(Library1 INTERFACE "$<IF:$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>,$<COMMAND:target_copy_resources,$<TARGET_NAME>,${CMAKE_CURRENT_SOURCE_DIR}/resources,Library1>,>" )
内容的提问来源于stack exchange,提问作者Sam Coutteau
相关产品推荐
相关产品推荐

