news 2026/9/4 6:43:57

CMake进阶实战与工程化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CMake进阶实战与工程化

本篇是 CMake 系列的收尾篇,在前两篇基础上深入语法细节、拆解真实工业级项目写法、提供可落地的工程规范与问题解决方案,最终形成从入门到企业级应用的完整知识体系。

1:CMake核心语法

前两篇用到了基础语法,本节系统梳理所有高频语法的进阶用法与底层规则,是写出复杂、可维护构建脚本的基础。

1:变量系统完全解析

1:变量的本质与列表

CMake 中所有变量本质都是字符串,所谓「列表」就是用分号;分隔的字符串:

set(SRC_LIST a.cpp b.cpp c.cpp) # 实际存储为:"a.cpp;b.cpp;c.cpp" # 取值 message(${SRC_LIST}) # 输出 a.cppb.cppc.cpp(分号被当成参数分隔) message("${SRC_LIST}") # 输出 a.cpp;b.cpp;c.cpp(加引号保留分号)
  • 未定义的变量取值为空字符串,不会报错,这是最常见的隐性 Bug 来源
  • 取消定义:unset(变量名)
  • 变量名严格区分大小写
2:三种变量作用域
作用域类型定义方式可见范围向上传递方法
函数作用域函数内set()仅当前函数内部set(变量 值 PARENT_SCOPE)
目录作用域CMakeLists.txt 中set()当前目录及所有子目录同上
缓存作用域set(变量 值 CACHE 类型 "描述")全局所有文件,跨配置保留命令行-D变量=值覆盖

易错点

  • 子目录 / 函数内修改变量,父作用域默认看不到,必须加PARENT_SCOPE
  • 缓存变量和普通变量同名时,普通变量会「遮蔽」缓存变量,导致逻辑混乱,避免同名
  • 环境变量读取用$ENV{变量名},设置用set(ENV{变量名} 值),仅当前 CMake 进程生效
3:缓存变量的完整用法
# 定义用户可配置的缓存变量 set(ENABLE_TEST OFF CACHE BOOL "是否启用单元测试") set(CMAKE_BUILD_TYPE Release CACHE STRING "构建类型") # 选项命令(等价于 BOOL 类型缓存变量) option(BUILD_SHARED_LIBS "是否构建动态库" OFF)
  • 缓存变量存储在CMakeCache.txt中,多次配置保持不变
  • 命令行通过-D可以覆盖:cmake .. -DENABLE_TEST=ON
  • cmake -LAH可以查看所有缓存变量及说明

2:流程控制进阶

1:if条件判断完整规则

真假判断常量

  • 真值:1ONYESTRUE、非零数字、非空且不是假值常量的字符串
  • 假值:0OFFNOFALSENOTFOUND、空字符串、未定义变量

常用比较运算符:

比较类型运算符示例
数值比较EQUAL / LESS / GREATER / LESS_EQUAL / GREATER_EQUALif(VERSION GREATER 3)
字符串比较STREQUAL / STRLESS / STRGREATERif(COMPILER STREQUAL "GNU")
版本比较VERSION_EQUAL / VERSION_LESS / VERSION_GREATERif(CMAKE_VERSION VERSION_GREATER 3.20)
逻辑运算AND / OR / NOTif(A AND B)
存在性判断DEFINED / TARGET / COMMAND / EXISTSif(TARGET MyMath)
路径判断IS_DIRECTORY / IS_ABSOLUTE / IS_SYMLINKif(IS_DIRECTORY ${path})
2:循环结构
# 1. 遍历列表 foreach(file ${SRC_LIST}) message("处理文件: ${file}") endforeach() # 2. 遍历数字范围(0~10) foreach(i RANGE 10) message("i = ${i}") endforeach() # 3. 范围带步长(起始 结束 步长:1 10 2 → 1,3,5,7,9) foreach(i RANGE 1 10 2) message(${i}) endforeach() # 4. while 循环 set(cnt 0) while(cnt LESS 5) math(EXPR cnt "${cnt} + 1") if(cnt EQUAL 3) continue() # 跳过本次循环 endif() message("cnt = ${cnt}") endwhile()
  • break()跳出循环,continue()跳过本次迭代
  • return()直接终止当前函数 / 文件的执行

