> 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/2.0-chinese/2.3-chinese.md).

# 2.3 处理与编译器相关的源代码

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

这个方法与前面的方法类似，我们将使用CMake来编译依赖于环境的条件源代码：本例将依赖于编译器。为了可移植性，我们尽量避免去编写新代码，但遇到有依赖的情况我们也要去解决，特别是当使用历史代码或处理编译器依赖工具，如[sanitizers](https://github.com/google/sanitizers)。从这一章和前一章的示例中，我们已经掌握了实现这一目标的所有方法。尽管如此，讨论与编译器相关的源代码的处理问题还是很有用的，这样我们将有机会从另一方面了解CMake。

## 准备工作

本示例中，我们将从`C++`中的一个示例开始，稍后我们将演示一个`Fortran`示例，并尝试重构和简化CMake代码。

看一下`hello-world.cpp`源代码:

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

std::string say_hello() {
#ifdef IS_INTEL_CXX_COMPILER
  // only compiled when Intel compiler is selected
  // such compiler will not compile the other branches
  return std::string("Hello Intel compiler!");
#elif IS_GNU_CXX_COMPILER
  // only compiled when GNU compiler is selected
  // such compiler will not compile the other branches
  return std::string("Hello GNU compiler!");
#elif IS_PGI_CXX_COMPILER
  // etc.
  return std::string("Hello PGI compiler!");
#elif IS_XL_CXX_COMPILER
  return std::string("Hello XL compiler!");
#else
  return std::string("Hello unknown compiler - have we met before?");
#endif
}

int main() {
  std::cout << say_hello() << std::endl;
  std::cout << "compiler name is " COMPILER_NAME << std::endl;
  return EXIT_SUCCESS;
}
```

`Fortran`示例(`hello-world.F90`):

```
program hello

  implicit none
#ifdef IS_Intel_FORTRAN_COMPILER
  print *, 'Hello Intel compiler!'
#elif IS_GNU_FORTRAN_COMPILER
  print *, 'Hello GNU compiler!'
#elif IS_PGI_FORTRAN_COMPILER
  print *, 'Hello PGI compiler!'
#elif IS_XL_FORTRAN_COMPILER
  print *, 'Hello XL compiler!'
#else
  print *, 'Hello unknown compiler - have we met before?'
#endif

end program
```

## 具体实施

我们将从`C++`的例子开始，然后再看`Fortran`的例子:

1. `CMakeLists.txt`文件中，定义了CMake最低版本、项目名称和支持的语言:

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

   ```
   add_executable(hello-world hello-world.cpp)
   ```
3. 通过定义以下目标编译定义，让预处理器了解编译器的名称和供应商:

   ```
   target_compile_definitions(hello-world PUBLIC "COMPILER_NAME=\"${CMAKE_CXX_COMPILER_ID}\"")

   if(CMAKE_CXX_COMPILER_ID MATCHES Intel)
     target_compile_definitions(hello-world PUBLIC "IS_INTEL_CXX_COMPILER")
   endif()
   if(CMAKE_CXX_COMPILER_ID MATCHES GNU)
     target_compile_definitions(hello-world PUBLIC "IS_GNU_CXX_COMPILER")
   endif()
   if(CMAKE_CXX_COMPILER_ID MATCHES PGI)
     target_compile_definitions(hello-world PUBLIC "IS_PGI_CXX_COMPILER")
   endif()
   if(CMAKE_CXX_COMPILER_ID MATCHES XL)
     target_compile_definitions(hello-world PUBLIC "IS_XL_CXX_COMPILER")
   endif()
   ```

现在我们已经可以预测结果了:

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

Hello GNU compiler!
```

使用不同的编译器，此示例代码将打印不同的问候语。

前一个示例的`CMakeLists.txt`文件中的`if`语句似乎是重复的，我们不喜欢重复的语句。能更简洁地表达吗？当然可以！为此，让我们再来看看`Fortran`示例。

`Fortran`例子的`CMakeLists.txt`文件中，我们需要做以下工作:

1. 需要使`Fortran`语言:

   ```
   project(recipe-03 LANGUAGES Fortran)
   ```
2. 然后，定义可执行文件及其对应的源文件。在本例中，使用大写`.F90`后缀:

   ```
   add_executable(hello-world hello-world.F90)
   ```
3. 我们通过定义下面的目标编译定义，让预处理器非常清楚地了解编译器:

   ```
   target_compile_definitions(hello-world
     PUBLIC "IS_${CMAKE_Fortran_COMPILER_ID}_FORTRAN_COMPILER"
     )
   ```

其余行为与`C++`示例相同。

## 工作原理

`CMakeLists.txt`会在配置时，进行预处理定义，并传递给预处理器。`Fortran`示例包含非常紧凑的表达式，我们使用`CMAKE_Fortran_COMPILER_ID`变量，通过`target_compile_definition`使用构造预处理器进行预处理定义。为了适应这种情况，我们必须将"Intel"从`IS_INTEL_CXX_COMPILER`更改为`IS_Intel_FORTRAN_COMPILER`。通过使用相应的`CMAKE_C_COMPILER_ID`和`CMAKE_CXX_COMPILER_ID`变量，我们可以在`C`或`C++`中实现相同的效果。但是，请注意，`CMAKE_<LANG>_COMPILER_ID`不能保证为所有编译器或语言都定义。

**NOTE**:*对于应该预处理的`Fortran`代码使用`.F90`后缀，对于不需要预处理的代码使用`.f90`后缀。*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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.3-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.
