> For the complete documentation index, see [llms.txt](https://chenxiaowei.gitbook.io/professional-cmake/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/professional-cmake/part1-chinese/3-chinese.md).

# 第3章：一个简单的项目

所有的CMake项目都从源目录下的CMakeLists.txt文件开始，可以把这个文件看作CMake项目文件，其定义构建过程中所有的内容，从源码和构建目标，到测试、打包和自定义任务。可以简单到只有几行，也可以相当复杂，从其他目录中拉入更多的文件。CMakeLists.txt是文本文件，可以直接编辑。

与源码的相比，CMake有自己的语言，有许多程序员熟悉的东西，比如变量、函数、宏、条件、循环、注释等等。这些概念和特性将在后面介绍，现在我们的目标只是将简单的构建作为起点。下面是一个简单的CMakeLists.txt文件，会生成一个可执行文件。

```
cmake_minimum_required(VERSION 3.2)
project(MyApp)
add_executable(myExe main.cpp)
```

上面示例中的每一行都会执行一个CMake命令，CMake命令类似于函数调用，但不直接返回值(除了支持参数时，后面的章节将展示如何将值返回)。参数之间可以用空格分隔，也可以跨多行分隔：

```
add_executable(myExe
 main.cpp
 src1.cpp
 src2.cpp
)
```

命令名称也是不区分大小写的，所以一下形式的效果都相同:

```
add_executable(myExe main.cpp)
ADD_EXECUTABLE(myExe main.cpp)
Add_Executable(myExe main.cpp)
```

每个人书写的方式可能不相同，但更常见的是命令名都使用小写字母(对于内置命令，CMake文档也遵循这种惯例)。

## 3.1. 管理CMake版本

CMake也在不断更新和扩展，以增加对新工具、平台和特性的支持。CMake的开发人员会确保新版本的向后兼容性，所以当用户更新CMake时，项目应该可以如同以前一样构建。有时，需要更改CMake的行为，或在新版本中引入更为严格的检查和警告。CMake没有要求所有的项目立即处理这个问题，而是提供了策略机制，允许项目使用“和CMake版本X.Y.Z的行为一样”。这使得CMake可以修复bug并引入新特性，并且仍然保持以往版本的行为。

项目指定CMake版本行为的详细信息是使用`cmake_minimum_required()`命令。这应该是CMakeLists.txt文件的第一行，这样项目的需求就会放在在其他事情之前。这个命令做了两件事:

* 指定了项目所需的CMake的最低版本。如果CMakeLists.txt文件使用的CMake版本比指定的版本低，将立即停止构建，并出现错误报告。
* 强制设置将CMake行为匹配到对应版本。

如果CMakeLists.txt没有在任何其他命令之前调用`cmake_minimum_required()`， CMake将发出警告。它需要知道如何为所有后续处理设置策略。对于大多数项目，就像它的名字那样，使用`cmake_minimum_required()`指定所需的最低CMake版本就足够了。它还暗示CMake应该与特定版本的行为相同。第12章中会更详细地讨论了策略设置，并解释如何根据需要进行定制。

`cmake_minimum_required()`命令很简单:

```
cmake_minimum_required(VERSION major.minor[.patch[.tweak]])
```

`VERSION`关键字必须出现，提供的版本详细信息必须有`major.minor`。大多数项目中，`patch`和`tweak`没有必要，因为新特性通常只在`minor`版本更新中出现(这是3.0版本后的官方行为)。只有当修复某个错误时，项目才应该指定`patch`部分。3.x的CMake使用了`tweak`，但项目不需要进行指定。

开发人员应该仔细考虑项目需要的最低CMake版本。版本3.2可能是所有新项目中最老的一个，因为它为现代CMake技术提供了相当完整的特性集。2.8.12的特性覆盖率降低了，缺少了许多有用的特性，但对较老的项目还是可用的。在此之前的版本缺乏实质性的特性，许多现代CMake技术还用不了。如果工作在移动平台，如iOS上，可能需要最近版本的CMake来支持最新的操作系统版本等等。

作为经验法则，选择最新CMake版本不会对构建项目产生重大的问题。最大的困难通常是需要支持旧平台的项目，其中系统提供的CMake版本可能相当旧。对于这种情况，开发人员应该考虑安装一个新版本。另一方面，如果项目本身是其他项目的依赖项，选择最新的CMake可能会成为障碍。这种情况下，可以使用一些新CMake版本的功能。使用老版本的主要缺点是，可能会弃用更多的警告，因为新版CMake会警告旧的行为，以鼓励项目更新。

## 3.2. project()

每个CMake项目都应该包含一个`project()`命令，它应该在`cmake_minimum_required()`之后出现。该命令常见的选项如下所示:

```
project(projectName
 [VERSION major[.minor[.patch[.tweak]]]]
 [LANGUAGES languageName ...]
)
```

项目名称是必需的，只能是字母、数字、下划线(\_)和连字符(-)，字母和下划线比较常用。由于不允许使用空格，所以项目名称不必用引号括起来。这个名字用于项目的生成器(如Xcode和Visual Studio)，也可以运用于各种项目的其他部分，比如默认打包名称和文档元数据，项目名称等等。`projectName`是`project()`命令惟一强制性参数。

`VERSION`只在CMake 3.0之后的版本支持。与`projectName`一样，CMake使用版本细节来填充变量并作为默认元数据，除此之外版本信息没有任何意义。尽管如此，定义项目版本是一个好习惯，以便项目的其他部分可以引用。第19章将深入讨论这个问题，并解释如何在CMakeLists.txt文件中引用版本信息。

`LANGUAGES`参数定义了应该为项目启用的编程语言，支持的语言包括`C`、`CXX`、`Fortran`、`ASM`、`Java`等。如果指定了多种语言，请用空格分隔。某些情况下，项目可能不使用任何语言，这可以使用`NONE`。如果没有语言选项，CMake将默认为`C`和`CXX`。3.0之前的CMake版本不支持`LANGUAGES`关键字，但使用旧形式的命令仍然可以指定`LANGUAGES`，操作如下:

```
project(myProj C CXX)
```

新项目应将CMake的最低版本至少指定为3.0，并使用关键字`LANGUAGES`。

`project()`命令不仅仅是填充变量。它的重要职责是检查每种语言的编译器，并确保它们能够正确地编译和链接，这样编译器和链接器设置的问题会在很早就发现。一旦这些检查通过，CMake就会设置变量和属性来控制所支持语言的构建。如果CMakeLists.txt文件没有调用`project()`或者调用得不够早，CMake会在内部隐式调用默认语言`C`和`CXX`，以确保依赖于编译器和链接器的其他命令正确设置。后面的章节详细介绍了如何建立工具链，并演示了如何查询和修改编译器标记、编译器位置等。

当CMake执行的编译器和链接器检查成功时，它们的结果会进行缓存，这样就不必在后续的CMake运行中重复。这些缓存信息存储在CMakeCache.txt文件的构建目录中。关于检查的额外细节可以在构建区的子目录中找到，开发人员只需要在使用新的或不常见的编译器时，或在设置用于交叉编译的工具链文件时，才需要查看。

## 3.3. 构建可执行文件

`add_executable()`命令告诉CMake为一组源文件创建一个可执行文件。此命令的形式为:

```
add_executable(targetName source1 [source2 ...])
```

这将创建一个可执行文件，可以在CMake项目中作为`targetName`引用。这个名称可以包含字母、数字、下划线和连字符。构建项目时，在构建目录中创建一个与平台相关名称的可执行文件。

```
add_executable(myApp main.cpp)
```

默认情况下，可执行文件的名称在Windows上是myApp.exe，在基于Unix的平台(如macOS、Linux等)上是myApp。可执行文件的名称可以用目标属性来指定。通过使用不同的名称多次调用`add_executable()`，可以在CMakeLists.txt文件中定义多个可执行文件。如果在多个`add_executable()`命令中使用相同的名称，CMake将失败并显示错误信息。

## 3.4. 注释

结束本章之前，演示一下如何向CMakeLists.txt文件添加注释。在本书中会大量使用注释，并且鼓励开发人员养成注释项目的习惯，就像注释普通源代码一样。CMake遵循与Unix Shell脚本类似的注释约定，任何以`#`字符开头的行都视为注释，除了在带引号的字符串中，CMakeLists.txt文件中一行中`#`之后的任何内容都视为注释。下面展示了一些注释示例，并与本章介绍的内容结合在一起:

```
cmake_minimum_required(VERSION 3.2)

# We don't use the C++ compiler, so don't let project()
# test for it in case the platform doesn't have one
project(MyApp VERSION 4.7.2 LANGUAGES C)

# Primary tool for this project
add_executable(mainTool
 main.c
 debug.c # Optimized away for release builds
)

# Helpful diagnostic tool for development and testing
add_executable(testTool testTool.c)
```

## 3.5. 总结

确保CMake项目都有`cmake_minimum_required()`命令作为其CMakeLists.txt文件的第一行。当决定最低版本号时，版本越新，项目能使用的特性就越多。该项目可能会更好地适应新平台或操作系统。相反，如果项目打算作为操作系统本身的一部分构建和发布(常见的Linux)，那么最低CMake版本可能由该发行版提供的CMake版本决定。

如果需要CMake 3.0或更高版本，最好尽早考虑项目版本号，并尽快将版本号合并到`project()`命令中。要克服现有过程的惰性，在项目生命周期的后期改变版本号非常困难。在决定版本控制策略时，请考虑诸如语义版本控制之类的方式。