3:高频内置命令详解

1:list列表操作
set(LIST a b c d) list(APPEND LIST e f) # 追加元素 → a;b;c;d;e;f list(PREPEND LIST z) # 头部插入 → z;a;b;c;d;e;f list(REMOVE_ITEM LIST b) # 移除指定元素 → z;a;c;d;e;f list(REMOVE_AT LIST 0) # 按下标移除 → a;c;d;e;f list(LENGTH LIST len) # 求长度 → 5 list(FIND LIST "c" idx) # 查找元素下标 → 1 list(SORT LIST) # 排序 list(REVERSE LIST) # 反转 list(JOIN LIST " " str) # 用空格拼接成字符串 → "a c d e f" list(REMOVE_DUPLICATES LIST) # 去重
2:string字符串操作
set(STR "Hello World") string(LENGTH ${STR} len) # 长度 → 11 string(TOUPPER ${STR} upper) # 转大写 → HELLO WORLD string(TOLOWER ${STR} lower) # 转小写 → hello world string(SUBSTRING ${STR} 0 5 sub) # 截取子串 → Hello string(REPLACE "World" "CMake" out ${STR}) # 替换 → Hello CMake string(FIND ${STR} "World" pos) # 查找位置 → 6 string(REGEX MATCH "W[a-z]+" match ${STR}) # 正则匹配 → World string(STRIP " hello " trimmed) # 去除首尾空格 → hello
3:math数学运算
math(EXPR result "3 + 5 * 2") # 支持 + - * / % 等运算符 message(${result}) # 输出 13
4:file文件操作
# 读取文件内容 file(READ "file.txt" content) # 写入文件 file(WRITE "out.txt" "内容") file(APPEND "out.txt" "追加内容") # 通配符匹配文件 file(GLOB SRC "src/*.cpp") file(GLOB_RECURSE ALL_SRC "src/*.cpp") # 递归匹配 # 创建目录 file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/output) # 复制/删除文件 file(COPY "a.txt" DESTINATION ${CMAKE_BINARY_DIR}) file(REMOVE "tmp.txt") file(REMOVE_RECURSE "build/tmp") # 下载文件 file(DOWNLOAD "https://example.com/file.zip" "file.zip")

再次提醒:file(GLOB)仅在配置阶段执行一次,新增源文件必须重新执行 cmake 配置才能识别。

5:message消息级别
message(STATUS "普通状态信息") # 带 -- 前缀,最常用 message(WARNING "警告信息") # 黄色,不终止配置 message(AUTHOR_WARNING "开发者警告") message(FATAL_ERROR "致命错误") # 红色,直接终止配置 message(SEND_ERROR "错误") # 报错,继续执行但不生成构建文件 message(DEBUG "调试信息") # 需要开启 --log-level=DEBUG 才显示

4:函数与宏

1:function函数
# 定义函数 function(calculate_sum a b result_var) math(EXPR res "${a} + ${b}") # 通过父作用域变量返回结果 set(${result_var} ${res} PARENT_SCOPE) endfunction() # 调用 calculate_sum(3 5 sum_result) message("3 + 5 = ${sum_result}") # 输出 8

内置参数变量:

  • ARGC:参数总数
  • ARGV:所有参数列表
  • ARGV0/ARGV1...:按位置访问参数
  • ARGN:超出形参声明的剩余参数(用于实现可变参数)
