CMake 介绍与基础使用
最近做项目用到ESP32,还顺便尝试换用CLion写STM32,开始不断接触到CMake编译链,感觉挺有意思也挺好用的。所以写一篇文章来简单介绍并记录一下CMake的基础用法。
简单介绍一下吧:
CMake 是个一个开源的跨平台自动化建构系统,用来管理软件建置的程序,并不依赖于某特定编译器,并可支持多层目录、多个应用程序与多个函数库。 CMake 通过使用简单的配置文件 CMakeLists.txt,自动生成不同平台的构建文件(如 Makefile、Ninja 构建文件、Visual Studio 工程文件等),简化了项目的编译和构建过程。 CMake 本身不是构建工具,而是生成构建系统的工具,它生成的构建系统可以使用不同的编译器和工具链。
CMake 的作用和优势:
- 跨平台支持: CMake 支持多种操作系统和编译器,使得同一份构建配置可以在不同的环境中使用。
- 简化配置: 通过 CMakeLists.txt 文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本。
- 自动化构建: CMake 能够自动检测系统上的库和工具,减少手动配置的工作量。
- 灵活性: 支持多种构建类型和配置(如 Debug、Release),并允许用户自定义构建选项和模块。
构建配置:
- CMakeLists.txt 文件: CMake 的配置文件,用于定义项目的构建规则、依赖关系、编译选项等。每个 CMake 项目通常包含一个或多个 CMakeLists.txt 文件。
- 构建目录: 为了保持源代码的整洁,CMake 鼓励使用独立的构建目录(Out-of-source 构建)。这样,构建生成的文件与源代码分开存放。
CMake 的基本工作流程:
- 编写 CMakeLists.txt 文件: 定义项目的构建规则和依赖关系。
- 生成构建文件: 使用 CMake 生成适合当前平台的构建系统文件(例如 Makefile、Visual Studio 工程文件)。
- 执行构建: 使用生成的构建系统文件(如 make、ninja、msbuild)来编译项目。
CMake 基础
简单介绍完 CMake ,现在来讲一下 CMake 的基础用法。
CMakeLists.txt 文件:
CMakeLists.txt 是 CMake 的配置文件,用于定义项目的构建规则、依赖关系、编译选项等,是非常重要的文件。
每个 CMake 项目通常都有一个或多个 CMakeLists.txt 文件。
文件结构和基本语法:
CMakeLists.txt 文件使用一系列的 CMake 指令来描述构建过程。常见的指令包括:
1.项目基础设置:
1. cmake_minimum_required
指定 CMake 最低版本要求。
1 | cmake_minimum_required(VERSION <version>) |
例如:
1 | cmake_minimum_required(VERSION 3.15) |
2. project
定义项目名称和使用的语言。
1 | project(<project_name> [<language>...]) |
例如:
1 | project(MyProject VERSION 1.0.0 LANGUAGES CXX) |
2. 生成目标:
1. add_executable
添加可执行文件目标。
1 | add_executable(<target> <source_files>...) |
例如:
1 | add_executable(MyExecutable main.cpp other_file.cpp) |
2. add_library
添加库文件目标。
1 | add_library(<target> <source_files>...) |
例如:
1 | # 静态库 |
3. add_subdirectory
添加子目录进行构建。
1 | add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL]) |
例如:
1 | add_subdirectory(src) |
3. 头文件和库链接:
1. target_include_directories
为目标添加包含目录。
1 | target_include_directories(TARGET target_name |
例如:
1 | target_include_directories(my_app PRIVATE |
PRIVATE - 仅对目标本身可见 PUBLIC -
对目标及其依赖目标可见 INTERFACE -
仅对依赖此目标的其他目标可见
2. target_link_libraries
为目标链接库。
1 | target_link_libraries(<target> [BEFORE] |
例如:
1 | target_link_libraries(my_app PRIVATE mylib) |
4. 变量和属性
1. set
设置变量。
1 | set(<variable> <value> [CACHE <type> <docstring> [FORCE]]) |
例如:
1 | set(SOURCE_FILES main.cpp utils.cpp) # 普通变量 |
2. set_target_properties
为目标设置属性。
1 | set_target_properties(<target> [<target>...] |
例如:
1 | set_target_properties(mylib PROPERTIES |
5. 查找包
1. find_package
查找依赖包。
1 | find_package(<PackageName> [version] [EXACT] [QUIET] [REQUIRED] |
例如:
1 | find_package(Boost 1.70 REQUIRED COMPONENTS filesystem system) # 查找 Boost |
2. pkg_check_modules
1 | pkg_check_modules(<prefix> [REQUIRED] [QUIET] |
例如:
1 | pkg_check_modules(GTK3 REQUIRED gtk+-3.0) |
6. 条件控制
1. option
定义用户可配置的选项,值为 ON 或 OFF,可在 cmake-gui 或 cmake 命令行中修改。
1 | option(<option_variable> "description" [initial_value]) |
例如:
1 | option(BUILD_TESTS "Build test programs" ON) # 是否构建测试 |
2. 条件语句
根据条件执行不同的 CMake 命令,支持多种条件判断。
1 | if(<condition>) |
例如:
1 | if(WIN32) |
7. 循环结构
1. foreach
遍历列表或数字范围,对每个元素执行一组命令。
1 | foreach(<loop_var> <items>) |
例如:
1 | set(SOURCES file1.cpp file2.cpp file3.cpp) |
2. while
当条件为真时循环执行一组命令。
1 | while(<condition>) |
例如:
1 | set(CNT 0) |
8. 文件和路径操作
1. file
执行文件系统操作,如读写文件、创建目录、搜索文件等。
1 | file(READ <filename> <out-var> [...]) |
例如:
1 | file(READ "version.txt" PROJECT_VERSION) # 读取文件内容 |
2. configure_file
复制文件并替换其中以 @VAR@ 或 ${VAR}
格式的变量。
1 | configure_file(<input> <output> |
例如:
1 | # config.h.in 内容: #define VERSION "@PROJECT_VERSION@" |
9. 消息输出
1. message
向用户输出信息,用于调试、状态显示或错误提示。
1 | message([<mode>] "message text") |
| Mode | 功能 |
|---|---|
STATUS |
一般状态信息,前缀 -- |
WARNING |
警告信息,继续执行 |
AUTHOR_WARNING |
开发者警告 |
SEND_ERROR |
错误信息,继续执行 |
FATAL_ERROR |
致命错误,终止配置 |
DEBUG |
调试信息 |
例如:
1 | message(STATUS "Configuring project...") # 输出状态 |
10. 测试和安装
1. enable_testing
启用当前目录及子目录的测试功能,使 add_test
可以添加测试。
1 | enable_testing() |
例如:
1 | enable_testing() |
2. add_test
注册一个测试用例,指定名称和要执行的命令。
1 | add_test(NAME <name> COMMAND <command> [<args>...]) |
例如:
1 | add_test(NAME MyTest COMMAND my_test_executable) |
3. install
定义文件、目标、目录的安装规则,指定安装位置。
1 | install(TARGETS <target>... |
例如:
1 | install(TARGETS my_app RUNTIME DESTINATION bin) # 安装可执行文件 |
11. 其他常用指令
1. target_compile_definitions
为目标添加预处理宏定义,编译时相当于 -D 选项。
1 | target_compile_definitions(<target> |
例如:
1 | target_compile_definitions(my_app PRIVATE DEBUG ENABLE_LOGGING) |
2. target_compile_options
为目标添加编译选项,传递给编译器的额外flags。
1 | target_compile_options(<target> [BEFORE] |
例如:
1 | target_compile_options(my_app PRIVATE -Wall -Wextra -Wpedantic) |
3. target_sources
为已存在的目标追加源文件。
1 | target_sources(<target> [BEFORE] |
例如:
1 | target_sources(my_lib PUBLIC |
4. include
加载并执行另一个 CMake 文件,类似于 C 语言的
#include。
1 | include(<file> [OPTIONAL] [RESULT_VARIABLE <var>]) |
例如:
1 | include(${CMAKE_SOURCE_DIR}/cmake/FindPackage.cmake) # 包含文件 |
5. function
定义一个函数,创建新的 CMake 命令,支持参数传递。
1 | function(<name> [<arg1> <arg2> ...]) |
例如:
1 | function(add_my_executable name) |
6. macro
定义一个宏,与函数类似但不在独立作用域执行,变量直接操作调用者作用域。
1 | macro(<name> [<arg1> <arg2> ...]) |
例如:
1 | macro(add_simple_target name) |
12. 常用变量速查
| 变量 | 说明 |
|---|---|
CMAKE_SOURCE_DIR |
项目根目录(包含顶层 CMakeLists.txt) |
CMAKE_BINARY_DIR |
构建目录(执行 cmake 的目录) |
CMAKE_CURRENT_SOURCE_DIR |
当前处理的 CMakeLists.txt 所在目录 |
CMAKE_CURRENT_BINARY_DIR |
当前构建目录 |
CMAKE_PROJECT_NAME |
最顶层项目的名称 |
PROJECT_NAME |
当前 project() 定义的项目名称 |
PROJECT_VERSION |
当前项目的版本号 |
CMAKE_CXX_STANDARD |
C++ 标准版本(如 11, 14, 17) |
CMAKE_CXX_COMPILER |
C++ 编译器路径 |
CMAKE_CURRENT_LIST_FILE |
当前处理的 CMake 文件路径 |
CMAKE_CURRENT_LIST_LINE |
当前处理的行号 |
CMAKE_VERSION |
CMake 版本号 |
CMAKE_BUILD_TYPE |
构建类型:Debug 或 Release |
CMAKE_SOURCE_DIR |
项目根目录 |
