美文网首页
Cmake中find_package命令的搜索模式之模块模式(M

Cmake中find_package命令的搜索模式之模块模式(M

作者: Domibaba | 来源:发表于2022-03-30 04:31 被阅读0次

一、模块模式简介

  前面介绍了find_package有两种搜索包的模式(参考find_package介绍),本篇文章介绍其中的一种:模块模式(Module Mode)。在这种模式下,当调用find_package命令查找<PackageName>包的时候,实际上会去查找一个名为Find<PackageName>.cmake的文件,这个文件的主要任务就是确定一个包是否可用,查找的结果会反映在变量<PackageName>_FOUND上供find_package的调用者使用。当找到可用的包,同时也会提供使用这个包所需要的变量、宏和导入目标(例如库文件)。
  前面已经介绍过使用系统提供的FindLibLZMA.cmake来查找LibLZMA库,这个文件是CMake在安装的时候就提供的,位于CMake的安装目录之下。因此对于未提供该文件的第三方库,我们可以通过自己生成Find<PackageName>.cmake来供CMake使用,接下来将介绍如何利用自己生成的.cmake文件找到自己编写的库。
  模块模式依赖另一个程序:pkg-config,在继续往下之前,请参考pkg-config用法详解了解这个命令。

二、标准变量名称

  Find<PackageName>.cmake文件承担了定义<PackageName>包相关的变量的作用,这些变量称作"标准变量"。一旦find_package调用成功,这些变量将返回给调用者使用,为了保证不同的包之间返回的变量不冲突,对编写的Find<PackageName>.cmake返回的标准变量名称有如下约束:所有的变量都是以PackageName_开头,PackageName就是文件Find<PackageName>.cmake中的<PackageName>,必须完全一致,大小写敏感。简单的列举几个变量定义如下,更多的变量定义见本文的四、对标准变量名称的更多说明

  • PackageName_INCLUDE_DIRS:使用包需要包含的头文件。
  • PackageName_LIBRARIES:使用包所需要的库文件,是全路径或者链接器能在库搜索目录下找到的库文件名称。
  • PackageName_DEFINITIONS:使用包所需要的编译选项。
  • PackageName_LIBRARY:库的路径,只有当包提供的是单个库的时候才能使用这形式。
  • PackageName_INCLUDE_DIR:使用包所需要包含的头文件目录,只能在单个库的使用,使用者需要将该路径加入到搜索路径中。

三、编写.cmake文件

  接下来我们来写一个.cmake文件,假设我们的包名为mymath,该包提供一个libmymath.a的库,其中包含一个add接口,简单计算两个整数的和并打印出结果(对这个库的更多信息可以参考pkg-config用法详解4.1~4.3小节)。在编写Findmymath.cmake之前,我们先来看下.cmake文件的格式。

3.1 .cmake文件的格式

  • 文件开头是license信息
  • 接着是一个CMake支持的多行注释(单行注释以#开头;多行注释是指:以#[开头,紧接着跟着0个或多个=,之后是[,接下来就是注释内容,注释可以跨越多行,然后以]0个或多个=]组成结束,开头的=个数要和结尾的=个数相等),.cmake要求注释以.rst:开头:
# 多行注释,0个=的情况
#[[
...
中间的内容都是注释
...
]]
# 多行注释,多个=的情况,开始的=和结尾的=要保持数量一致,此例子中为5个=
#[=====[
...
中间的内容都是注释
...
]=====]
  • 接下来在注释中间申明find_package的标准变量及相关说明。
    1)首先是包的名字,分为两行,第一行是包名字,第二行是包名字下方的下划线---(与包名字等长度)。
    2)接着是对包的一个简要描述,这个没有特殊要求。
    3)接下来分为几个部分,主要是作用对几类变量的申明(导入变量、结果变量、缓存变量),格式都是一致的:首先是变量类型的说明,并在其下方以等长^^^^标识,接着是一段文件对变量内容的简要描述,接着是我们要定义的标准变量了,变量以``标准变量``标识(两对``符号,中间是变量名称),每个变量下可以对变量做一个简短的描述说明。
  • 注释部分到此结束,接下来是对库进行真正查找,并把注释部分申明的变量进行赋值的过程。
    1)先尝试使用pkg-config来找到真正的库,pkg-config是系统提供的命令用于找系统中是否存在相关的库(参考pkg-config用法详解),在CMake中使用如下两条,CMake会从<PackageName>.pc文件中读取对应的变量。
    find_package(PkgConfig)
    pkg_check_modules(PC_mymath QUIET mymath)
    2)如果能找到库,那么变量PC_mymath_FOUND存在,并且可以得到mymath相关的头文件和库目录,并且是存储在以PC_mymath_XXX开头的变量中,例如PC_mymath_INCLUDE_DIRS(头文件目录)、PC_mymath_LIBRARY_DIRS(库文件目录)、PC_mymath_LIBRARIES(库名称)等等(具体有哪些变量可以参考man pkg-config或者在CMakeCache.txt中过滤PC_mymath查看)。
  • 上一步利用了pkg-config获得的变量还不是最终要给find_package返回的变量,我们要对返回的变量做正确的赋值,并最终调用include(FindPackageHandleStandardArgs)find_package_handle_standard_args将变量返回给find_package调用处。
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(mymath
FOUND_VAR mymath_FOUND
REQUIRED_VARS
 mymath_LIBRARY
 mymath_INCLUDE_DIR
VERSION_VAR mymath_VERSION
)