2:macro宏
macro(print_msg msg) message("宏输出: ${msg}") endmacro()
3:function和marco的核心区别
特性functionmacro
作用域创建独立局部作用域继承调用者作用域,直接修改外部变量
参数是真正的局部变量是字符串替换,类似 C 宏
return 行为退出函数退出调用者整个作用域
适用场景有返回值、需要局部变量的逻辑简单的代码片段替换、设置外部变量

最佳实践:优先用 function,逻辑更清晰,避免变量污染;只有需要直接修改调用者变量时才用 macro。

4:参数解析cmake_parse_arguments

用于实现带命名参数的复杂函数:

function(my_install) set(options OPTIONAL FAST) set(oneValueArgs TARGET DESTINATION) set(multiValueArgs FILES) cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) message("TARGET: ${ARG_TARGET}") message("DESTINATION: ${ARG_DESTINATION}") message("FILES: ${ARG_FILES}") if(ARG_FAST) message("启用快速模式") endif() endfunction() # 调用 my_install( TARGET myapp DESTINATION bin FILES a.txt b.txt FAST )

5:生成器表达式详解

生成器表达式是 CMake 的高级特性,语法为$<...>在生成阶段(配置结束后)才求值,可以获取配置阶段不知道的信息(如构建类型、目标路径、编译器 ID 等)。

常用生成器表达式

表达式作用示例
$<CONFIG:Debug>判断是否为 Debug 构建$<$<CONFIG:Debug>:-g>
$<CXX_COMPILER_ID:GNU>判断是否为 GCC 编译器$<$<CXX_COMPILER_ID:GNU>:-Wall>
$<PLATFORM_ID:Linux>判断是否为 Linux 平台$<$<PLATFORM_ID:Linux>:-pthread>
$<TARGET_FILE:目标名>获取目标的完整输出路径$<TARGET_FILE:MyMath>
$<TARGET_FILE_DIR:目标名>获取目标所在目录-
$<INSTALL_INTERFACE:路径>安装时生效的路径头文件安装路径
$<BUILD_INTERFACE:路径>构建时生效的路径构建时头文件路径
$<IF:条件,值1,值2>三元表达式$<IF:$<CONFIG:Debug>,DEBUG,RELEASE>

实战用法

# Debug 构建才加 -g 和 DEBUG 宏 target_compile_options(myapp PRIVATE $<$<CONFIG:Debug>:-g>) target_compile_definitions(myapp PRIVATE $<$<CONFIG:Debug>:DEBUG_MODE>) # GCC 才加 -Wall -Wextra target_compile_options(myapp PRIVATE $<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra>) # 同时设置构建时和安装时的头文件路径 target_include_directories(MyMath PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> )

2:真实开源项目CMake实战

学习优秀开源项目的写法,是快速提升工程能力的最佳方式。本节拆解三个典型项目的 CMake 设计。

1:JsonCpp项目分析

JsonCpp 是经典的 JSON 解析库,它的 CMake 体现了成熟开源库的标准写法,非常适合作为库项目的模板。

核心配置亮点

1:标准的C++标准设置
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_EXTENSIONS OFF) # 关闭编译器扩展,保证跨编译器一致 set(CMAKE_CXX_STANDARD_REQUIRED ON) # 不支持该标准则直接报错

最佳实践:永远关闭CMAKE_CXX_EXTENSIONS,避免 GCC 的 GNU 扩展导致在 MSVC/Clang 上编译失败。

2:构建类型兜底
if(NOT DEFINED CMAKE_BUILD_TYPE AND NOT DEFINED CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE Release CACHE STRING "构建类型" FORCE) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif()

防止用户未指定构建类型,导致生成未优化的 Debug 版本。

3:编译器差异处理
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wall -Wconversion -Wshadow) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") add_compile_options(-Wall -Wconversion -Wshadow -Wextra) elseif(MSVC) add_compile_options(/W4) endif()

针对不同编译器设置合适的警告选项,体现跨平台兼容性。

4:同时构建静态库和动态库

通过选项控制,分别设置输出名、版本、安装规则,满足不同使用需求:

