# 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.c`和`hwclient.c`组成，这两个源文件将构建为两个独立的可执行文件。执行时，它们将打印“Hello, World”。

## 具体实施

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

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

   ```
   cmake_minimum_required(VERSION 3.6 FATAL_ERROR)

   project(recipe-09 LANGUAGES C)

   set(CMAKE_C_STANDARD 99)
   set(CMAKE_C_EXTENSIONS OFF)
   set(CMAKE_C_STANDARD_REQUIRED ON)
   ```
2. 使用CMake附带的find-module，查找`pkg-config`。这里在`find_package`中传递了`QUIET`参数。只有在没有找到`pkg-config`时，CMake才会报错:

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

   ```
   pkg_search_module(
     ZeroMQ
     REQUIRED
         libzeromq libzmq lib0mq
     IMPORTED_TARGET
     )
   ```
4. 如果找到ZeroMQ库，则打印状态消息:

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

   ```
   add_executable(hwserver hwserver.c)
   target_link_libraries(hwserver PkgConfig::ZeroMQ)
   add_executable(hwclient hwclient.c)
   target_link_libraries(hwclient PkgConfig::ZeroMQ)
   ```
6. 现在，我们可以配置和构建示例:

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

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

   ```
   Connecting to hello world server…
   Sending Hello 0…
   Received World 0
   Sending Hello 1…
   Received World 1
   Sending Hello 2…
   ...
   ```

## 工作

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

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

与`find_package`一样，这些函数接受`REQUIRED`和`QUIET`参数。更详细地说，我们对`pkg_search_module`的调用如下:

```
pkg_search_module(
  ZeroMQ
  REQUIRED
      libzeromq libzmq lib0mq
  IMPORTED_TARGET
  )
```

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

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://chenxiaowei.gitbook.io/cmake-cookbook/3.0-chinese/3.9-chinese.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
