> For the complete documentation index, see [llms.txt](https://chenxiaowei.gitbook.io/cmake-cookbook/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://chenxiaowei.gitbook.io/cmake-cookbook/5.0-chinese/5.8-chinese.md).

# 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语言完成:

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

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

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

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

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

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

   ```
   add_executable(use-uuid use-uuid.cpp)
   target_link_libraries(use-uuid
     PUBLIC
         PkgConfig::UUID
     )
   ```

## 工作原理

`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_compiles`是`try_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})`。*