3.2 编写自己的Findmymath.cmake文件

  假定我们使用的库mymath已经提供了.pc文件,并能够通过pkg-config方式找到它(可以通过pkg-onfig --list-all查看到mymath库,参考pkg-config用法详解)。

  接下来我们来编写库mymathFindmymath.cmake文件,参照前面.cmake文件说明,内容如下,示例只提供了库目录、库文件、头文件等少量变量信息:

# Findmymath.cmake

#[============[.rst:
Findmymath
----------

对这个文件的描述:查找mymath库

Imported Targets
^^^^^^^^^^^^^^^^

如果提供导出可执行目标,可以在下面进行定义:
``mymath::mymath``
    导出mymath::mymath可执行目标,我们的测试库并未提供,该处只是一个示意

Result Variables
^^^^^^^^^^^^^^^^

可以在下面定义一些普通变量:

``mymath_FOUND``
    如果找到mymath库,该变量值为True.
``mymath_VERSION``
    mymath库的版本.
``mymath_INCLUDE_DIRS``
    使用mymath库需要包含的头文件.
``mymath_LIBRARIES``
    使用mymath库需要用到的库文件.

Cache Variables
^^^^^^^^^^^^^^^

可以在下面定义一些缓存变量:

``mymath_INCLUDE_DIR``
    包含mymath.h头文件的目录.
``mymath_LIBRARY``
    mymath库所在的目录.
]============]

find_package(PkgConfig)
pkg_check_modules(PC_mymath QUIET mymath)

find_path(mymath_INCLUDE_DIR
    NAMES mymath.h
    PATHS ${PC_mymath_INCLUDE_DIRS}
    PATH_SUFFIXES mymath)

find_library(mymath_LIBRARY
    NAMES mymath
    PATHS ${PC_mymath_LIBRARY_DIRS})

set(mymath_VERSION ${PC_mymath_VERSION})
set(mymath_INCLUDE_DIRS "/just/for/include/test") # 只是为了测试用,没有实际作用
set(mymath_LIBRARY_DIRS "/just/for/library/test") # 只是为了测试用,没有实际作用

# 将变量导出给调用者使用
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(mymath
  FOUND_VAR mymath_FOUND
  REQUIRED_VARS
    mymath_LIBRARY
    mymath_INCLUDE_DIR
    mymath_INCLUDE_DIRS
    mymath_LIBRARY_DIRS
  VERSION_VAR mymath_VERSION
)

  我们的测试文件test.cpp如下:

// test.cpp
#include "mymath.h"
int main(int argc, char** argv)
{
    mymath::add(1, 2);
    return 0;
}

  最后,在我们的CMakeLists.txt来使用find_package来找到并使用mymath