option(JSONCPP_WITH_STATIC "构建静态库" ON) option(JSONCPP_WITH_SHARED "构建动态库" ON) if(JSONCPP_WITH_STATIC) add_library(jsoncpp_static STATIC ${SRC}) set_target_properties(jsoncpp_static PROPERTIES OUTPUT_NAME jsoncpp) install(TARGETS jsoncpp_static EXPORT jsoncppTargets ...) endif() if(JSONCPP_WITH_SHARED) add_library(jsoncpp_shared SHARED ${SRC}) set_target_properties(jsoncpp_shared PROPERTIES OUTPUT_NAME jsoncpp VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} ) install(TARGETS jsoncpp_shared EXPORT jsoncppTargets ...) endif()
5:完整的发布配置
  • 安装头文件、库文件
  • 生成 pkg-config 的.pc文件
  • 生成 Config.cmake 支持 find_package
  • 提供 CTest 单元测试
  • 配置 CPack 打包

2:libcurl项目分析

Curl 是大型跨平台网络库,它的 CMake 体现了复杂跨平台项目的工程能力

设计亮点

1:丰富的功能选项

大量option()命令控制功能开关,用户可以按需裁剪:

option(CURL_USE_OPENSSL "使用 OpenSSL 作为 SSL 后端" ON) option(CURL_USE_MBEDTLS "使用 mbedTLS 作为 SSL 后端" OFF) option(BUILD_CURL_EXE "构建 curl 命令行工具" ON) option(BUILD_TESTING "构建测试" OFF)
2:款平台适配

大量平台分支处理差异:

if(WIN32) target_link_libraries(curl PRIVATE ws2_32 crypt32) # Windows 系统库 elseif(APPLE) find_library(SECURITY_LIB Security) target_link_libraries(curl PRIVATE ${SECURITY_LIB}) elseif(UNIX) target_link_libraries(curl PRIVATE pthread) endif()
3:灵活的依赖检测

同时支持 pkg-config 和手动查找两种方式,兼容不同系统:

find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_check_modules(OPENSSL QUIET openssl) endif() if(NOT OPENSSL_FOUND) find_package(OpenSSL REQUIRED) endif()
4:模块化目录结构

通过add_subdirectory拆分 lib、src、docs、tests、examples 等模块,结构清晰,便于维护。

3:Qt CMake项目分析

Qt 项目的 CMake 体现了GUI 项目的典型配置

核心配置

cmake_minimum_required(VERSION 3.16) project(MyQtApp LANGUAGES CXX) # 开启 Qt 三大自动工具 set(CMAKE_AUTOMOC ON) # 自动处理 Q_OBJECT 宏的元对象编译 set(CMAKE_AUTOUIC ON) # 自动编译 .ui 界面文件 set(CMAKE_AUTORCC ON) # 自动编译 .qrc 资源文件 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 查找 Qt 模块 find_package(Qt6 COMPONENTS Widgets Network REQUIRED) add_executable(MyApp main.cpp mainwindow.cpp) # 链接 Qt 模块 target_link_libraries(MyApp PRIVATE Qt6::Widgets Qt6::Network)

4:优秀项目的共性

  1. 明确的最低版本要求cmake_minimum_required放在第一行
  2. 标准的语言设置:明确 C/C++ 标准,关闭编译器扩展
  3. 目标优先的现代写法:所有配置挂载在目标上,避免全局命令
  4. 完善的跨平台处理:针对不同编译器、平台做适配
  5. 可配置的功能选项:通过 option 让用户按需裁剪
  6. 完整的发布流程:install、find_package 支持、测试、打包一应俱全
  7. 清晰的目录结构:模块化拆分,职责分明

3:CMake 常见问题排查手册

本节汇总开发中最常遇到的问题,每个问题给出「现象→原因→解决方案→预防方法」的完整排查思路。

