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
文件:
-
声明一个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)
-
使用CMake附带的find-module,查找
pkg-config
。这里在find_package
中传递了QUIET
参数。只有在没有找到pkg-config
时,CMake才会报错:find_package(PkgConfig REQUIRED QUIET)
-
找到
pkg-config
时,我们将使用pkg_search_module
函数,以搜索任何附带包配置.pc
文件的库或程序。该示例中,我们查找ZeroMQ库:pkg_search_module( ZeroMQ REQUIRED libzeromq libzmq lib0mq IMPORTED_TARGET )
-
如果找到ZeroMQ库,则打印状态消息:
if(TARGET PkgConfig::ZeroMQ) message(STATUS "Found ZeroMQ") endif()
-
然后,添加两个可执行目标,并链接到ZeroMQ。这将自动设置包括目录和链接库:
add_executable(hwserver hwserver.c) target_link_libraries(hwserver PkgConfig::ZeroMQ) add_executable(hwclient hwclient.c) target_link_libraries(hwclient PkgConfig::ZeroMQ)
-
现在,我们可以配置和构建示例:
$ mkdir -p build $ cd build $ cmake .. $ cmake --build .
-
在终端中,启动服务器,启动时会输出类似于本例的消息:
Current 0MQ version is 4.2.2
-
然后,在另一个终端启动客户端,它将打印如下内容:
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
(用于链接库),供后续使用。