简介:CMake 3.24.0 源码分发包面向 C/C++ 开发者,提供跨平台构建系统的核心实现,用于统一管理多编译器、多平台下的项目构建流程。压缩包约 9.91 兆字节,内含两千个文件,其中近六千个 cmake 脚本负责定义构建规则,近七千个 txt 文本说明配置与参数,C/C++ 源文件(约一千四百个 c、一千三百个 h、八百余个 cxx、六百余个 cpp)构成编译主体,另有近两千个 rst 文档及测试、Dockerfile 等辅助内容,便于环境复现与源码研读。已有 426 人学习浏览该资源,适合需要离线安装特定版本、深入阅读构建系统实现,或用 CMake 管理复杂项目的开发者和运维人员。通过这份源码包,可学习 CMake 的模块组织与生成器逻辑,理解 FindPackage、CTest、CPack 等组件的实际用法,为自定义构建脚本、排查编译链接错误提供一手参考。CMake 以简洁的 CMakeLists.txt 描述项目,支持生成 Makefile、Ninja、Visual Studio 等工程,在依赖管理、测试集成与打包分发上方案成熟。
1. cmake-3.24.0.tar.gz:C++ 构建链路里最值得手动装的版本
做 C++ 的人对 cmake-3.24.0.tar.gz 应该不陌生,这是 CMake 在 3.24 时代的完整源码包,官方发布页直接给 tar.gz 格式,专供那些不信任系统包管理器版本、需要定制安装路径、或者要在离线环境里搭构建链路的场景。我的建议很直接:如果你的系统自带的 cmake 还停在 3.16 或 3.20,而你的 C++ 项目已经用上了 target_precompile_headers、文件集(file SET)、Presets 的 condition 字段,那这个源码包是当前性价比最高的升级路径。它解决的痛点不是「能装」,而是「装上之后不破坏系统原有环境、又能让新老项目共存」。适合三类人:被旧版 CMake 的 FindXXX.cmake 折磨的库使用者、要给 CI 镜像固化构建版本的同学、还有需要在内网离线编译的团队。
2. 为什么绕不过源码安装:3.24 的边界与前置依赖
2.1 3.24.0 到底带来了什么:先看这五个改动
在动手编译之前,得先搞清楚 3.24.0 相比旧版的分水岭在哪。我拆过 3.20 到 3.24 之间的 changelog,真正影响 C++ 工程实践的改动集中在五个点:
第一是 file(GET_RUNTIME_DEPENDENCIES) 在安装阶段可用。以前你要在 install() 之后手动把 DLL、SO 拷到运行目录,现在可以在 CMake 脚本里直接读取目标依赖的运行时库,Windows 下打包 Qt 程序时这个能力非常实用。
第二是 CMakePresets.json 的 condition 字段支持了 equals、notEquals 等比较操作,可以基于缓存变量做条件化配置。这意味着同一份 Presets 能在 Windows 和 Linux 上走不同的构建参数。
第三是 Imported Targets 的传递依赖做得更干净,尤其是显式标记了 INTERFACE_LINK_LIBRARIES_DIRECT 的目标,静态库的链接顺序出错概率明显下降。
第四个改动是--install支持--strip,安装时直接剥离符号表,省去后续手动 strip 的步骤。
第五是 cmake-file-api 的 codemodel v2 增加了 target 的 install 路径信息,给 IDE 和代码补全工具提供了更完整的数据。
这五条里,前三条是日常高频命中的,后面两条属于部署环节的加分项。下载源码包后你会看到 CHANGES.rst 有几十页更新记录,但真正值得为之一装的就是这类对既有工作流的直接改进。
2.2 编译前的硬件与软件门槛:不是有 gcc 就能跑
源码包解压后 configure 阶段会遇到一批显式检查,这里有一份比较实际的底线清单:
编译器方面,3.24.0 要求 C++11 编译器能完整编译 CMake 自身源码。GCC 需要 5.0 以上,Clang 需要 4.0 以上。实测 GCC 4.8.5 直接报错,原因是 CMake 源码里用了std::make_unique,老编译器在标准库层面就不满足。如果你手头只有老 GCC,建议先装一个更新版本的编译器再回来编译 CMake。
构建工具链上,Unix 平台要么有 make,要么有 ninja。Make 3.81 以上就行,Ninja 需要 1.10 以上——如果系统 ninja 太老,configure 能过但实际编译会报语法不支持。
OpenSSL 是一个隐蔽的坑。CMake 的 configure 阶段会自动找 OpenSSL 来启用 cmake-server 的 TLS 支持和 curl 的 HTTPS 下载能力。系统里没有 OpenSSL 头文件的话,configure 会降级为不支持网络下载的版本——它不会报错,只是生成的 cmake 在file(DOWNLOAD)时只能走 HTTP。如果你打算用 CMake 的FetchContent拉 GitHub 仓库,这一步必须提前确认cmake --version之外还要看有没有 libssl-dev。
我一般会在编译前跑一个检查命令,五个条件一次看清:
gcc --version | head -1 make --version | grep -o 'GNU Make [0-9.]*' | head -1 ninja --version openssl version python3 --version cmake --version注意最后一条:如果系统里已经有一个 cmake,新编译的 CMake 也要能调用它来做引导。旧版 CMake 对源码目录的 configure 有最低版本要求,官方标注是 3.24.0 源码构建需要 3.19 以上的现有 CMake。这一条比编译器版本更容易被忽略——很多服务器上只有 cmake 3.10,configure 到一半会提示 "CMake 3.19 or higher is required"。解决方法是先解压新源码里的 bootstrap 脚本直接编译最小版本,再引导完整 build。
2.3 configure 参数解析:build 类型、模块、路径
源码包的 configure 脚本接受几十个参数,真正常用的不超过八个。先把默认行为说清楚:直接./configure会生成 Makefile,采用 Unix Makefiles 生成器,默认安装路径是 /usr/local,build 类型是空(没有 -O3 优化),并且默认启用可选的第三方库检测。
实际部署时我通常使用这组参数:
./configure --prefix=/opt/cmake-3.24.0 \ --parallel=8 \ -- -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_USE_OPENSSL=ON \ -DBUILD_TESTING=OFF--prefix指定安装目录,好处是不污染 /usr,卸载时直接删目录。--parallel=8控制并行编译,不设的话 bootstrap 阶段会用单线程跑,源码包完整编译要接近十分钟,设了 8 线程能压到两分钟以内。--之后的内容直接传给生成的 CMake,CMAKE_BUILD_TYPE=Release加优化,CMAKE_USE_OPENSSL=ON强制启用 SSL 支持,BUILD_TESTING=OFF跳过测试目标,能省掉将近一半的编译时间。
再说明一个容易踩的选项:--qt-qmake。如果你系统里装了 Qt,configure 会自动检测 Qt 的 qmake 并构建 cmake-gui。但服务器环境往往只装了 Qt5 的运行时库、没有完整开发包,这时 CMake 会警告 qt development not found,然后静默跳过 GUI——这没问题。唯独一种情况要显式关:你不想让 GUI 打包进你的镜像,加-DCMake_QT_VERSION=0可以明确禁用。
configure 脚本执行完后,要留意输出末尾的这几行:
-- Build files have been written to: /path/build -- CMake 3.24.0 configured successfully如果最后一行没出现,一定是前置依赖检查不过。常见的是 OpenSSL 头文件缺失,此时看 error log 里的 "Could NOT find OpenSSL",解决办法是安装 libssl-dev 后重新从 configure 跑,不需要每次全量重来。
3. 三套编译安装路径:从本机、用户目录到离线环境
3.1 系统级安装:最常规,但权限边界要先看清楚
源码包在开发机上装到系统目录是常见做法。步骤固定,先解压再进目录:
tar -xzf cmake-3.24.0.tar.gz cd cmake-3.24.0 ./bootstrap --prefix=/usr/local make -j8 sudo make installbootstrap 和 configure 的差异值得说清楚:bootstrap 是 CMake 用来引导自身的脚本,它会把一个最小可用的 cmake 编译到 build 目录,然后用那个小 cmake 再配置完整的构建。所以第 4 步 make 用的已经是你新编译的那个最小 cmake 在驱动了。--prefix=/usr/local是老牌系统级路径,装完后cmake --version应显示 3.24.0。
需要注意一个隐藏行为:./bootstrap在 build 目录里生成的文件带有你当前用户属性,sudo make install只接管安装阶段的权限写入,目录里的 intermediate 文件不用 root 权限,所以不建议对整个源码目录做 chown -R root,否则后面想重新编译反而麻烦。
系统级安装适合自己有 sudo、且不需要多版本并存的场景。它的风险在于如果系统有包管理器装的 cmake 在 /usr/bin,而你把新版本装到 /usr/local/bin,PATH 顺序决定了谁生效——常见做法是确认echo $PATH里 /usr/local/bin 排在前面,或者直接调hash -r刷新 shell 缓存。
3.2 用户级安装:不碰系统目录的多版本方案
更推荐的是装到用户目录,这样 conda、虚拟环境、不同项目各用各的,回退版本只是改环境变量的事:
./bootstrap --prefix=$HOME/.local/cmake-3.24 make -j8 make install export PATH=$HOME/.local/cmake-3.24/bin:$PATH这个方案的关键点是$HOME/.local/cmake-3.24没有占用系统位置,安装过程不需要 sudo。后续要用它时,在~/.bashrc或~/.zshrc里加一行export PATH=$HOME/.local/cmake-3.24/bin:$PATH,其他版本的 CMake 还在原位不变。
还有一个容易被忽略的细节:CMake 的模块路径也跟 prefix 走。新装的 CMake 在找FindXXX.cmake时,会去$prefix/share/cmake-3.24/Modules里搜。如果你以前有自定义的.cmake模块放在系统路径,要注意新 CMake 找不到——验证方法很简单:
cmake --system-information 2>/dev/null | grep CMAKE_ROOT它会告诉你实际的模块目录在哪。多数项目里这是好事,相当于隔离了系统模块和项目模块;但如果你依赖了某些由旧 CMake 提供的 Find 模块,就得用CMAKE_MODULE_PATH显式指给新 CMake。
3.3 离线与交叉编译场景:没有系统 CMake 也能引导
内网机器的经典尴尬是:没有联网、系统 cmake 版本太老、又不想改系统目录。cmake-3.24.0.tar.gz 在这种场景下要配合二进制引导文件来用。
官方在发布源码包的同级目录会提供编译好的引导二进制(bootstrapping binaries),文件名像cmake-3.24.0-linux-x86_64.tar.gz。如果你的内网机器可以拿到这个文件,流程最省事:解压后把 bin 目录放进 PATH,然后源码包在 configure 阶段会自动找到这个可用的 cmake,不会触发「需要 CMake 3.19+」的检查。
如果连二进制引导包都拿不到,那就必须用 bootstrap 脚本从零开始。bootstrap 脚本会先编译一个最小 cmake,整个过程不依赖已有的 cmake,只依赖 C++ 编译器和 make。编译时长大概翻倍,但至少能通关:
./bootstrap --prefix=/opt/internal-cmake make -j4 make install离线编译还有一个需要提前排查的:CMake 源码里的测试数据包含许多 SSL 证书相关的测试用例,虽然 BUILD_TESTING=OFF 不影响主安装,但国外源码包里内嵌的curl和libarchive模块会在 configure 时检测 zlib、bz2 这些压缩库。内网机器如果缺这些开发库,会报Could NOT find BZip2或Could NOT find ZLIB,然后在编译cmake-curl时失败。提前用包管理器装好 zlib1g-dev、libbz2-dev、libssl-dev、libarchive-dev 能省掉一整天的折腾。
4. C++ 项目实战:3.24 特性怎么落进 CMakeLists.txt
4.1 用 file SET 管理头文件:告别散落 include 目录
C++ 项目里头文件的管理一直是 CMakeLists.txt 里最啰嗦的部分。3.24 的file(SET)提供了标准化的头文件分组方式,让 IDE 能精确知道每个目标关联了哪些头文件。我一般会这么写:
cmake_minimum_required(VERSION 3.24) project(demo CXX) file(SET APP_HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include FILES include/utils/string_utils.h include/utils/logger.h include/core/engine.h TYPE HEADERS) add_library(engine STATIC src/engine.cpp ${APP_HEADERS} ) target_include_directories(engine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)这段配置的价值在于:file(SET)里TYPE HEADERS标记的头文件会进入 IDE 的文件浏览树,也会被install()正确识别,但不会参与编译。对比以前用add_library后面跟一堆.h的做法,本质区别是文件集具有命名和分组能力,且可以被多个目标引用。
BASE_DIRS指定了相对路径的基准,FILES里的路径写相对路径也合法。需要说明的是,文件集在 3.23 版本引入,3.24 才彻底稳定,这是你要用 3.24 而不是 3.20 的核心理由之一。
4.2 Presets 的 condition 字段:一份配置吃透多套构建
旧版CMakePresets.json里想根据平台切换 generator 或缓存变量,只能拆成不同 preset 名字再手动选择。3.24 的 condition 字段解放了这一点:
{ "version": 6, "cmakeMinimumRequired": { "major": 3, "minor": 24, "patch": 0 }, "configurePresets": [ { "name": "linux-debug", "displayName": "Linux Debug", "generator": "Ninja", "binaryDir": "${sourceDir}/build/${presetName}", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "CMAKE_CXX_COMPILER": "g++" }, "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Linux" } }, { "name": "linux-release", "displayName": "Linux Release", "generator": "Ninja", "binaryDir": "${sourceDir}/build/${presetName}", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", "CMAKE_CXX_COMPILER": "g++" }, "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Linux" } } ], "buildPresets": [ { "name": "linux-debug-build", "configurePreset": "linux-debug", "verbose": true } ] }注意version字段必须是 6,3.24 才支持这个 schema。condition里支持equals、notEquals、inList、notInList等运算符,${hostSystemName}是预设变量,对应uname -s的结果。如果你的项目需要在 Linux 上用 gcc、在 Windows 上用 MSVC,写两个带 condition 的 preset 就够了,不需要再手动指定。
实际调用时:
cmake --preset linux-debug cmake --build --preset linux-debug-build好处是用--preset之后,binaryDir自动落在build/<presetName>,多个构建类型互不干扰,不会出现 Debug 和 Release 版二进制互相覆盖的经典问题。
4.3 配合 OpenSSL:让 FetchContent 走 HTTPS
3.24 编译时如果开了CMAKE_USE_OPENSSL=ON,项目里用FetchContent拉依赖时会自动走 HTTPS 并用证书校验。这比旧版跑 HTTP 或者忽略证书安全得多:
include(FetchContent) FetchContent_Declare( googletest URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz URL_HASH SHA256=8ad8a1ae4eebd9f6e9d3b9ea1e0b6d7d0e0b1f1e2b3c4d5e6f7a8b9c0d1e2f3a4 ) FetchContent_MakeAvailable(googletest)这里两个坑值得说。一个是URL_HASH必须写对,CMake 在下载后比对 SHA256,不一致会中止并提示实际哈希值,把提示里的值填回来就行。另一个是第一次下载后源码会缓存在CMAKE_CACHEFILE_DIR/_deps下,以后构建不会再重复下载,但如果 URL 变更了而缓存目录还在,CMake 会误以为依赖已就绪——遇到神秘编译错误先清掉_deps目录再重试。
另外,如果你的网络环境需要走内部代理,3.24 的file(DOWNLOAD)和FetchContent都读环境变量https_proxy、http_proxy、no_proxy,旧版在某些平台忽略了no_proxy,3.24 修掉了这个行为。离线环境下把依赖源码预先用脚本放好,或者用FETCHCONTENT_SOURCE_DIR_GOOGLETEST指定本地路径,都是绕开网络的常规解法。
5. 编译安装与配置避坑:五条排查记录写在这里
5.1 configure 报错找不到 C++ 编译器
现象:执行./bootstrap后立刻退出,提示CMake Error: your C++ compiler does not support C++11。
原因通常是两个:编译器版本太旧,或者CXX环境变量指向了一个不存在的路径。源码包在 bootstrap 阶段会显式编译一个小测试程序来验证 C++11 语法,如果编译器是 GCC 4.8 或更老,基本必挂。
解决:先echo $CXX看有没有残留的异常值,再用g++ --version确认真实版本。如果是版本太老,安装新 GCC 后指定export CXX=/usr/bin/g++,然后重跑 bootstrap。注意源码目录里之前生成的 build 文件不会自动清理,稳妥做法是删除源码目录重新解压,或者make clean后重来。
5.2 编译到一半报 OpenSSL 头文件缺失
现象:bootstrap 阶段正常,完整编译过程中在cmcurl或cmzlib目标上百来个错误,全是openssl/ssl.h: No such file or directory。
原因:configure 检测 OpenSSL 时只查了库文件没查头文件,或者头文件路径不在默认的 include 搜索路径里。二进制包能编译但报 SSL 函数未定义引用也是同一条根因。
解决:安装libssl-dev,如果是 CentOS 则是openssl-devel。装完别急着 make,建议把源码目录下 build 目录里 CMakeCache.txt 中CMAKE_USE_OPENSSL的 ON/OFF 看一遍,确认是 ON 再继续。出现链接错误时先跑make clean再重新 make,避免残留对象文件和新 headers 混用导致更诡异的报错。
5.3 安装完成后 cmake --version 还是旧版
现象:/usr/local/bin下明明有 cmake 3.24,执行cmake --version却显示 3.16。
原因:系统里存在多个 cmake 可执行文件,which cmake指向的是/usr/bin/cmake,说明/usr/bin排在$PATH前面。另外 shell 可能缓存了旧路径的 hash 表。
解决:先跑which -a cmake找出所有位置,再执行type cmake看 shell 解析到哪个。如果是 hash 缓存问题,一行hash -r就够。如果是 PATH 顺序问题,在~/.bashrc里把新路径写到export PATH=/usr/local/bin:$PATH而不是追加到末尾,然后在项目构建脚本里固定写法。
5.4 Presets 配置报 schema 版本不支持
现象:项目里CMakePresets.json在 3.24 下运行报preset version 4 is not supported或直接提示 schema 错误。
原因:很可能是构建设置里CMakePresets.json的version字段写的是 4,这是 3.21 时代的格式,3.24 识别不了或者会按兼容模式解析。另一个可能是你用了$env{PATH}这类变量,而 3.24 之前的 schema 不支持。
解决:把version升到 6,同时检查所有宏和变量的写法符合 3.24 的 schema。跑cmake --list-presets能验证解析是否成功,如果输出为空或报错,CMake 给出的 error 信息里一般会指明具体字段哪一行出错。
5.5 安装后的 cmake 无法找到项目原本依赖的库
现象:新 CMake 配置一个老项目,报Could NOT find XXX,但系统里确定装了对应库。
原因:新旧 CMake 的 Find 模块实现有差异,尤其是 FindOpenMP、FindBoost 这些对版本敏感的模块。3.24 模块搜索顺序调整过,并且对Boost_NO_BOOST_CMAKE变了默认值,导致老写法失效。
解决:优先看 error 信息里的版本要求,必要时在 CMakeLists.txt 里显式指定set(Boost_NO_SYSTEM_PATHS ON)或set(CMAKE_PREFIX_PATH "/usr/local")导入路径。另外要注意 install 路径如果深入/opt/cmake-3.24.0,那么CMAKE_MODULE_PATH不会被自动带上,需要显式添加。检查cmake --system-information里CMAKE_SYSTEM_PREFIX_PATH是否包含预期的前缀目录,是排查这类问题的正规入口。
6. 验证三板斧:日志、清单与构建工具链体检
源码包装完只是开始,验证阶段我一般用三个命令确认这版 CMake 真实可用。第一板斧是构造一个小工程跑完整配置与构建,确认二进制产出正常:
mkdir -p /tmp/cmake-smoke && cd /tmp/cmake-smoke cat > CMakeLists.txt <<'EOF' cmake_minimum_required(VERSION 3.24) project(smoke CXX) add_executable(smoke main.cpp) EOF cat > main.cpp <<'EOF' #include <iostream> int main() { std::cout << "cmake ok" << std::endl; } EOF /opt/cmake-3.24.0/bin/cmake -S . -B build /opt/cmake-3.24.0/bin/cmake --build build ./build/smoke这不算聪明的验证,但能一次暴露编译器路径、库搜索、rpath 三类问题。
第二板斧是检查compile_commands.json生成情况,因为 3.24 改进了文件 API 的输出粒度,用它来喂 clangd 或别的 IDE 补全工具,C++ 开发体验会明显改善:
cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ls -l build/compile_commands.json文件存在且非空,说明 CMake 的CMAKE_EXPORT_COMPILE_COMMANDS路径没坏;再用jq '. | length'看条目数是否和源码文件数对得上。
第三板斧是确认网络功能。现代 C++ 项目几乎离不开依赖下载,我习惯在用户目录建一个 FetchContent 的最小测试:
cat > /tmp/fetch-test/CMakeLists.txt <<'EOF' cmake_minimum_required(VERSION 3.24) project(fetchtest CXX) include(FetchContent) FetchContent_Declare(spdlog URL https://github.com/gabime/spdlog/archive/refs/heads/v1.x.tar.gz) FetchContent_MakeAvailable(spdlog) EOF cmake -S /tmp/fetch-test -B /tmp/fetch-test/build命令跑通说明 OpenSSL 编译时支持正常,证书链也能工作。这里有个细节:cmake 首发 URL 拉取时如果报 SSL 证书错误,先检查系统时间是否准确——内网机器时间漂移严重时 HTTPS 握手会直接失败,那和 CMake 本身没关系。
以上三步全过,新装的 CMake 才算真正能吃进 C++ 项目的日常循环。从那以后我每次迁移构建环境都强制把这三项走一遍,日志保留成清单,下一台机器照着跑就行。希望这份笔记帮你在 cmake-3.24.0.tar.gz 上少走一段弯路。
本文还有配套的精品资源,点击获取