1:VS Code 头文件下红线但编译正常

现象:代码能正常编译运行,但 VS Code 提示找不到头文件,标红波浪线。原因:VS Code 的 C/C++ 插件的智能提示和 CMake 是两套独立系统,默认不知道 CMake 配置的头文件路径。解决方案

  1. 最佳方案:安装 CMake Tools 插件,它会自动同步 CMake 的头文件路径给 C/C++ 插件
  2. 手动配置:编辑.vscode/c_cpp_properties.json,把头文件路径加入includePath
  3. 快捷修复:鼠标移到红线上 → Quick Fix → Add to includePath预防:项目初始化时就安装 CMake Tools 插件。

2: 新增源文件后编译不包含

现象:新增了 cpp 文件,重新编译时报 undefined reference,或者根本没编译这个文件。原因:用了file(GLOB)收集源文件,它只在配置阶段执行一次,新增文件不会自动检测。解决方案:重新执行一次cmake ..配置,再编译。预防

  • 小型项目用 GLOB,记得新增文件后重新配置
  • 大型项目显式列出所有源文件,更可靠
  • CMake 3.12+ 可以加CONFIGURE_DEPENDS参数:file(GLOB SRC CONFIGURE_DEPENDS "*.cpp"),构建时自动检查文件变化

3:find_package 找不到已安装的库

现象:库已经安装到系统,但find_package报错 Could NOT find。原因:库的安装路径不在 CMake 的默认搜索路径中。解决方案

  1. 手动指定搜索前缀:cmake .. -DCMAKE_PREFIX_PATH=/path/to/lib/install
  2. 指定模块路径:cmake .. -DCMAKE_MODULE_PATH=/path/to/cmake/modules
  3. 开启查找调试:cmake .. --debug-find-pkg=包名,查看具体查找过程
  4. 检查库是否安装了 CMake 配置文件(Config.cmake)或 Find 模块预防:安装库时用默认前缀/usr/local,或者记录安装路径。

4:链接错误 undefined reference

现象:编译通过,链接时报 undefined reference toxxx()原因

  1. 没有链接对应的库
  2. 库的链接顺序不对(依赖库要放在被依赖库后面)
  3. 函数实现没有编译进去
  4. C/C++ 混合编程时 extern "C" 问题解决方案
  5. 检查target_link_libraries是否包含了对应的库
  6. 检查依赖的作用域是不是 PRIVATE 导致没传递给下游
  7. 检查源文件是否被加入到目标的源文件列表
  8. C++ 调用 C 函数时,头文件加extern "C"包裹预防:用现代 CMake 的目标链接方式,自动处理依赖传递。

5:动态库运行时找不到

现象:编译正常,运行时报 error while loading shared libraries: libxxx.so: cannot open shared object file。原因:系统动态链接器找不到动态库的位置。解决方案

  1. 临时解决:export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH
  2. 永久解决:把库路径加入/etc/ld.so.conf,然后执行ldconfig
  3. CMake 层面解决:设置可执行文件的 RPATH:
    set_target_properties(myapp PROPERTIES INSTALL_RPATH "$ORIGIN/../lib" BUILD_RPATH "${CMAKE_BINARY_DIR}/lib" )

预防:发布时用$ORIGIN相对路径 RPATH,实现解压即运行。

6:CMake 缓存导致配置不生效

现象:改了 CMakeLists.txt 或者命令行参数,重新 cmake 后配置还是旧的。原因:CMakeCache.txt 中的缓存变量优先级高于 CMakeLists.txt 中的设置。解决方案

  1. 删除 build 目录下的 CMakeCache.txt,重新配置
  2. 或者直接删除整个 build 目录,从头配置(最干净)
  3. cmake -U 变量名取消指定缓存变量预防:修改缓存相关配置后,清理缓存再重新配置。

7:多线程编译报错但单线程正常

