5.8 探究可执行命令

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

目前为止,我们已经展示了如何检查给定的源代码,是否可以由所选的编译器编译,以及如何确保所需的编译器和链接器标志可用。此示例中,将显示如何检查是否可以在当前系统上编译、链接和运行代码。

准备工作

本示例的代码示例是复用第3章第9节的配置,并进行微小的改动。之前,我们展示了如何在您的系统上找到ZeroMQ库并将其链接到一个C程序中。本示例中,在生成实际的C++程序之前,我们将检查一个使用GNU/Linux上的系统UUID库的小型C程序是否能够实际运行。

具体实施

开始构建C++项目之前,我们希望检查GNU/Linux上的UUID系统库是否可以被链接。这可以通过以下一系列步骤来实现:

  1. 声明一个混合的C和C++11程序。这是必要的,因为我们要编译和运行的测试代码片段是使用C语言完成:

    1. cmake_minimum_required(VERSION 3.6 FATAL_ERROR)
    2. project(recipe-08 LANGUAGES CXX C)
    3. set(CMAKE_CXX_STANDARD 11)
    4. set(CMAKE_CXX_EXTENSIONS OFF)
    5. set(CMAKE_CXX_STANDARD_REQUIRED ON)
  2. 我们需要在系统上找到UUID库。这通过使用pkg-config实现的。要求搜索返回一个CMake导入目标使用IMPORTED_TARGET参数:

    1. find_package(PkgConfig REQUIRED QUIET)
    2. pkg_search_module(UUID REQUIRED uuid IMPORTED_TARGET)
    3. if(TARGET PkgConfig::UUID)
    4. message(STATUS "Found libuuid")
    5. endif()
  3. 接下来,需要使用CheckCSourceRuns.cmake模块。C++的是CheckCXXSourceRuns.cmake模块。但到CMake 3.11为止,Fortran语言还没有这样的模块:

    1. include(CheckCSourceRuns)
  4. 我们声明一个_test_uuid变量,其中包含要编译和运行的C代码段:

    1. set(_test_uuid
    2. "
    3. #include <uuid/uuid.h>
    4. int main(int argc, char * argv[]) {
    5. uuid_t uuid;
    6. uuid_generate(uuid);
    7. return 0;
    8. }
    9. ")
  5. 我们声明CMAKE_REQUIRED_LIBRARIES变量后,对check_c_source_runs函数的调用。接下来,调用check_c_source_runs,其中测试代码作为第一个参数,_runs变量作为第二个参数,以保存执行的检查结果。之后,取消CMAKE_REQUIRED_LIBRARIES变量的设置:

    1. set(CMAKE_REQUIRED_LIBRARIES PkgConfig::UUID)
    2. check_c_source_runs("${_test_uuid}" _runs)
    3. unset(CMAKE_REQUIRED_LIBRARIES)
  6. 如果检查没有成功,要么是代码段没有编译,要么是没有运行,我们会用致命的错误停止配置:

    1. if(NOT _runs)
    2. message(FATAL_ERROR "Cannot run a simple C executable using libuuid!")
    3. endif()
  7. 若成功,我们继续添加C++可执行文件作为目标,并链接到UUID:

    1. add_executable(use-uuid use-uuid.cpp)
    2. target_link_libraries(use-uuid
    3. PUBLIC
    4. PkgConfig::UUID
    5. )

工作原理

check_<lang>_source_runs用于C和C++的函数,与check_<lang>_source_compile相同,但在实际运行生成的可执行文件的地方需要添加一个步骤。对于check_<lang>_source_compiles, check_<lang>_source_runs的执行可以通过以下变量来进行:

  • CMAKE_REQUIRED_FLAGS:设置编译器标志。
  • CMAKE_REQUIRED_DEFINITIONS:设置预编译宏。
  • CMAKE_REQUIRED_INCLUDES:设置包含目录列表。
  • CMAKE_REQUIRED_LIBRARIES:设置可执行目标需要连接的库列表。

由于使用pkg_search_module生成的为导入目标,所以只需要将CMAKE_REQUIRES_LIBRARIES设置为PkgConfig::UUID,就可以正确设置包含目录。

正如check_<lang>_source_compilestry_compile的包装器,check_<lang>_source_runs是CMake中另一个功能更强大的命令的包装器:try_run。因此,可以编写一个CheckFortranSourceRuns.cmake模块,通过适当包装try_run, 提供与C和C++模块相同的功能。

NOTE:pkg_search_module只能定义导入目标(CMake 3.6),但目前的示例可以使工作,3.6之前版本的CMake可以通过手动设置所需的包括目录和库check_c_source_runs如下:set(CMAKE_REQUIRED_INCLUDES $ {UUID_INCLUDE_DIRS})set(CMAKE_REQUIRED_LIBRARIES $ {UUID_LIBRARIES})