# CMakeLists.txt,和测试文件test.cpp在同个目录
cmake_minimum_required(VERSION 3.10.2)
project(find_package_test)
message("Find path: ${CMAKE_MODULE_PATH}")
find_package(mymath)
if (mymath_FOUND)
    add_executable(test test.cpp)
    include_directories(${mymath_INCLUDE_DIR})
    target_link_libraries(test ${mymath_LIBRARY})
    set_target_properties(test PROPERTIES
        INTERFACE_COMPILE_OPTIONS "${PC_mymath_CFLAGS_OTHER}") # 这个语句在本例中不是必须,只是说明可以通过这种方式获取编译mymath库需要使用的编译选项
endif()

  在CMakeLists.txt下执行cmake命令,此处为了演示,直接指定CMAKE_MODULE_PATH的值为当前的.cmake所在路径,这样find_package命令会直接找到我们编写的.cmake文件并读取其中的内容,编译并运行最终程序(只摘取了我们关注的显示信息):

# 执行cmake
cmake . -DCMAKE_MODULE_PATH=./

# 运行结果,首次会打印出来找到的库以及所在的全路径、版本号等信息
......
-- Found PkgConfig: /usr/local/bin/pkg-config (found version "0.29.2")
-- Found mymath: /XXX/mymath/lib/libmymath.a (found version "1.0")
......

# 执行make
make

# 运行程序
./test

# 运行结果
Add 1 and 2 is 3

四、对标准变量名称的更多说明

  • PackageName_INCLUDE_DIRS:使用包需要包含的头文件。

  • PackageName_LIBRARIES:使用包所需要的库文件,是全路径或者链接器能在库搜索目录下找到的库文件名称。

  • PackageName_DEFINITIONS:使用包所需要的编译选项。

  • PackageName_EXECUTABLE:可执行文件的全路径。

  • PackageName_YYY_EXECUTABLE:与上面类似,不过此处的YYY表示包中提供的其他可执行文件的名字,通常是全大写,可以使用这种方式避免名称冲突。可以用于包提供多个可执行文件的场景。

  • PackageName_LIBRARY_DIRS:可选,库目录。

  • PackageName_ROOT_DIR:包查找的根目录

  • PackageName_VERSION_VV:指定模块的版本号是VV,但是要注意,一个模块可能有多个历史的版本,只能有一个版本的变量设置成true,例如模块mymath有三个版本,分别是Version 1Version 2Version 3,那么mymath_VERSION_1mymath_VERSION_2mymath_VERSION_3这三个变量只能有一个设置为true,否则会报错。

  • PackageName_WRAP_YY:当该变量设置为false,表明相关的包装命令无法使用。

  • PackageName_Yy_FOUND:此处的Yy表示包的组件,必须跟find_package命令提供的组件名称完全匹配,如果该变量设置为false,表明组件未找到或者不可用。可用于检查哪些组件是可用的。

  • PackageName_FOUND:如果能找到模块,那么该变量会置为true

  • PackageName_NOT_FOUND_MESSAGE:未找到时候的消息提示。

  • PackageName_RUNTIME_LIBRARY_DIRS:可选,运行库的搜索路径,当可执行文件需要链接到共享库的时候需要指定该变量,以让链接器能找到对应的共享库路径。在windows下,会将该变量内容加入到PATH,Linux下会加入到LD_LIBRARY_PATH

  • PackageName_VERSION:找到模块的全版本字符串,值得注意的是当前许多存在的模块使用的是PackageName_VERSION_STRING替代。

  • PackageName_VERSION_MAJOR:主版本号

  • PackageName_VERSION_MINOR:次版本号

  • PackageName_VERSION_PATCH:补丁版本号

  • PackageName_LIBRARY:库路径,只有当模块提供的是单个库的时候才能使用这形式,多个库可以参考下面的命令。

  • PackageName_Yy_LIBRARY:模块PackageName提供的库Yy的路径,当一个包下有多个库、或者其他包有同名的库时(也就是不同包之间库重名的时)使用。

  • PackageName_INCLUDE_DIR:使用库需要包含的头文件目录,只能在单个库的使用。使用者需要将该路径加入到搜索路径中。

  • PackageName_Yy_INCLUDE_DIR:多个库时候,指定使用库Yy需要包含的头文件。


附录:参考文档

  1. https://cmake.org/cmake/help/v3.0/module/FindPkgConfig.html

相关文章

网友评论

      本文标题:Cmake中find_package命令的搜索模式之模块模式(M

      本文链接:https://www.haomeiwen.com/subject/ohfvjrtx.html