CMake 零基础:编写你的第一个 CMakeLists.txt
💡 博主简介:专注分享高性能开发技术知识,包括C/C++、Linux、网络协议、设计模式、中间件、云原生、数据库、分布式架构等。
👉
🎖️ CSDN 专家博主,阿里云 博客专家、华为云 云享专家、腾讯云 创作之星
👉
🔔 专栏介绍:化繁为简,用清晰易懂的语言和丰富的示例,手把手教你掌握 CMake 的核心概念和实用技巧。无论是 C++ 新手还是资深开发者,都能轻松上手,构建跨平台、可维护、高效的 C++ 工程。
👉
🔔 专栏地址:CMake 轻松学
👉
🔔 博客主页:https://blog.csdn.net/Long_xu
一、背景介绍
还在手动编译你的 C++ 项目吗?告别繁琐的命令行,让 CMake 来拯救你! 作为一名 C++ 开发者,你一定遇到过这些问题:
- 项目越来越大,编译命令越来越长,每次构建都让人头疼。
- 项目需要在不同的操作系统 (Windows、Linux、macOS) 上编译,构建脚本却不兼容。
- 项目中使用了第三方库,需要手动管理依赖关系,稍有不慎就出错。
如果你正在经历这些痛苦,那么 CMake 就是你所需要的利器!CMake 是一个跨平台的构建系统生成器。 简单来说,它并不会直接编译你的代码,而是根据你提供的 CMakeLists.txt 文件,生成适合你当前平台的构建文件(例如:Makefile、Visual Studio 解决方案)。
那么,CMakeLists.txt 又是什么呢?
CMakeLists.txt 是一个文本文件,它描述了你的 C++ 项目的构建规则。 你可以在 CMakeLists.txt 中指定:
- 项目名称
- 源代码文件
- 头文件搜索路径
- 需要链接的库
- 编译选项
- 等等…
CMake 的核心作用就是:
- 读取 CMakeLists.txt 文件。
- 根据 CMakeLists.txt 中描述的规则,生成构建系统所需的输入文件。 例如:生成 Makefile 或 在 Windows 上的
.sln
文件。 - 然后,可以使用生成的构建文件来编译你的项目。
使用 CMake 的好处:
- 跨平台: 一份 CMakeLists.txt 文件,可以在不同的操作系统上生成对应的构建文件,无需修改。
- 自动化: 自动生成构建文件,简化编译流程。
- 依赖管理: 方便地添加和管理第三方库的依赖关系。
- 模块化: 支持将项目拆分成多个模块,方便管理和维护。
- 可扩展: 可以自定义构建规则,满足各种需求。
CMake 能够帮助你更好地管理 C++ 项目的构建过程,提高开发效率,并保证项目的可移植性。 在接下来的内容中,本文将一起编写你的第一个 CMakeLists.txt 文件,让你快速入门 CMake!
二、准备环境
在开始编写 CMakeLists.txt 之前,需要确保 CMake 已经正确安装,并且准备好开发环境。
如果还没有安装 CMake 工具,参考前面的一篇文章,有详细的安装教程,这里不再赘述。验证 CMake 是否安装:
cmake --version
如果成功安装,将会显示 CMake 的版本信息,例如:
cmake version 3.25.1
CMake suite maintained and supported by Kitware (kitware.com/cmake).
如果提示 “cmake: command not found” 或类似错误,说明 CMake 没有安装 或者 没有添加到系统环境变量中。
除了 CMake 之外,最好也准备好 C++ 编译器和 IDE 或编辑器。
C++ 编译器:
- Windows: 建议使用 Visual Studio 自带的 C++ 编译器。
- Linux: 建议使用 GCC (GNU Compiler Collection) 或 Clang。
- macOS: 建议使用 Clang (Xcode 自带)。
IDE 或编辑器:
- VS Code: 免费、轻量级、插件丰富,强烈推荐。安装 C++ 扩展和 CMake Tools 扩展可以获得更好的开发体验。
- CLion: JetBrains 出品的 C++ IDE,功能强大,但需要付费。对 CMake 支持非常好。
- Qt Creator: 免费、跨平台、适用于 Qt 项目开发,对 CMake 支持良好。
- Visual Studio: Windows 平台上常用的 IDE,功能强大,但比较重量级。
完成以上准备工作后,我们就可以开始编写第一个 CMakeLists.txt 文件了!
三、认识 CMakeLists.txt:项目的“蓝图”
3.1、什么是 CMakeLists.txt?
在深入学习 CMake 之前,我们首先要理解 CMakeLists.txt 是什么,以及它在整个构建过程中扮演的角色。
CMakeLists.txt 的定义:CMakeLists.txt 是一个文本文件,用于描述 C++ 项目的构建规则。 它可以被看作是项目的“蓝图”,告诉 CMake 如何编译、链接代码,以及如何生成最终的可执行文件或库。
CMakeLists.txt 文件通常位于项目的根目录,也可以位于子目录中,用于描述子目录下的构建规则。
CMake 的核心工作就是读取 CMakeLists.txt 文件,并根据其中的描述,生成特定平台的构建系统。 换句话说,CMakeLists.txt 是 CMake 的输入,而构建系统(例如 Makefile 或 Visual Studio 解决方案)是 CMake 的输出。
CMakeLists.txt 文件中可以包含以下信息:
- 项目名称: 使用
project()
命令定义项目的名称。 - 源代码文件: 使用
add_executable()
或add_library()
命令指定需要编译的源代码文件。 - 头文件搜索路径: 使用
include_directories()
命令指定头文件的搜索路径。 - 需要链接的库: 使用
target_link_libraries()
命令指定需要链接的库。 - 编译选项: 使用
add_compile_options()
命令添加编译选项。 - 其他构建规则: 例如,自定义命令、安装规则等。
可以把 CMakeLists.txt 想象成一份详细的“菜谱”,CMake 则是一位厨师。菜谱(CMakeLists.txt)告诉厨师(CMake)需要哪些食材(源代码文件、库),以及如何烹饪(编译、链接)这些食材,最终做出一道美味佳肴(可执行文件或库)。
3.2、CMakeLists.txt 的基本语法
要编写有效的 CMakeLists.txt 文件,首先需要了解其基本的语法规则。CMakeLists.txt 使用一种简单的、基于命令的语法。
-
CMakeLists.txt 的核心是命令。命令是 CMake 提供的预定义函数,用于执行特定的构建任务。命令的格式:
command_name(argument1 argument2 ...)
。而且命令名称大小写不敏感,但是,为了代码的可读性,建议使用小写字母。 -
可以在 CMakeLists.txt 中使用变量存储字符串值。使用
set()
命令定义变量,使用${}
来引用变量的值。CMake 中的变量都是字符串类型。当然,CMake 提供了一些内置变量,用于获取项目信息、系统信息等。例如:CMAKE_SOURCE_DIR
: 项目的根目录。CMAKE_BINARY_DIR
: 构建目录。CMAKE_CXX_COMPILER
: C++ 编译器的路径。
-
注释用于在 CMakeLists.txt 中添加说明文字,不会被 CMake 执行。使用
#
符号开始的行都是注释。 -
语法规则:命令的参数使用空格分隔。换行 可以使用反斜杠
\
来进行换行。使用双引号"
来定义包含空格的字符串。使用分号;
来定义列表。
简单示例:
# 设置 CMake 的最低版本要求
cmake_minimum_required(VERSION 3.10)
# 定义项目名称
project(MyFirstCMakeProject)
# 定义源代码文件
set(SOURCE_FILES main.cpp util.cpp)
# 添加可执行文件
add_executable(MyApp ${SOURCE_FILES})
# 打印消息
message(STATUS "Building project ${PROJECT_NAME}")
四、编写你的第一个 CMakeLists.txt
4.1、准备工作
在编写CMakeLists.txt 之前,先了解一些常用的基本命令(前面虽然有提到,但是没有详细解释,很多人都不知道什么含义),以便更好的写出 CMakeLists.txt 内容:
命令 | 功能描述 | 参数 | 示例 |
---|---|---|---|
cmake_minimum_required | 指定 CMake 的最低版本要求。 | VERSION version : CMake 的最低版本号(字符串)。 | cmake_minimum_required(VERSION 3.15) |
project | 定义项目名称、版本、描述等基本信息。 | projectName : 项目名称(字符串,必需)。 VERSION version : 项目版本(字符串,可选)。 DESCRIPTION description : 项目描述(字符串,可选)。 HOMEPAGE_URL url : 项目主页 URL(字符串,可选)。 LANGUAGES languages ... : 支持的编程语言(可选)。 | project(MyProject VERSION 1.0 DESCRIPTION "My project" LANGUAGES CXX) |
add_executable | 定义一个可执行文件及其源代码。 | executableName : 可执行文件的名称(字符串,不含后缀)。 source1 source2 ... : 源代码文件列表。 | add_executable(MyApp main.cpp util.cpp) |
message | 在 CMake 的输出中打印消息,用于调试和提供构建信息。 | [STATUS、WARNING、AUTHOR_WARNING、FATAL_ERROR、SEND_ERROR] : 消息类型。 "message text" ... : 要打印的消息内容(字符串)。 | message(STATUS "Building project ${PROJECT_NAME}") message(WARNING "Potential issue") |
这个先介绍常用的几个,方便初学者学习,后面再扩展其他更多的命令。
准备一份代码,文件名为hello.cpp
,代码内容如下:
#include <iostream>
#include <string>
std::string say_hello()
{
return std::string("Hello, CMake world!");
}
int main()
{
std::cout << say_hello() << std::endl;
return 0;
}
本示例由单个源文件组成,用于生成可执行文件。
4.2、具体步骤
要让CMake理解你的C++项目,光有代码是不够的,还需要一份详细的“配置清单”来指导它如何编译和链接。这份“配置清单”就是用CMake自己的语言编写的,所有指令和语法细节都可以在CMake官方文档(https://cmake.org/cmake/help/latest/)中查阅。这份“配置清单”必须存储在一个名为CMakeLists.txt
的文件里,而且文件名的大小写必须完全匹配,否则CMake就无法识别。
切记:文件的名称区分大小写,必须命名为CMakeLists.txt
,CMake才能够解析。
步骤如下:
-
用编辑器打开一个文本文件,将这个文件命名为
CMakeLists.txt
。 -
在第一行中指定使用 CMake 时的最低版本要求。如果当前使用的 CMake 版本低于 该版本,则会立即触发一个致命错误,从而终止配置过程。
cmake_minimum_required(VERSION 3.5)
-
在第二行中,使用
project
命令声明项目名称,并指定项目使用的编程语言(例如CXX
代表 C++)。project(myAPP LANGUAGES CXX)
-
使用
add_executable
来指示 CMake 创建一个新的构建目标,例如名为hello-world
的可执行文件。该可执行文件将通过对源文件进行编译和链接生成。CMake 会为编译器应用默认设置,并自动选择适合当前平台的生成工具。add_executable(hello-world hello.cpp)
-
将该文件与源文件
hello.cpp
放在相同的目录中。切记,它只能被命名为CMakeLists.txt
。 -
现在,可以通过创建
build
目录,在build
目录下来配置项目:$ mkdir -p build $ cd build $ cmake .. -- The CXX compiler identification is GNU 11.4.0 -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: /usr/bin/c++ - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Building project recipe-hello -- Configuring done -- Generating done -- Build files have been written to: /home/fly/workspace/cmakeProj/firstCMake/build $ ls CMakeCache.txt CMakeFiles Makefile cmake_install.cmake
-
一切顺利,项目的配置已经在
build
目录中生成。可以编译可执行文件:$ cmake --build . [ 50%] Building CXX object CMakeFiles/hello-world.dir/hello.cpp.o [100%] Linking CXX executable hello-world [100%] Built target hello-world $ ls CMakeCache.txt CMakeFiles Makefile cmake_install.cmake hello-world $ ./hello-world Hello, CMake world!
4.3、工作原理
一个简单的CMakeLists.txt
内容如下:
cmake_minimum_required(VERSION 3.5)
project(recipe-hello VERSION 1.0 LANGUAGES CXX)
add_executable(hello-world hello.cpp)
message(STATUS "Building project ${PROJECT_NAME}")
注意:CMake语言不区分大小写,但是参数区分大小写。
另外,在 CMake 中,虽然 C++ 是默认的编程语言,但建议在 project
命令中通过 LANGUAGES
选项明确指定项目所用的语言。
为了配置项目并生成构建器,要在命令行界面 (CLI) 下运行 CMake。CMake CLI 提供了各种选项,运行 cmake -help
命令将输出完整的帮助信息,其中列出了所有可用选项。
$ cmake --help
Usage
cmake [options] <path-to-source>
cmake [options] <path-to-existing-build>
cmake [options] -S <path-to-source> -B <path-to-build>
Specify a source directory to (re-)generate a build system for it in the
current working directory. Specify an existing build directory to
re-generate its build system.
Options
-S <path-to-source> = Explicitly specify a source directory.
-B <path-to-build> = Explicitly specify a build directory.
-C <initial-cache> = Pre-load a script to populate the cache.
-D <var>[:<type>]=<value> = Create or update a cmake cache entry.
-U <globbing_expr> = Remove matching entries from CMake cache.
-G <generator-name> = Specify a build system generator.
-T <toolset-name> = Specify toolset name if supported by
generator.
-A <platform-name> = Specify platform name if supported by
generator.
--toolchain <file> = Specify toolchain file
[CMAKE_TOOLCHAIN_FILE].
--install-prefix <directory> = Specify install directory
[CMAKE_INSTALL_PREFIX].
-Wdev = Enable developer warnings.
-Wno-dev = Suppress developer warnings.
-Werror=dev = Make developer warnings errors.
-Wno-error=dev = Make developer warnings not errors.
-Wdeprecated = Enable deprecation warnings.
-Wno-deprecated = Suppress deprecation warnings.
-Werror=deprecated = Make deprecated macro and function warnings
errors.
-Wno-error=deprecated = Make deprecated macro and function warnings
not errors.
--preset <preset>,--preset=<preset>
= Specify a configure preset.
--list-presets = List available presets.
-E = CMake command mode.
-L[A][H] = List non-advanced cached variables.
--build <dir> = Build a CMake-generated project binary tree.
--install <dir> = Install a CMake-generated project binary
tree.
--open <dir> = Open generated project in the associated
application.
-N = View mode only.
-P <file> = Process script mode.
--find-package = Legacy pkg-config like mode. Do not use.
--graphviz=[file] = Generate graphviz of dependencies, see
CMakeGraphVizOptions.cmake for more.
--system-information [file] = Dump information about this system.
--log-level=<ERROR|WARNING|NOTICE|STATUS|VERBOSE|DEBUG|TRACE>
= Set the verbosity of messages from CMake
files. --loglevel is also accepted for
backward compatibility reasons.
--log-context = Prepend log messages with context, if given
--debug-trycompile = Do not delete the try_compile build tree.
Only useful on one try_compile at a time.
--debug-output = Put cmake in a debug mode.
--debug-find = Put cmake find in a debug mode.
--trace = Put cmake in trace mode.
--trace-expand = Put cmake in trace mode with variable
expansion.
--trace-format=<human|json-v1>
= Set the output format of the trace.
--trace-source=<file> = Trace only this CMake file/module. Multiple
options allowed.
--trace-redirect=<file> = Redirect trace output to a file instead of
stderr.
--warn-uninitialized = Warn about uninitialized values.
--no-warn-unused-cli = Don't warn about command line options.
--check-system-vars = Find problems with variable usage in system
files.
--profiling-format=<fmt> = Output data for profiling CMake scripts.
Supported formats: google-trace
--profiling-output=<file> = Select an output path for the profiling data
enabled through --profiling-format.
--help,-help,-usage,-h,-H,/? = Print usage information and exit.
--version,-version,/V [<f>] = Print version number and exit.
--help-full [<f>] = Print all help manuals and exit.
--help-manual <man> [<f>] = Print one help manual and exit.
--help-manual-list [<f>] = List help manuals available and exit.
--help-command <cmd> [<f>] = Print help for one command and exit.
--help-command-list [<f>] = List commands with help available and exit.
--help-commands [<f>] = Print cmake-commands manual and exit.
--help-module <mod> [<f>] = Print help for one module and exit.
--help-module-list [<f>] = List modules with help available and exit.
--help-modules [<f>] = Print cmake-modules manual and exit.
--help-policy <cmp> [<f>] = Print help for one policy and exit.
--help-policy-list [<f>] = List policies with help available and exit.
--help-policies [<f>] = Print cmake-policies manual and exit.
--help-property <prop> [<f>] = Print help for one property and exit.
--help-property-list [<f>] = List properties with help available and
exit.
--help-properties [<f>] = Print cmake-properties manual and exit.
--help-variable var [<f>] = Print help for one variable and exit.
--help-variable-list [<f>] = List variables with help available and exit.
--help-variables [<f>] = Print cmake-variables manual and exit.
Generators
......
要对这些选项进行更详细的了解,参考 CMake 手册以获取更多信息。
通过下列命令生成构建器:
$ mkdir -p build
$ cd build
$ cmake ..
在这个例子中,首先创建了一个名为 build 的目录(这将作为生成构建系统的位置),然后进入该目录,并通过指定 CMakeLists.txt 文件所在的位置(在本例中位于父目录)来运行 CMake。也可以通过以下命令达到相同的效果:
cmake -H. -Bbuild
该命令具有跨平台特性,并使用了 -H
与 -B
两个 CLI 选项。其中,-H
用于从当前目录查找根 CMakeLists.txt
文件,而 -Bbuild
指定了将生成的所有构建文件放入名为 build
的目录中。
注意:cmake -H. -Bbuild
这种用法也是 CMake 官方推荐的标准方式(详情可见 cmake-developers 邮件列表)。示例中采用传统方法,即先创建一个构建目录,进入该目录后再通过指向 CMakeLists.txt
文件的位置来配置项目。
运行cmake
命令会输出一系列状态消息,显示配置信息:
$ cmake ..
-- The CXX compiler identification is GNU 11.4.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Building project recipe-hello
-- Configuring done
-- Generating done
-- Build files have been written to: /home/fly/workspace/cmakeProj/firstCMake/build
注意:在与 CMakeLists.txt
同一目录下执行 cmake .
,理论上能够对项目进行配置。但是,这样做会将所有生成文件写入到项目根目录中,从而造成源代码和生成文件混杂在一起,这种源代码内构建是不推荐的。我们更倾向于使用源外构建的方式。
CMake 本质上是一个构建系统生成器,它描述了所选构建系统(如 Unix Makefile、Ninja、Visual Studio 等)如何编译代码的过程,并根据配置生成对应的构建指令。默认情况下,在 GNU/Linux 和 macOS 系统上,CMake 会使用 Unix Makefile 生成器,而在 Windows 系统上,Visual Studio 是默认的生成器。
在 GNU/Linux 平台上,CMake 默认使用 Unix Makefile 来构建项目。主要生成文件包括:
- Makefile:包含
make
命令用来执行构建指令。 - CMakefile:记录临时文件及目录,CMake 用它来检测操作系统、编译器等环境信息,同时还根据所选生成器附带特定文件。
- cmake_install.cmake:用于处理项目安装规则的 CMake 脚本,在项目安装过程中被调用。
- CMakeCache.txt:顾名思义,这是一个用于缓存配置选项的文件,方便在重新配置项目时使用。
要构建示例项目,我们运行以下命令:
$ cmake --build .
CMake并不限制构建目录的名称或所在位置,因此我们可以将构建目录置于项目根目录之外,其效果同样良好:
$ mkdir -p /tmp/someplace
$ cd /tmp/someplace
$ cmake /path/to/source
$ cmake --build .
4.4、进阶前瞻
官方文档 https://cmake.org/runningcmake/ 提供了运行 CMake 的简明概述。由 CMake 生成的构建系统(例如上述示例中的 Makefile)包含了构建目标文件、可执行文件和库的各种目标及其规则。在当前示例中,唯一的可执行目标是 hello-world
,可以执行以下命令来查看所有可用目标:
$ cmake --build . --target help
The following are some of the valid targets for this Makefile:
... all (the default if no target is provided)
... clean
... depend
... edit_cache
... rebuild_cache
... hello-world
... hello.o
... hello.i
... hello.s
除了用于构建可执行文件的目标外,CMake 还生成了多种其他目标。通过 cmake --build . --target <target-name>
语法,可以实现如下功能:
- all(或 Visual Studio 生成器中的 ALL_BUILD):默认目标,用于构建项目中所有目标。
- clean:删除所有生成的文件。
- rebuild_cache:重新生成源文件的依赖关系(如果存在)。
- edit_cache:允许直接编辑缓存。
对于较复杂的工程,在测试阶段和安装规则的支持下,CMake 会额外生成一些目标:
- test(或在 Visual Studio 生成器中对应为 RUN_TESTS):借助 CTest 来运行测试套件。后续文章详细介绍。
- install:用于执行项目的安装规则。后续文章详细介绍。
- package:该目标调用 CPack,以生成项目的分发包。后续文章详细介绍。
五、切换生成器
CMake 是一个构建系统生成器,能够使用单个 CMakeLists.txt 文件为不同平台和工具集配置项目。在 CMakeLists.txt 中描述构建系统需要执行的操作,以便配置和编译代码。根据这些指令,CMake 会为所选的构建系统(如 Unix Makefile、Ninja、Visual Studio 等)生成相应的指令。关于生成器的内容,在后续文章中讨论,这里先只讨论如何切换生成器。
通过cmake --help
会找到可用生成器的列表。例如,安装了CMake 3.22.1的GNU/Linux机器上的输出:
Generators
The following generators are available on this platform (* marks default):
Green Hills MULTI = Generates Green Hills MULTI files
(experimental, work-in-progress).
* Unix Makefiles = Generates standard UNIX makefiles.
Ninja = Generates build.ninja files.
Ninja Multi-Config = Generates build-<Config>.ninja files.
Watcom WMake = Generates Watcom WMake makefiles.
CodeBlocks - Ninja = Generates CodeBlocks project files.
CodeBlocks - Unix Makefiles = Generates CodeBlocks project files.
CodeLite - Ninja = Generates CodeLite project files.
CodeLite - Unix Makefiles = Generates CodeLite project files.
Eclipse CDT4 - Ninja = Generates Eclipse CDT 4.0 project files.
Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files.
Kate - Ninja = Generates Kate project files.
Kate - Unix Makefiles = Generates Kate project files.
Sublime Text 2 - Ninja = Generates Sublime Text 2 project files.
Sublime Text 2 - Unix Makefiles
= Generates Sublime Text 2 project files.
因此,为项目切换生成器变得非常简单。只需复用上一节示例中的 hello.cpp
和 CMakeLists.txt
文件,唯一的区别在于,当使用 CMake
时,我们需要在命令行中显式指定生成器,因此需要使用 -G
参数。
例如:
# 配置项目
mkdir -p build
cd build
cmake -G Ninja ..
# 构建项目
cmake --build .
与前次配置相比,各步骤的输出基本没有改变。但需要注意的是,每种生成器都会生成自己特有的一组文件,因此编译过程的输出及生成目录内的内容会有所不同:
- build.ninja 和 rules.ninja:这两个文件分别包含了 Ninja 的所有构建语句及详细构建规则。
- CMakeCache.txt:这是 CMake 用于缓存配置信息的文件,与生成器无关。
- CMakeFiles:这个目录中存放了 CMake 在配置过程中生成的临时文件。
- cmake_install.cmake:该 CMake 脚本负责处理安装规则,并在执行安装命令时被调用。
cmake --build .
:这一命令封装了 ninja 命令,使之成为一个跨平台的构建接口。
如果希望深入了解生成器的详细信息,推荐查阅 CMake 的官方文档,链接如下:
https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html
六、总结
CMakeLists.txt 文件是 CMake 的输入,通过该文件定义项目的构建规则。了解 CMake 的基本命令和语法,将为顺利编写 CMakeLists.txt 打下基础。本文将通过具体的步骤演示如何创建一个简单的 CMakeLists.txt 文件,并配置项目以生成可执行文件,帮助读者快速上手 CMake。