第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。大多数项目中,patchtweak没有必要,因为新特性通常只在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),也可以运用于各种项目的其他部分,比如默认打包名称和文档元数据,项目名称等等。projectNameproject()命令惟一强制性参数。

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

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

project(myProj C CXX)

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

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

当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()命令中。要克服现有过程的惰性,在项目生命周期的后期改变版本号非常困难。在决定版本控制策略时,请考虑诸如语义版本控制之类的方式。

Last updated