现象make -j多线程编译报错,单线程make正常。原因:目标之间的依赖顺序没有声明,多线程时依赖的目标还没编译完就开始编译依赖它的目标。解决方案:用add_dependencies(目标A 目标B)显式声明 A 依赖 B,保证构建顺序。预防:所有有依赖关系的目标都显式声明依赖,不要依赖隐式的目录顺序。

8:安装后头文件 / 库路径不对

现象:执行 install 后,文件没有安装到预期的目录。原因

  1. 没有用 GNUInstallDirs,硬写了错误的路径
  2. CMAKE_INSTALL_PREFIX被缓存成旧值
  3. install 命令的 DESTINATION 写错解决方案
  4. 引入include(GNUInstallDirs),用标准变量${CMAKE_INSTALL_BINDIR}
  5. 清理缓存,重新指定安装前缀
  6. 检查 install 命令的路径配置预防:永远用 GNUInstallDirs 标准变量,不要硬写路径。

4:工程化最佳实践与扩展

1:现代CMake编码规范

  1. 目标优先:所有配置用target_*命令挂载在目标上,禁止用全局的include_directorieslink_directoriesadd_definitions
  2. 作用域明确:明确指定 PRIVATE/PUBLIC/INTERFACE,不要省略
  3. 避免全局变量污染:尽量少用全局set(),变量尽量局部化
  4. 命名规范:目标名、选项名统一用下划线或连字符,保持一致
  5. 路径规范:用${CMAKE_CURRENT_SOURCE_DIR}${CMAKE_CURRENT_BINARY_DIR}等内置变量,不要用相对路径
  6. 版本明确:明确指定cmake_minimum_required、C/C++ 标准、库版本
  7. 注释清晰:复杂的配置、选项、自定义命令加注释说明

2:include 和 add_subdirectory深度对比

特性include()add_subdirectory()
作用执行指定的 cmake 脚本文件执行子目录下的 CMakeLists.txt
变量作用域继承当前作用域,直接读写当前变量创建独立的目录作用域,变量隔离
源码路径变量不改变CMAKE_CURRENT_SOURCE_DIR切换为子目录路径
构建目录不创建对应的构建子目录创建对应的构建子目录
适用场景加载工具函数、公共配置、宏定义构建源码子模块、子库

简单记忆:工具脚本用 include,源码模块用 add_subdirectory。

3:C++包管理器对比

CMake 原生配合包管理器可以大幅提升依赖管理效率:

包管理器特点优势劣势适用场景
vcpkg微软官方,跨平台预编译二进制,Windows 支持好,库数量多版本管理较弱Windows 优先的跨平台项目
Conan功能最全,去中心化严格的版本管理,二进制缓存,支持任意构建系统学习曲线较陡企业级大型跨平台项目
Hunter纯 CMake 实现和 CMake 深度集成,配置即下载库数量较少,编译慢纯 CMake 栈的中小型项目
FetchContentCMake 内置无需额外工具,配置时下载源码每次都要编译,没有二进制缓存轻量依赖、小型项目

4:主流IDE与编译器支持矩阵

IDE支持平台CMake 支持程度最佳适用场景
Visual StudioWindows原生深度集成Windows 桌面、大型项目
CLionWindows/macOS/Linux深度集成,体验最佳跨平台 C++ 开发
VS Code全平台插件支持,灵活度高轻量开发、远程开发
XcodemacOS原生支持Apple 生态开发
Qt Creator全平台深度集成Qt 项目开发
编译器平台CMake 支持
GCCLinux/macOS/Windows(MinGW)完美支持
ClangLinux/macOS/Windows完美支持
MSVCWindows完美支持

5:大型项目框架构建

  • 1:目录结构标准化
