【cmake】大型项目中组件的配置应用

1 篇文章 0 订阅

 

引言

在工程中需要用到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--

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值