# 2.2 处理与平台相关的源代码

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

理想情况下，应该避免依赖于平台的源代码，但是有时我们没有选择，特别是当要求配置和编译不是自己编写的代码时。本示例中，将演示如何使用CMake根据操作系统编译源代码。

## 准备工作

修改`hello-world.cpp`示例代码，将第1章第1节的例子进行修改:

```cpp
#include <cstdlib>
#include <iostream>
#include <string>

std::string say_hello() {
#ifdef IS_WINDOWS
  return std::string("Hello from Windows!");
#elif IS_LINUX
  return std::string("Hello from Linux!");
#elif IS_MACOS
  return std::string("Hello from macOS!");
#else
  return std::string("Hello from an unknown system!");
#endif
}

int main() {
  std::cout << say_hello() << std::endl;
  return EXIT_SUCCESS;
}
```

## 具体实施

完成一个`CMakeLists.txt`实例，使我们能够基于目标操作系统有条件地编译源代码：

1. 首先，设置了CMake最低版本、项目名称和支持的语言:

   ```
   cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
   project(recipe-02 LANGUAGES CXX)
   ```
2. 然后，定义可执行文件及其对应的源文件:

   ```
   add_executable(hello-world hello-world.cpp)
   ```
3. 通过定义以下目标编译定义，让预处理器知道系统名称:

   ```
   if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
     target_compile_definitions(hello-world PUBLIC "IS_LINUX")
   endif()
   if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
     target_compile_definitions(hello-world PUBLIC "IS_MACOS")
   endif()
   if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
     target_compile_definitions(hello-world PUBLIC "IS_WINDOWS")
   endif()
   ```

   继续之前，先检查前面的表达式，并考虑在不同系统上有哪些行为。
4. 现在，准备测试它，并配置项目:

   ```
   $ mkdir -p build
   $ cd build
   $ cmake ..
   $ cmake --build .
   $ ./hello-world

   Hello from Linux!
   ```

Windows系统上，将看到来自Windows的Hello。其他操作系统将产生不同的输出。

## 工作原理

`hello-world.cpp`示例中，有趣的部分是基于预处理器定义`IS_WINDOWS`、`IS_LINUX`或`IS_MACOS`的条件编译:

```
std::string say_hello() {
#ifdef IS_WINDOWS
  return std::string("Hello from Windows!");
#elif IS_LINUX
  return std::string("Hello from Linux!");
#elif IS_MACOS
  return std::string("Hello from macOS!");
#else
  return std::string("Hello from an unknown system!");
#endif
}
```

这些定义在CMakeLists.txt中配置时定义，通过使用`target_compile_definition`在预处理阶段使用。可以不重复`if-endif`语句，以更紧凑的表达式实现，我们将在下一个示例中演示这种重构方式。也可以把`if-endif`语句加入到一个`if-else-else-endif`语句中。这个阶段，可以使用`add_definitions(-DIS_LINUX)`来设置定义(当然，可以根据平台调整定义)，而不是使用`target_compile_definition`。使用`add_definitions`的缺点是，会修改编译整个项目的定义，而`target_compile_definitions`给我们机会，将定义限制于一个特定的目标，以及通过`PRIVATE|PUBLIC|INTERFACE`限定符，限制这些定义可见性。第1章的第8节，对这些限定符有详细的说明:

* **PRIVATE**，编译定义将只应用于给定的目标，而不应用于相关的其他目标。
* **INTERFACE**，对给定目标的编译定义将只应用于使用它的目标。
* **PUBLIC**，编译定义将应用于给定的目标和使用它的所有其他目标。

**NOTE**:*将项目中的源代码与平台相关性最小化，可使移植更加容易。*


---

# 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/2.0-chinese/2.2-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.