项目根/ ├── CMakeLists.txt # 顶级配置,只做全局设置和子目录添加 ├── cmake/ # 自定义模块、工具脚本、工具链文件 ├── src/ # 主程序源码 ├── libs/ # 内部库模块 ├── include/ # 公开头文件 ├── tests/ # 单元测试、集成测试 ├── examples/ # 示例代码 ├── docs/ # 文档 └── third_party/ # 第三方依赖(可选)
  • 2:模块化拆分:每个库一个独立目录,有自己的 CMakeLists.txt,对外只暴露必要的头文件和目标
  • 3:依赖分层:明确依赖关系,避免循环依赖,底层库不依赖上层
  • 4:统一编译选项:通过接口库统一管理全项目的编译选项、警告等级、C++ 标准:
add_library(project_options INTERFACE) target_compile_features(project_options INTERFACE cxx_std_17) target_compile_options(project_options INTERFACE -Wall -Wextra) # 所有目标都链接这个接口库 target_link_libraries(myapp PRIVATE project_options)
  • 5:版本管理:用 project 命令的 VERSION 参数管理项目版本,自动生成版本头文件
  • 6:交叉编译入门(拓展)

交叉编译是在一个平台上编译出另一个平台的可执行文件(比如在 Linux 上编译 ARM 平台的程序),CMake 通过工具链文件实现:

# toolchain-arm.cmake set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm) set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) set(CMAKE_FIND_ROOT_PATH /usr/arm-linux-gnueabihf) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

使用时指定工具链文件:

cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchain-arm.cmake

5:CMake总结

至此我们已经用三篇完整学完了整个 CMake 的全部内容,并补充了大量实战拓展:

第一篇:基础入门篇

  • CMake 核心认知与环境搭建
  • 单文件工程构建与完整命令行工作流
  • 可执行文件的编译与安装

第二篇:工程核心篇

  • 现代 CMake 目标 - 属性系统
  • 静态库 / 动态库的制作、引用与发布
  • 第三方依赖查找与集成
  • CTest 测试与 CPack 打包

第三篇:进阶实战篇

  • CMake 语法进阶与生成器表达式
  • 真实开源项目 CMake 分析
  • 常见问题排查手册
  • 工程化最佳实践与拓展
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 6:43:32

Python驱动Django漏洞挖掘:从黑盒探测到白盒审计的实战指南

简介&#xff1a;本资源是一套基于Python与Django框架实现的Web漏洞挖掘扫描系统&#xff0c;面向信息安全初学者、毕业设计学生及Web安全实践者&#xff0c;聚焦于自动化识别SQL注入等常见Web漏洞&#xff0c;并支持高中低风险分级可视化报告输出。压缩包共461个文件&#xff…

作者头像 李华
网站建设 2026/9/4 6:43:20

Moreau–Yosida正则化与Langevin采样:稀疏后验的活跃轨迹复杂度分析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 6:41:55

从PCB设计到发光爪刀:一个硬件开发全流程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 6:41:53

基于springboot小说在线阅读平台(源码+文档+部署+讲解)

温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台…

作者头像 李华
网站建设 2026/9/4 6:39:14

适配MacBook Neo!贝尔金将推30W充电器新配色,售价26.99美元

贝尔金新配色充电器适配MacBook Neo 本月晚些时候&#xff0c;贝尔金将推出30W BoostCharge Compact USB - C充电器新配色&#xff0c;包括柑橘色、淡粉色、靛蓝色和银色&#xff0c;旨在适配苹果经济实惠的笔记本电脑MacBook Neo。此前7月&#xff0c;Satechi推出了一系列与Ma…

作者头像 李华
网站建设 2026/9/4 6:38:42

Unity热更新实战:搭建基于Lua与XLua的可靠热更框架

上个项目上线后收到一条线上反馈&#xff0c;有玩家卡在某个活动的结算界面&#xff0c;点了几次都没反应。后台日志显示&#xff0c;是C#端一个玩法逻辑对配置表key做了非空判断&#xff0c;但新版本配置里这个key确实被服务端移除了。修起来不难&#xff0c;难的是客户端已经…

作者头像 李华