# 15.8 项目转换为CMake的常见问题

我们总结一下，在这一章中所所学到的知识。

## 代码修改总结

在本章中，讨论了如何将项目移植到CMake进行构建。我们以Vim项目为例，添加了以下文件:

```
.
├── CMakeLists.txt
└── src
    ├── autogenerate.cmake
    ├── CMakeLists.txt
    ├── config.h.cmake.in
    ├── libvterm
    │    └── CMakeLists.txt
    ├── pathdef.c.in
    └── testdir
        ├── CMakeLists.txt
        └── test.cmake
```

可以在线查看修改： <https://github.com/dev-cafe/vim/compare/b476cb7...cmake-support>

为了简单起见，我们省略了许多选项和调整，并将重点放在最重要的步骤上。

## 常见问题

在结束讨论之前，我们想指出一些迁移到CMake时常见的问题。

* **全局变量代码异味**：这点适用于任何编程语言，CMake也不例外。跨CMake文件的变量，特别是从子到父`CMakeLists.txt`文件的“向上”传递的变量，这是明显的“异味代码”。通常，会有一种更好的方法来传输依赖关系。理想情况下，依赖项应该通过目标导入。与其将库列表组装成一个变量并在文件之间携带该变量，不如逐个链接到定义库的地方。不是将源文件组装成变量，而是使用`target_sources`添加源文件。当链接到库时，在可用时使用导入的目标，而不是变量。
* **最小化顺序的影响**：CMake不是一种声明性语言，但是也不应该使用命令式范式进行处理。执行严格命令的代码往往是脆弱的，这也与变量有关(见上一段)。一些语句和模块的顺序是必要的，但是为了实现健壮的CMake框架，我们应该避免不必要的顺序强制。应该多使用`target_sources`、`target_compile_definition`、`target_include_directory`和`target_link_libraries`。避免使用全局范围语句，如`add_definition`、`include_directory`和`link_libraries`，从而避免定义全局编译标志。如果可能，为每个目标定义编译标志。
* **不在build目录之外生成文件**：强烈建议不要将生成的文件放在构建目录之外。原因是生成的文件通常依赖于所选择的选项、编译器或构建类型。如果写入原目录树，我们就放弃了用同一套源码维护多个构建的可能性，并且会使构建步骤的重现复杂化。
* **尽可能使用函数，而不是宏**：它们的作用范围不同，功能范围也有限定。所有变量修改都需要显式标记，这也向读者展示了重新定义的变量。如果可以最好使用函数，必要时再使用宏。
* **避免shell命令**：Shell可能不能移植到其他平台(如Windows)。可以使用CMake中的命令或函数。如果没有可用的CMake等效函数，请考虑调用Python脚本。
* **Fortran中，注意后缀**：需要预处理的Fortran源文件是大写的`.F90`后缀。无预处理的源文件应该以`.f90`为后缀。
* **避免显式路径**：这条建议在定义目标和引用文件时都适用。当引用当前路径时，可使用`CMAKE_CURRENT_LIST_DIR`。这样做的好处是，当移动或重命名一个目录时，构建不会出问题。
* **不应该在函数调用中进行模块包含**：将CMake代码模块化是一个很好的策略，但是包含模块不应该执行CMake代码。相反，将CMake代码封装到函数和宏中，并在包含模块之后显式地调用这些函数和宏。当意外地多次包含模块时，这条建议可以防止意外的副作用，并使执行CMake代码模块的操作更易读。


---

# 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/15.0-chinese/15.8-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.
