一、CMake 基础
CMakeLists.txt 是 CMake 的配置文件,用于定义项目的构建规则、依赖关系、编译选项等。每个 CMake 项目通常都有一个或多个 CMakeLists.txt 文件。
1. 基本语法
1. 指定 CMake 的最低版本要求
cmake_minimum_required(VERSION <version>)
// 例如:
cmake_minimum_required(VERSION 3.20)
如何查询自己 Linux 离 CMake 版本呢?
2. 定义项目名称和编程语言
project(<project_name> [<language>...])
// 例如 编译语言为 C++
project(template CXX)
3. 指定要生成的可执行文件和其源文件
add_executable(<target> <source_files>...)
// 例如:
add_executable(template src/template.cpp)
4. 创建一个库(静态库或动态库)及其源文件
add_library(<target> <source_files>...)
// 例如:
// 创建静态库:静态库通常以 .a (Unix-like systems) 或 .lib (Windows) 为扩展名
add_library(hello STATIC hello .cpp)
// 创建动态库:动态库通常以 .so (Unix-like systems) 或 .dll (Windows) 为扩展名
add_library(hello SHARED hello .cpp)
5. 链接目标文件与其他库
// 1.
// 作用于全局,即所有后续定义的目标
link_libraries(<libraries>)
// 例如
link_libraries(MyLibrary)
// 2.
target_link_libraries(<target> <libraries>...)
// 例如:
target_link_libraries(sayHello MyLibrary)
6 .添加头文件搜索路径:
// 1.
// 作用于全局,即所有后续定义的目标
include_directories(<dirs>...)
// 例如:
include_directories(${PROJECT_SOURCE_DIR}/include)
include_directories(/usr/include/libxml2)
// 2.
// INTERFACE, PUBLIC, PRIVATE: 控制包含目录的可见性。
// PRIVATE: 只对当前目标有效。
// PUBLIC: 对当前目标及其依赖者都有效。
// INTERFACE: 只对依赖于当前目标的其他目标有效。
// [SYSTEM]: 如果使用了 SYSTEM 关键字,编译器会将这些目录视为系统包含目录,这可能会减少警告信息。
// [BEFORE]: 如果使用了 BEFORE 关键字,新的包含目录会被添加到现有包含目录之前,而不是之后。
target_include_directories(TARGET target_name
[BEFORE | AFTER]
[SYSTEM] [PUBLIC | PRIVATE | INTERFACE]
[items1...])
// 例如:
target_include_directories(hello PRIVATE ${PROJECT_SOURCE_DIR}/include)
7. 设置变量的值:
set(<variable> <value>...)
// 例如:
set(CMAKE_CXX_STANDARD 11)
8. 安装规则
// RUNTIME, ARCHIVE, LIBRARY, OBJECTS, FRAMEWORK, BUNDLE, PRIVATE_HEADER, PUBLIC_HEADER, RESOURCE: 指定不同类型的目标组件。
// RUNTIME: 可执行文件或动态链接库。
// ARCHIVE: 静态库。
// LIBRARY: 动态库(共享库)。
// OBJECTS: 目标文件。
// FRAMEWORK: macOS 框架。
// BUNDLE: macOS 应用程序包。
// PRIVATE_HEADER: 私有头文件。
// PUBLIC_HEADER: 公共头文件。
// RESOURCE: 资源文件。
install(TARGETS target1 [target2 ...]
[RUNTIME DESTINATION dir]
[LIBRARY DESTINATION dir]
[ARCHIVE DESTINATION dir]
[INCLUDES DESTINATION [dir ...]]
[PRIVATE_HEADER DESTINATION dir]
[PUBLIC_HEADER DESTINATION dir])
// 例如:
install(TARGETS hello RUNTIME DESTINATION bin)
9. 条件语句 (if, elseif, else, endif 命令)
// 这里直接上示例理解吧
// CMAKE_SYSTEM_PROCESSOR 包含当前处理器架构名称
//
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
add_definitions(-DX86_64)
target_link_libraries(control_node /lib/x86_64-linux-gnu/libxml2.so)
target_link_libraries(control_node /lib/x86_64-linux-gnu/libcurl.so )
target_link_libraries(control_node /usr/lib/x86_64-linux-gnu/libboost_filesystem.so)
target_link_libraries(control_node /lib/x86_64-linux-gnu/libsqlite3.so)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
add_definitions(-DAARCH64)
target_link_libraries(control_node /lib/aarch64-linux-gnu/libxml2.so)
target_link_libraries(control_node /usr/local/lib/libcurl.so )
target_link_libraries(control_node /lib/aarch64-linux-gnu/ibsqlite3.so)
target_link_libraries(control_node /lib/aarch64-linux-gnu/libboost_filesystem.so)
endif()
10. 自定义命令 (add_custom_command 命令)
// TARGET hello: 指定自定义命令应用于 hello 目标。
// POST_BUILD: 指定命令在 hello 构建完成后执行。
// COMMAND ${CMAKE_COMMAND} -E echo "Build completed.": 执行的命令是 cmake -E echo "Build completed.",其中 ${CMAKE_COMMAND} 是 CMake 的可执行文件路径,-E 选项表示使用 CMake 内置的命令解释器。
// COMMENT "Notifying build completion": 为自定义命令提供描述性文本,这将在构建过程中显示。
add_custom_command(
TARGET hello POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Build completed."
COMMENT "Notifying build completion"
)
// 运行结果
[ 50%] Building CXX object CMakeFiles/MyExecutable.dir/main.cpp.o
[100%] Linking CXX executable MyExecutable
[100%] Built target MyExecutable
Notifying build completion
Build completed.
11. 变量和缓存变量
1. 变量 (普通变量)
作用范围:普通变量的作用范围通常是局部的,即它们只在定义它们的 CMakeLists.txt 文件或函数/宏中有效。
生命周期:普通变量的生命周期仅限于当前构建过程。每次重新运行 CMake 时,这些变量都会被重新初始化。
使用场景:适用于临时存储数据、控制逻辑流程等。
定义方式:使用 set 命令来定义
// 定义一个普通变量
set(MY_VARIABLE "Hello, World!")
// 使用变量
message(STATUS "MY_VARIABLE: ${MY_VARIABLE}")
2. 缓存变量 (Cache Variables)
作用范围:缓存变量的作用范围是全局的,并且可以在多次 CMake 运行之间保持状态。
生命周期:缓存变量的生命周期跨越多次 CMake 运行,直到用户显式地删除或覆盖它们。
使用场景:适用于需要在多次 CMake 运行之间保持状态的数据,例如配置选项、路径设置等。
定义方式:使用 set 命令并加上 CACHE 关键字来定义。
// 定义一个缓存变量
set(MY_CACHE_VARIABLE "Some Value" CACHE STRING "Description of the variable")
// 使用缓存变量
// set 命令用于设置变量。
// MY_CACHE_VARIABLE 是要设置的变量名。
// "Some Value" 是该变量的初始值。
// CACHE 关键字表示这是一个缓存变量。
// STRING 是变量的数据类型。CMake 支持多种数据类型,包括 STRING, BOOL, FILEPATH, PATH, INTERNAL 等。
message(STATUS "MY_CACHE_VARIABLE: ${MY_CACHE_VARIABLE}")
12. 查找库和包
CMake 可以通过 find_package() 指令自动检测和配置外部库和包。常用于查找系统安装的库或第三方库。
// 基本用法:
find_package(Boost REQUIRED)
// 指定版本:
find_package(Boost 1.70 REQUIRED)
// 查找库并指定路径:
find_package(OpenCV REQUIRED PATHS /path/to/opencv)
// 使用查找到的库:
target_link_libraries(MyExecutable Boost::Boost)
// 设置包含目录和链接目录:
include_directories(${Boost_INCLUDE_DIRS})
link_directories(${Boost_LIBRARY_DIRS})
二、高级特性
CMake 高级特性允许我们更灵活地管理和配置 CMake 项目,以适应复杂的构建需求和环境。
- 自定义 CMake 模块和脚本:创建自定义模块和脚本以简化构建过程。
- 构建配置和目标:使用多配置生成器和定义多个构建目标。
- 高级查找和配置:灵活地查找包和配置构建选项。
- 生成自定义构建步骤:添加自定义命令和目标以执行额外的构建操作。
- 跨平台和交叉编译:支持不同平台的构建和交叉编译。
- 目标属性和配置:设置和修改目标的属性,以满足特定需求。
1. 自定义 CMake 模块和脚本
1. 创建自定义模块
// 1.
// 在项目目录下创建一个 cmake/ 目录,用于存放自定义 CMake 模块。
// 2.
// 在 cmake/ 目录下创建一个 MyModule.cmake 文件。
// 3.
// 在 CMakeLists.txt 文件中包含自定义模块:
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
include(HelloModule)
message(STATUS "${CMAKE_SOURCE_DIR} = " ${CMAKE_SOURCE_DIR})
sayHello()
// 例如
// 自定义模块示例 (MyModule.cmake):
function(sayHello)
message(STATUS "hello")
endfunction()
// 在 CMakeLists.txt 中调用自定义函数:
my_custom_function()
2. 调用自定义模块
自定义 CMake 脚本允许你执行自定义配置操作,灵活处理复杂的构建要求。
// 创建自定义脚本:
// 在项目中创建一个脚本文件(例如 config.cmake)。
// 在脚本中编写 CMake 指令。
// 在 CMakeLists.txt 文件中调用脚本:
include(${CMAKE_SOURCE_DIR}/config.cmake)
2. 构建配置和目标
1. 多配置生成器
CMake 支持多种构建配置(如 Debug、Release)。多配置生成器允许你在同一构建目录中同时生成不同配置的构建。
// 指定配置类型
// 1.
//在 CMakeLists.txt 中设置默认配置:
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type")
// 2.
// 使用 Visual Studio:
// 在 Visual Studio 中选择构建配置(Debug 或 Release)。
2. 构建目标
你可以定义多个构建目标,每个目标可以有不同的构建设置和选项。
// 添加多个目标:
add_executable(MyExecutable1 src/main1.cpp)
add_executable(MyExecutable2 src/main2.cpp)
// 设置目标属性:
set_target_properties(MyExecutable1 PROPERTIES COMPILE_DEFINITIONS "DEBUG")
set_target_properties(MyExecutable2 PROPERTIES COMPILE_DEFINITIONS "RELEASE")
3. 高级查找和配置
1. 查找包的高级用法
find_package() 指令可以用于查找和配置复杂的第三方库和包。
// 查找包的高级选项:
find_package(Boost REQUIRED COMPONENTS filesystem system)
// 设置查找路径:
set(BOOST_ROOT "/path/to/boost")
find_package(Boost REQUIRED)
4. 生成自定义构建步骤
1. 可以通过 CMake 配置文件来控制构建选项和配置
可以在运行 cmake 命令时传递各种选项来设置变量值。这些变量可以是缓存变量或普通变量。
// 示例
// 设置编译类型为 Release
cmake -DCMAKE_BUILD_TYPE=Release ..
// 设置自定义变量 MY_CUSTOM_VAR
cmake -DMY_CUSTOM_VAR="My Value" ..
2. 使用 CMakeCache.txt
CMakeCache.txt 文件存储了所有缓存变量的值。你可以手动编辑这个文件来修改变量值,或者在首次运行 cmake 时通过命令行设置这些变量。
3. 使用 CMakePresets.json 和 CMakeUserPresets.json
CMake 3.19 及以上版本引入了 CMakePresets.json 和 CMakeUserPresets.json 文件,它们提供了一种更现代的方式来管理构建预设。这些文件允许你定义一组预设配置,包括工具链文件、构建类型、环境变量等。
4. 使用 ccmake 或 cmake-gui
ccmake 和 cmake-gui 是 CMake 提供的图形界面工具,允许用户交互式地设置和查看缓存变量。
5. 跨平台和交叉编译
// 指定平台
cmake -DCMAKE_SYSTEM_NAME=Linux ..
// 交叉编译
cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/toolchain.cmake ..
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
6. 目标属性和配置
1. 设置和修改目标的属性,如编译选项、链接选项等
// 设置编译选项:
set_target_properties(MyExecutable PROPERTIES COMPILE_OPTIONS "-Wall")
// 设置链接选项:
set_target_properties(MyExecutable PROPERTIES LINK_FLAGS "-L/path/to/lib")
2. 自定义编译和链接选项
为特定目标设置自定义的编译和链接选项。
// 设置编译选项:
target_compile_options(MyExecutable PRIVATE -Wall -Wextra)
// 设置链接选项:
target_link_options(MyExecutable PRIVATE -L/path/to/lib)
参考文档:
菜鸟自学CMake:https://www.runoob.com/cmake/cmake-basic.html
CMake 官方文档: https://cmake.org/cmake/help/latest/guide/tutorial/index.html