3.9 检测外部库:Ⅰ. 使用pkg-config

NOTE:此示例代码可以在 https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-03/recipe-09 中找到,包含一个C的示例。该示例在CMake 3.6版(或更高版本)中是有效的,并且已经在GNU/Linux、macOS和Windows上进行过测试。https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-03/recipe-09 中也有一个适用于CMake 3.5的示例。

目前为止,我们已经讨论了两种检测外部依赖关系的方法:

  • 使用CMake自带的find-module,但并不是所有的包在CMake的find模块都找得到。
  • 使用<package>Config.cmake, <package>ConfigVersion.cmake<package>Targets.cmake,这些文件由软件包供应商提供,并与软件包一起安装在标准位置的cmake文件夹下。

如果某个依赖项既不提供查找模块,也不提供供应商打包的CMake文件,该怎么办?在这种情况下,我们只有两个选择:

  • 依赖pkg-config程序,来找到系统上的包。这依赖于包供应商在.pc配置文件中,其中有关于发行包的元数据。
  • 为依赖项编写自己的find-package模块。

本示例中,将展示如何利用CMake中的pkg-config来定位ZeroMQ消息库。下一个示例中,将编写一个find模块,展示如何为ZeroMQ编写属于自己find模块。

准备工作

我们构建的代码来自ZeroMQ手册 http://zguide.zeromq.org/page:all 的示例。由两个源文件hwserver.chwclient.c组成,这两个源文件将构建为两个独立的可执行文件。执行时,它们将打印“Hello, World”。

具体实施

这是一个C项目,我们将使用C99标准,逐步构建CMakeLists.txt文件:

  1. 声明一个C项目,并要求符合C99标准:

    1. cmake_minimum_required(VERSION 3.6 FATAL_ERROR)
    2. project(recipe-09 LANGUAGES C)
    3. set(CMAKE_C_STANDARD 99)
    4. set(CMAKE_C_EXTENSIONS OFF)
    5. set(CMAKE_C_STANDARD_REQUIRED ON)
  2. 使用CMake附带的find-module,查找pkg-config。这里在find_package中传递了QUIET参数。只有在没有找到pkg-config时,CMake才会报错:

    1. find_package(PkgConfig REQUIRED QUIET)
  3. 找到pkg-config时,我们将使用pkg_search_module函数,以搜索任何附带包配置.pc文件的库或程序。该示例中,我们查找ZeroMQ库:

    1. pkg_search_module(
    2. ZeroMQ
    3. REQUIRED
    4. libzeromq libzmq lib0mq
    5. IMPORTED_TARGET
    6. )
  4. 如果找到ZeroMQ库,则打印状态消息:

    1. if(TARGET PkgConfig::ZeroMQ)
    2. message(STATUS "Found ZeroMQ")
    3. endif()
  5. 然后,添加两个可执行目标,并链接到ZeroMQ。这将自动设置包括目录和链接库:

    1. add_executable(hwserver hwserver.c)
    2. target_link_libraries(hwserver PkgConfig::ZeroMQ)
    3. add_executable(hwclient hwclient.c)
    4. target_link_libraries(hwclient PkgConfig::ZeroMQ)
  6. 现在,我们可以配置和构建示例:

    1. $ mkdir -p build
    2. $ cd build
    3. $ cmake ..
    4. $ cmake --build .
  7. 在终端中,启动服务器,启动时会输出类似于本例的消息:

    1. Current 0MQ version is 4.2.2
  8. 然后,在另一个终端启动客户端,它将打印如下内容:

    1. Connecting to hello world server
    2. Sending Hello 0
    3. Received World 0
    4. Sending Hello 1
    5. Received World 1
    6. Sending Hello 2
    7. ...

工作

当找到pkg-config时, CMake需要提供两个函数,来封装这个程序提供的功能:

  • pkg_check_modules,查找传递列表中的所有模块(库和/或程序)
  • pkg_search_module,要在传递的列表中找到第一个工作模块

find_package一样,这些函数接受REQUIREDQUIET参数。更详细地说,我们对pkg_search_module的调用如下:

  1. pkg_search_module(
  2. ZeroMQ
  3. REQUIRED
  4. libzeromq libzmq lib0mq
  5. IMPORTED_TARGET
  6. )

这里,第一个参数是前缀,它将用于命名存储搜索ZeroMQ库结果的目标:PkgConfig::ZeroMQ。注意,我们需要为系统上的库名传递不同的选项:libzeromqlibzmqlib0mq。这是因为不同的操作系统和包管理器,可为同一个包选择不同的名称。

NOTE:pkg_check_modulespkg_search_module函数添加了IMPORTED_TARGET选项,并在CMake 3.6中定义导入目标的功能。3.6之前的版本,只定义了变量ZeroMQ_INCLUDE_DIRS(用于include目录)和ZeroMQ_LIBRARIES(用于链接库),供后续使用。