引言
在工程中需要用到Boost库时,CMakeLists.txt通常是这样写的:
find_package(Boost COMPONENTS system thread)
这样就可以按需依赖Boost的组件动态库,而不是整个Boost库。
思考,find_pacakge如何查找组件?如何让自己的工程库也能通过查找组件的方式找到各个子功能呢?
find_package如何查找组件?
find_package(<package> [version] [EXACT] [QUIET] [MODULE]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[NO_POLICY_SCOPE])
官网链接:https://cmake.org/cmake/help/v3.5/command/find_package.html
简单翻译下:
参数说明:
[version] EXACT --指定要查找的包的版本,及是否完全匹配
QUIET --不显示查询产生的信息
REQUIRED --未找到则产生错误信息
常用相关变量:
<PackageName>_FOUND:找到则为true
<PackageName>_FIND_COMPONENTS: 搜索的组件列表
<PackageName>_INCLUDE_DIRS:找到的头文件列表
<PackageName>_LIBRARIES:找到的动态库列表
查找过程:
两种查找方式:Module模式(查找Find<PackageName>.cmake)和Config模式(查找<PackageName>Config.cmake或者<PackageName>-config.cmake)。
当指定[Module]选项时只使用Module模式查找,否则,先从Module模式查找,找不到,再从Config模式查找。
Module模式查找路径顺序:
1)CMAKE_MODULE_PATH 变量;
2)CMake自带Find模块;
Config模式查找路径顺序:
<prefix>/(lib/<arch>|lib*|share)/cmake/<name>*/ (U)
<prefix>/(lib/<arch>|lib*|share)/<name>*/ (U)
<prefix>/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/ (U)
Config模式<prefix>包含的路径: (版本3.5.1,可根据自己的cmake版本查找官方文档)
1)-DVAR=value方式配置:CMAKE_PREFIX_PATH、CMAKE_FRAMEWORK_PATH、CMAKE_APPBUNDLE_PATH;
2)环境变量配置:<PackageName>_DIR、CMAKE_PREFIX_PATH、CMAKE_FRAMEWORK_PATH、CMAKE_APPBUNDLE_PATH;
3)HINTS指定目录;
4)系统环境变量:PATH(如果路径为/bin或者/sbin则自动查找父级路径);
5)用户包注册表中的路径;
6)指定PATHS查找的路径;
如何创建自己的工程组件?
思路:
1)假设工程名称为test_cmake,需生成test_cmakeConfig.cmake文件,并放置到其他工程能够找到的目录,如/usr/local,该文件能够根据test_cmake_FIND_COMPONENTS搜索的组件列表,自动查找被依赖的组件,并填充test_cmake_INCLUDE_DIRS和test_cmake_LIBRARIES变量,供其他工程访问。
2) 各个组件的Config.cmake文件中声明组件之间的依赖关系。
实现:
参考Poco源码: https://github.com/pocoproject/poco
实现案例资源链接:https://download.csdn.net/download/JL_Gao/12919907
test_cmake用于生成两个组件component1和component2,工程目录结构如下:
1 顶层CMakeLists.txt文件
功能:设置一些环境变量、遍历所有组件目录进行编译、安装cmake文件
1)设置CMAKE_MODULE_PATH(本工程cmake所在路径),并包含定义的宏文件
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
include(test_cmakeMacros)
2)add_subdirectory递归遍历所有子目录
# find all subdirectories
MACRO(SUBDIRLIST result curdir)
file(GLOB children RELATIVE ${curdir} ${curdir}/*)
set(dirlist "")
FOREACH(child ${children})
IF(IS_DIRECTORY ${curdir}/${child})
list(APPEND dirlist ${child})
ENDIF()
ENDFOREACH()
set(${result} ${dirlist})
ENDMACRO()
set(project_COMPONENTS "")
FUNCTION(ADDSUBDIR curdir)
SUBDIRLIST(SUBDIRS ${curdir})
FOREACH(subdir ${SUBDIRS})
IF(EXISTS ${curdir}/${subdir}/CMakeLists.txt)
message("add_subdirectory:${curdir}/${subdir}")
add_subdirectory(${curdir}/${subdir})
list(APPEND project_COMPONENTS ${subdir})
ELSE()
ADDSUBDIR(${curdir}/${subdir})
ENDIF()
ENDFOREACH()
ENDFUNCTION()
ADDSUBDIR(${CMAKE_SOURCE_DIR})
3)安装cmake文件
set(projectConfigPackageLocation "lib${LIB_SUFFIX}/cmake/${PROJECT_NAME}")
configure_file(cmake/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Config.cmake" @ONLY)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Config.cmake
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}ConfigVersion.cmake
DESTINATION
"${projectConfigPackageLocation}"
COMPONENT
Devel
)
说明:
configure_file:根据test_cmakeConfig.cmake.in生成test_cmakeConfig.cmake文件。
install:make install安装时,将test_cmakeConfig.cmake和test_cmakeConfigVersion.cmake拷贝到安装目录。
2 顶层cmake目录下的文件
cmake_uninstall.cmake.in --卸载cmake
test_cmakeConfigVersion.cmake .in --版本cmake
test_cmakeConfig.cmake.in --config模式cmake
1)获取find_package的test_cmake_FIND_REQUIRED参数和test_cmake_FIND_QUIETLY参数。
2)遍历搜索test_cmake_FIND_COMPONENTS中指定的组件,并填充test_cmake_INCLUDE_DIRS变量和test_cmake_LIBRARIES变量,这两个变量就是其他工程需要依赖的头文件和动态库列表。注意遍历时查找的包名称为:
"test_cmake-${module}",也就是要查找"test_cmake-${module}Config.cmake"文件,这个文件就是组件/cmake目录下的make文件。
set(FIND_PROJECT_NAME test_cmake)
foreach(module ${${FIND_PROJECT_NAME}_FIND_COMPONENTS})
find_package(${FIND_PROJECT_NAME}-${module}
${_project_FIND_PARTS_QUIET}
${_project_FIND_PARTS_REQUIRED}
PATHS "${_project_install_prefix}" NO_DEFAULT_PATH
)
if (NOT ${FIND_PROJECT_NAME}-${module}_FOUND)
if (${FIND_PROJECT_NAME}_FIND_REQUIRED_${module})
set(_project_NOTFOUND_MESSAGE "${_project_NOTFOUND_MESSAGE}Failed to find ${FIND_PROJECT_NAME} component \"${module}\" config file at \"${_project_install_prefix}/${FIND_PROJECT_NAME}-${module}/${FIND_PROJECT_NAME}-${module}Config.cmake\"\n")
elseif(NOT ${FIND_PROJECT_NAME}_FIND_QUIETLY)
message(WARNING "Failed to find ${FIND_PROJECT_NAME} component \"${module}\" config file at \"${_project_install_prefix}/${FIND_PROJECT_NAME}-${module}/${FIND_PROJECT_NAME}-${module}Config.cmake\"")
endif()
endif()
# For backward compatibility set the LIBRARIES variable
list(APPEND ${FIND_PROJECT_NAME}_LIBRARIES "${FIND_PROJECT_NAME}::${module}")
endforeach()
list(APPEND ${FIND_PROJECT_NAME}_INCLUDE_DIRS "${CMAKE_INSTALL_PREFIX}/include/${FIND_PROJECT_NAME}")
test_cmakeMacros.cmake --组件用到的宏PROJECT_GENERATE_PACKAGE(用于安装cmake文件)和PROJECT_INSTALL_COMPONENT(用于安装组件的头文件和动态库等)
3 组件CMakeLists.txt文件
用于生成组件动态库,其中component2依赖于component1。component2的CMakeLists.txt文件如下:
cmake_minimum_required(VERSION 3.5.1)
set(COMPONENT_NAME component2)
find_package(Boost COMPONENTS system thread)
add_library(${COMPONENT_NAME} SHARED
src/component2.cpp
)
add_library(${PROJECT_NAME}::${COMPONENT_NAME} ALIAS ${COMPONENT_NAME})
set_target_properties(${COMPONENT_NAME}
PROPERTIES
VERSION ${SHARED_LIBRARY_VERSION} SOVERSION ${SHARED_LIBRARY_VERSION}
OUTPUT_NAME ${PROJECT_NAME}-${COMPONENT_NAME}
DEFINE_SYMBOL ${COMPONENT_NAME}_EXPORTS
)
target_link_libraries(${COMPONENT_NAME}
${Boost_LIBRARIES}
${PROJECT_NAME}::component1
)
target_include_directories(${COMPONENT_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
)
PROJECT_INSTALL_COMPONENT(${COMPONENT_NAME})
PROJECT_GENERATE_PACKAGE(${COMPONENT_NAME})
1)动态库component2的别名为"test_cmake::component2",生成的动态库名称为"libtest_cmake-component2.so"
2)调用PROJECT_INSTALL_COMPONENT宏,安装头文件和动态库
3)调用PROJECT_GENERATE_PACKAGE宏,安装test_cmake-component2Config.cmake和导出安装test_cmake-component2Targets.cmake。
export(EXPORT "${target_name}Targets"
FILE "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}-${target_name}Targets.cmake"
NAMESPACE "${PROJECT_NAME}::"
)
4 组件cmake目录下的文件
test_cmake-component2Config.cmake --用于指定依赖的其他组件
include(CMakeFindDependencyMacro)
find_dependency(test_cmake-component1)
include("${CMAKE_CURRENT_LIST_DIR}/test_cmake-component2Targets.cmake")
过程说明:
编译过程(以component2为例):
1)顶层CMakeLists.txt文件变量子目录(组件目录)进行编译;
2)组件CMakeLists.txt文件生成component2动态库,别名为test_cmake::component2,动态库文件名为 "libtest_cmake-component2.so"。导出"test_cmake-component2Targets.cmake"文件,安装"test_cmake-component2Config.cmake"文件。
搜索组件时(以component2为例):
1)调用test_cmakeConfig.cmake文件,遍历要搜索的组件,搜索 "test_cmake-component2Config.cmake" 文件是否存在。
2)若存在,则将test_cmake::component2加入到test_cmake_LIBRARIES列表。
如何使用工程组件?
test_cmake_find工程CMakeLists.txt
cmake_minimum_required(VERSION 3.5.1)
project(test_cmake_find)
find_package(test_cmake REQUIRED COMPONENTS
component1
component2
)
include_directories(
include
${test_cmake_INCLUDE_DIRS}
)
add_executable(${PROJECT_NAME}
src/test_cmake_find.cpp
)
target_link_libraries(${PROJECT_NAME}
${test_cmake_LIBRARIES}
)
--End--
--如果不正之处请指正。待细节补充2020.10.12--