资讯动态

从源码编译安装CMake 3.24.0:交叉编译与C++工程实践

发布时间:2026/10/20 7:16:26 来源:尧图企业网站定制
简介CMake 3.24.0 是跨平台、开源的构建系统管理工具面向需要在 Windows、macOS、Linux 及嵌入式平台统一管理 C/C 项目构建的开发者。它通过 CMakeLists.txt 描述项目结构生成 Makefile、Visual Studio 工程或 Ninja 构建文件支持自动检测依赖库、多配置构建和 CTest 测试集成有效解决多编译器与复杂依赖环境下的构建配置难题。源码压缩包体积约 9.91MB含约 2000 个文件以 txt 说明文档、cmake 脚本、rst 文档、C/C 源文件与头文件、cpp 文件为主并包含大量测试用例与示例代码目录结构清晰涵盖源码、文档、测试与工具模块便于按需检索。解压后可获得完整源码树、构建脚本及测试集便于深入理解模块机制、自定义函数、find_package 包查找流程也为二次开发或嵌入式场景移植提供参考。已有 427 人学习下载适合希望从底层掌握跨平台构建配置的 C 开发者以及需要定制构建流程的工程团队无论是个人项目还是团队协作都能帮助开发者更高效地完成构建系统的搭建与维护。1. 先问一个问题CMake 官网的预编译包一拉就能用为什么还要折腾这份 cmake-3.24.0.tar.gz因为 C 工程一旦涉及交叉编译、离线环境或版本切换预编译包就不够用了。源码包里有 Bootstrap.cmake、全套 Modules 和 Tests遇到构建问题可以直接翻源码定位还能把 CMake 装到任意路径。简单说这是一份 CMake 3.24.0 完整源码发行版能让你不被系统包管理器绑架把构建工具链完全握在自己手里。适合被旧版 CMake 折磨过的 C 开发者、做交叉编译的嵌入式工程师以及想在 CI 里固定工具链版本的人。以下基于我实际解压、编译、用源码包构建 C 工程的过程展开。2. 解开 cmake-3.24.0.tar.gz源码目录里的设计与选型2.1 Bootstrap.cmake为什么编译入口不是 configure普通开源包的编译入口通常是 configure 脚本而 CMake 源码包不一样顶层放的是 Bootstrap.cmake、CMakeLists.txt编译入口是./bootstrap。我第一次拆这个包时也困惑过这里把两个文件的分工说清楚。Bootstrap.cmake 负责解决“先有鸡还是先有蛋”的问题。CMake 自己是用 C 写的而编译 CMake 又需要一个能跑的 CMake。bootstrap 会先检查系统里有没有 cmake 命令有则直接用它来构建没有则用脚本生成一个最小可执行的引导程序再用这个引导程序去构建完整版。所以你在终端里看到的第一阶段输出不是checking for gcc...而是类似CMake bootstrap system这样的文本这个阶段就是在做编译器探测和最小引导。CMakeLists.txt 是完整构建的真正入口它把 Source、Utilities、Tests 等子目录组织起来最终产出 cmake、ctest、cpack、cmake-gui 等工具。也就是说当你拿到 cmake-3.24.0.tar.gz第一反应应该是解压后看顶层文件而不是直接./configure那样只会得到一个 No such file or directory。tar -xzf cmake-3.24.0.tar.gz cd cmake-3.24.0 ls -1 | head -n 20这段命令做的是解压和快速浏览顶层结构。head -n 20只取前 20 个条目避免被庞大的 Tests 目录刷屏。正常情况下你会看到 Bootstrap.cmake、CMakeLists.txt、Modules、Source、Utilities、Tests、Help、Auxiliary 这些核心条目。看到这些再动手配置心里就有底了。2.2 核心目录拆解Modules、Source、Utilities 与 Tests对 C 工程师来说至少有三个目录值得仔细翻。Modules/ 是价值最高的目录里面是 CMake 官方提供的全部 .cmake 模块。搜索模块像 FindBoost.cmake、FindOpenSSL.cmake语言检测逻辑在 Modules/Compiler/ 下平台检测逻辑在 Modules/Platform/ 下。当你的工程里find_package找不到某个库时直接翻这里的 Find 脚本比上网搜索更可靠——你能看到它到底搜索了哪些路径、哪些环境变量问题立刻就会变得清楚。Source/ 是 CMake 自身的 C 源码。里面有 cmake、ctest、ccmake、cmake-gui 等子目录。想给 CMake 打补丁、或者想搞明白target_link_libraries到底怎么解析的就到这个目录看对应源文件。真正排查疑难问题的人往往是在这里找到答案的。Utilities/ 里是随 CMake 分发的第三方组件源码和维护脚本。cmzlib、json、kwiml 这些都被放在这里保证了 CMake 构建时对系统第三方库的依赖最小。Tests/ 则是官方回归测试大到完整工程构建小到某个语法特性的解析都有覆盖。自己改过源码之后跑这里可以验证改动没有破坏原有行为。与其把 CMake 当成一个黑盒子不如把源码包当成“附带完整源码、测试用例和调试符号的工具链”。这是它相比官方预编译包的核心区别。预编译包只能给你一个跑起来的二进制而源码包把“为什么会这样”的答案也一并给了你。2.3 源码包 vs 预编译二进制包版本固定与空间代价很多 Linux 发行版包管理器里的 CMake 版本偏老——某个长期支持发行版默认可能还停在 3.16 左右而 C20 工程的很多新特性检测、Presets 支持都要新版才能完整工作。源码包带来的实际优势有三个版本固定一个环境里可以同时存在系统自带旧版和源码装的新版通过 PATH 或软链接切换不影响系统包管理器。深度定制bootstrap 时可以选择要不要图形界面、要不要测试、安装到哪个目录还能对 Qt 支持做裁剪。可调试当 CMake 生成的编译规则出现莫名行为时可以直接 gdb 起 Source/ 下编出来的二进制断点看它如何解析你的 CMakeLists.txt。维度系统包管理器官方二进制包源码包版本自由度低依赖仓库中官网给什么用什么高指定小版本完全固定安装路径多数固定需 root可选任意路径--prefix 指定可调试性极低低高自带完整源码空间占用小中等大编译后约 1-2 GB离线安装依赖缓存需要预下载好解压即可编译代价也很明显源码编译在普通机器上大概需要 10 到 30 分钟生成目录会占用额外磁盘空间。如果机器内存只有 2 GB不建议把并行度拉满否则链接阶段容易直接 OOM。但对大多数从业者来说这点时间换来的版本可控性完全值得。还有一点要注意如果只是想在 CI 里快速跑一个脚本预编译包就够不必折腾源码源码包更适合那些需要长期维护、对工具链版本敏感、甚至要改 CMake 行为的场景。选型没有绝对优劣关键是知道每个选项的边界在哪里。3. 从零编译安装 CMake 3.24.0bootstrap 参数与三步安装命令3.1 安装前依赖检查在敲任何命令之前先确认三件事。第一编译器可用。gcc/g 或 clang/clang 至少要存在且版本不能太老。如果系统里只有老掉牙的版本bootstrap 阶段生成的引导程序可能在运行时直接报 GLIBCXX 相关错误这种错误定位起来相当费时间。第二基础构建工具齐全。CMake 源码编译需要 make、ld、ar以及基础头文件。某些精简版系统里连 make 都没有那就先补齐。第三OpenSSL 开发库。如果后面要用 CMake 的file(DOWNLOAD)通过 HTTPS 拉依赖openssl 头文件必须有。不装也能编译成功但相关功能会缺失。我一般用一段命令快速检查gcc --version g --version make --version ld --version pkg-config --modversion openssl 2/dev/null || echo openssl-dev not found看到openssl-dev not found时不必立刻中断安装但心里要记下这个限制。CMake 编译本身不强制依赖 OpenSSL只是 HTTPS 下载功能会受影响。真正需要这个功能时再回头补装也来得及。3.2 bootstrap 的核心参数--prefix、--parallel、--no-qt把解压后的目录作为工作目录执行./bootstrap --prefix/opt/cmake-3.24.0 --parallel8 --no-qt三个参数的实际意义如下。--prefix决定安装路径。我强烈建议按版本号建目录比如 /opt/cmake-3.24.0而不是直接装到 /usr/local。这样以后想装 3.28 时两套版本可以并存切换只需改 PATH 或软链接。--parallel控制并行度。8 是比较保守的值四核八线程的机器可以跑满而不会卡死核心数更多的机器可以适当提高到 12 或 16但不要超过物理核心数太多。--no-qt跳过基于 Qt 的图形界面构建。命令行环境下基本用不到 cmake-gui省掉的编译时间在低配机器上相当可观。bootstrap 成功之后工作目录里会多出一个 Makefile 和一堆 CMake 相关的中间文件。这时才能执行真正的编译。bootstrap 的收尾输出里会有类似CMake has bootstrapped的字样看到它再继续下一步。--------------------------------------------- CMake has bootstrapped. Now run: make ---------------------------------------------请注意如果 bootstrap 中途报错要看日志的前 20 行而不是最后 20 行。绝大多数失败发生在早期的编译器探测阶段真正的错误原因通常在最前面。3.3 make、make install 与环境变量落地编译和安装是两步make -j8 make install第一行把 CMake 各组件编出来。看到Linking CXX executable cmake之类的输出属于正常。编译过程可能持续很长中间如果报某个头文件找不到回头检查依赖是否装全了而不是反复重跑 make。第二行把 bootstrap 阶段指定的 prefix 目录真正落盘。安装完成后/opt/cmake-3.24.0 下会出现 bin、share 等子目录bin 里有 cmake、ctest、cpack、cmake-gui如果没加 --no-qt等可执行文件。最后把新版本接到 PATH 上。我习惯用软链接ls /opt/cmake-3.24.0/bin ln -sf /opt/cmake-3.24.0/bin/cmake /usr/local/bin/cmake ln -sf /opt/cmake-3.24.0/bin/ctest /usr/local/bin/ctest ln -sf /opt/cmake-3.24.0/bin/cpack /usr/local/bin/cpack cmake --version软链接方案的好处是系统原有路径不受影响想切回旧版本时删除软链即可。坏处是如果同时装 3.24 和 3.28 两套软链只能指向其中一个。真正需要多版本切换时我一般写一个极简脚本把版本目录和 PATH 的对应关系固化成几个函数而不是靠手工 export。这一步做完一个可用的 CMake 3.24.0 才算真正落地可以进入工程验证阶段了。4. 用 CMake 3.24.0 构建一个 C20 工程从 CMakeLists.txt 到交叉编译4.1 一个符合现代 CMake 习惯的 CMakeLists.txt用一个脱敏后的模拟项目 X 来演示一个 util 静态库加一个 main 可执行文件使用 C20 特性。项目结构如下demo/ ├── CMakeLists.txt ├── include/demo/util.hpp ├── src/util.cpp └── src/main.cppCMakeLists.txt 我通常写成这样cmake_minimum_required(VERSION 3.24) project(cpp_demo LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_library(demo_util STATIC src/util.cpp ) target_include_directories(demo_util PUBLIC include) add_executable(demo_main src/main.cpp) target_link_libraries(demo_main PRIVATE demo_util)这里有四个关键点值得说明。cmake_minimum_required(VERSION 3.24)写死了最低版本在旧 CMake 上会直接拒绝执行这本身就是一种版本保护。project(... LANGUAGES CXX)表示只启用 C避免 CMake 去额外探测 C 编译器省掉一次无谓的检测。add_library(demo_util STATIC ...)用静态库便于演示链接真实项目按需改成 SHARED 即可。target_include_directories(... PUBLIC include)的 PUBLIC 表示链接 demo_util 的目标也会继承 include 路径这样 main.cpp 里#include demo/util.hpp时直接能找到头文件。util.hpp 和 util.cpp 很简单但能覆盖最基本的头文件与源码分离模式// include/demo/util.hpp #pragma once namespace demo { int triple(int x); }// src/util.cpp #include demo/util.hpp namespace demo { int triple(int x) { return x * 3; } }// src/main.cpp #include demo/util.hpp #include cstdio int main() { std::printf(%d\n, demo::triple(7)); return 0; }这套写法没有任何魔法。头文件路径靠 target_include_directories 传递源码列表直接在 add_library 里给出适合中小型工程起步。4.2 用 -S 与 -B 分离源码和构建目录构建产物如果直接混在源码目录里清洁工程时极为痛苦。我强烈建议把编译产物放到 build 子目录cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPERelease cmake --build build ./build/demo_main-S .指定源码根目录-B build指定构建目录。首次配置会在 build 里生成 CMakeCache.txt它记下了本次配置的全部参数后续再次cmake --build build时不需要重新配置。这里我用 Ninja 生成器增量编译比 Unix Makefiles 快不少而且失败信息读起来更舒服。想切回 Makefile 时把-G Ninja去掉就行。# 切回 Unix Makefiles cmake -S . -B build-make -DCMAKE_BUILD_TYPERelease cmake --build build-make -j8如果你在 Windows 上用 Visual Studio 生成器命令变成cmake -S . -B build -A x64 cmake --build build --config Release注意 MSVC 生成器是多配置型的Debug/Release 在构建阶段用--config指定而不是像 Ninja 这样在配置阶段用-DCMAKE_BUILD_TYPE指定。这个区别是新手最容易踩的坑把-DCMAKE_BUILD_TYPERelease丢给 Visual Studio 生成器配置能成功但构建时找不到那个配置因为 Visual Studio 会沿用--config指定的值。4.3 交叉编译用 toolchain 文件告诉 CMake 目标环境嵌入式开发者最熟悉的场景是在 x86 开发机上编译 ARM 平台的代码。CMake 3.24 在这个场景下表现很稳核心是两件事设置 CMAKE_SYSTEM_NAME让 CMake 知道目标平台不是本机提供一个 toolchain 文件指定目标平台的编译器与工具链。# 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_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)配置时加一行参数cmake -S . -B build-arm -DCMAKE_TOOLCHAIN_FILEtoolchain-arm.cmake cmake --build build-arm这里有一个非常关键的知识点。如果没设CMAKE_TRY_COMPILE_TARGET_TYPECMake 默认会在配置阶段生成一个小可执行文件用来探测编译器是否可用。交叉编译时如果 sysroot 里缺动态链接器或 C 运行库这个测试链接就会失败进而报“编译器不可用”的误判。把它设成 STATIC_LIBRARY 后CMake 只生成静态库验证编译能力跳过最终链接步骤误报率会大幅下降。真实项目中交叉编译往往还涉及 sysroot 路径。这时 toolchain 文件里需要追加set(CMAKE_SYSROOT /path/to/sysroot)让 CMake 在搜索头文件和库时以 sysroot 为根而不是用开发机的 /usr/include。3.24 版本的这套机制已经比较成熟配合--debug-find可以把每个搜索路径都看清楚排查路径问题时非常好用。5. 避坑与常见问题五条能省下半天排查时间的记录5.1 bootstrap 阶段报错找不到可用的 C 编译器现象CMake Error: your C compiler does not support C11原因bootstrap 脚本会先探测系统默认的 g。如果环境变量 CXX 被指向一个不存在的路径或者系统里残留了过于老旧的 gCMake 的编译器检测就会失败。另一个常见原因是 PATH 里混进了一个不完整的工具链导致编译器找到了但内部头文件缺失。解决显式指定编译器再跑一次export CC/usr/bin/gcc export CXX/usr/bin/g ./bootstrap --prefix/opt/cmake-3.24.0 --parallel8 --no-qt如果 g 本身版本过老先升级编译器再编译 CMake不要在这上面凑合。CMake 3.24 对编译器的新特性多少有依赖拿一个上世纪的编译器来编它只会把问题拖到后面更隐蔽的阶段。5.2 make install 之后cmake --version 还是旧版本现象/opt/cmake-3.24.0/bin/cmake --version输出 3.24.0但在 shell 里输入cmake --version却还是系统自带的旧版本。原因PATH 优先级问题。系统自带的 /usr/bin/cmake 排在 /opt/cmake-3.24.0/bin 前面shell 找到第一个 cmake 就停了。which cmake一看就能确认到底执行的是哪个路径。解决把新版本 bin 目录插到 PATH 最前面export PATH/opt/cmake-3.24.0/bin:$PATH source ~/.bashrc或者用软链接覆盖 /usr/local/bin 下的入口见 3.3 节。注意软链接方案只解决入口问题如果某些脚本直接写死了 /usr/bin/cmake那就必须显式指定完整路径了。5.3 切换编译器后构建目录里的 Cache 漂移现象新拉下来的代码用 CMake 配置时报 “The C compiler identification is unknown”删掉 build 目录重新配置也一样。原因build/CMakeCache.txt 里缓存了上次使用的编译器路径和检测结果。无论你换编译器还是修改环境影响变量只要 CMake 认为缓存有效它就不会重新做编译器识别。解决删掉整个构建目录重建比用-U参数一个个删缓存变量更彻底rm -rf build cmake -S . -B build -G Ninja从那以后我养成了一个习惯换编译器前把旧 build 目录完整压缩备份新配置出问题时可以直接对比缓存差异。这个习惯帮我省掉了好几次把问题归结为“玄学”的冲动。5.4 把构建目录整体拷贝到另一台机器后构建失败现象在开发机上配置好的 build 目录传到服务器上后cmake --build build报一堆找不到头文件。原因CMakeCache.txt 里记录的源码路径、编译器路径都是绝对路径。换机器后路径对不上编译器版本也可能不同缓存就成了定时炸弹。解决构建目录本来就不该整体迁移。正确做法是在新机器上重新执行配置cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPERelease如果实在需要迁移预编译产物只拷贝 install 阶段产出的内容不要搬整个 build。这样虽然损失了增量编译状态但换来的是确定性和可维护性。5.5 配置时报找不到 OpenSSL但编译机明明装了现象CMake 配置阶段输出Could NOT find OpenSSL但系统里确实安装过 openssl 相关包。原因很多发行版把运行时库和开发头文件拆成两个包。只装了运行时库没装libssl-dev或openssl-develCMake 的 find_package 找不到头文件自然报找不到。解决补装开发包后清理缓存重新配置apt install libssl-dev rm -rf build cmake -S . -B build判断一个库能不能被 CMake 找到标准从来不是“系统里有没有这个命令”而是“头文件和库文件的位置是否在 find_package 的默认搜索路径里”。这个认知能避免很多无谓的排查。6. 把 CMake 3.24.0 用顺手Presets、跟踪日志与最终验证6.1 用 CMakePresets.json 固化参数每次手敲-DCMAKE_BUILD_TYPERelease -G Ninja很琐碎。3.24 对 CMakePresets.json 的支持已经相当完整项目中放一个 preset 文件参数就被固化下来了{ version: 3, cmakeMinimumRequired: { major: 3, minor: 24, patch: 0 }, configurePresets: [ { name: dev, generator: Ninja, binaryDir: ${sourceDir}/build-dev, cacheVariables: { CMAKE_BUILD_TYPE: Debug } }, { name: release, generator: Ninja, binaryDir: ${sourceDir}/build-release, cacheVariables: { CMAKE_BUILD_TYPE: Release } } ] }之后构建只需cmake --preset dev cmake --build --preset dev参数固化进工程文件新同事拉下来直接构建不靠口头传命令。注意cmakeMinimumRequired要如实写否则旧版 CMake 解析这个 JSON 时可能直接报错或静默忽略行为不一致。6.2 用 --trace 和 --debug-output 处理“玄学”问题CMakeLists.txt 写多了之后最难受的是“这段逻辑明明该执行却没执行”。此时不要乱加 message直接开跟踪cmake --trace --trace-expand -S . -B build--trace打印每一条被执行的 CMake 命令--trace-expand把变量展开成真实值。配合逻辑再跑一遍哪个分支没走进去一目了然。另一种常见情况是find_package找不到目标输出信息又太含糊。这时用cmake --debug-find -S . -B build它会详细列出每个 find_xxx 的搜索位置和命中情况信息量很足特别适合处理头文件路径类问题。比在脚本里临时加 message 更干净也更容易还原现场。6.3 最终验证确认安装的 CMake 能构建真实工程我每次装完一份源码包都会用一个最小的 C 文件做最终验收而不是只看版本号#include iostream #include version int main() { #ifdef __cpp_lib_ranges std::cout C20 ranges supported\n; #else std::cout C20 ranges not available\n; #endif }对应的 CMakeLists.txt 只需要四行cmake_minimum_required(VERSION 3.24) project(check_install LANGUAGES CXX) add_executable(check main.cpp)然后执行cmake -S . -B build cmake --build build ./build/check如果输出C20 ranges supported说明编译器、CMake 与标准库三者配合正常。如果输出not available问题大概率不在 CMake而在编译器或标准库版本。这一点我经常提醒身边同事CMake 只是构建工具它不会改变编译器对 C20 特性的支持别把账算在 CMake 头上。从拆包到装好再到支撑一个基本 C 工程和交叉编译场景这套 cmake-3.24.0.tar.gz 的完整价值已经落地。还记得我在某台老开发机上第一次用源码包装 CMake 时因为少装 libssl-dev 折腾了近一个小时从那以后我每次装完必先跑一遍上面的验收流程并且把 CMakePresets.json 顺手签进工程给同事省掉了一道“怎么配参数”的口头交接。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