☰
CMake 3.24.0 源码编译安装、PATH 切换与排错实战
2026/10/24 4:42:47 网站建设 项目流程

简介:cmake-3.24.0.tar.gz 是 CMake 3.24.0 的源码归档包,面向需要在 Linux、Unix 及跨平台环境中自行编译构建工具的 C++ 开发者,尤其适合受限于发行版仓库版本、希望锁定特定 CMake 版本或研究其内部实现的中高级用户。压缩包约 9.91MB,内含约 2000 个文件,以 txt 说明与 cmake 模块脚本为主体,辅以 rst 文档、c、cxx、cpp 源码和 h 头文件,另有 json、py、sh 等配置与辅助脚本,覆盖核心源码、模块库、测试用例与文档四类内容。目前已有 427 人学习下载。解压后可按 bootstrap、make、make install 的流程完成编译安装,也可直接查阅 Modules 目录下的依赖查找实现以及 CTest、CPack 相关代码,理解库检测、打包分发与测试集成的具体做法;对需要定制构建规则、排查版本兼容问题或阅读构建系统源码的读者,这份源码包提供了完整且可复现的一手材料。

1. 版本这关先过:为什么我停在 cmake 3.24.0 而不是追最新版

cmake-3.24.0.tar.gz 这个源码包,我在没有外网、包管理器又只给到 cmake 3.13 的构建机上装过不下十遍。起因几乎每次一样:C++ 项目里一句cmake_minimum_required(VERSION 3.20),系统自带的 cmake 直接罢工,报错就一行,接手的人能在那儿耗一下午。3.20 到 3.24 之间差的不是小数点,是FetchContent、CMakePresets.json、-S/-B这些写法能不能用的分水岭。装它本身不难,难的是装完 PATH 里跑的还是旧的那个,或者 bootstrap 第一步就被编译器卡住。下面按我实际落地的顺序,从依赖核对、bootstrap 参数一直写到装完怎么验。

2. 装前把环境点清:cmake 3.24.0 的依赖组合与编译器底线

在源码编译这件事上,CMake 有点特殊:它是用一套老工具链去造一个新工具。也就是说,编译器版本、glibc 版本、压缩库开发包这三样,任何一样没对齐,bootstrap 阶段就会给你一个语焉不详的报错。这一章先把这三样点清楚,再动手解包。

2.1 三个版本号先对齐:CMake 3.24.0、g++ 与 glibc

cmake-3.24.0 的源码在构建阶段要求一个支持 C++11 的编译器。理论上 g++ 4.8 也能过,但我在老机器上实际遇到过一次模板实例化时的编译器内部错误,换成 g++ 7 之后立刻正常。所以我的底线是:g++ 7 及以上,能上 9 或 11 更稳。检查方式很直接:

g++ --version | head -1 ldd --version | head -1

第一条看编译器主版本,第二条看 glibc。glibc 这条线最容易被忽略——源码编译出来的 cmake 二进制是动态链接的,会和构建机的 glibc 版本绑死。如果你打算把这个 prefix 打包扔到另一台老机器上,必须在那台最老的机器上编译,否则运行时会直接报GLIBC_2.xx not found。

| 机器情况 | 自带 cmake | 我的处理方式 | | 内网构建机,包管理器是 yum/dnf,glibc 2.17 | 2.8 ~ 3.17 | 源码装 3.24.0 到独立 prefix,不动系统包 | | 开发机,包管理器是 apt | 3.16 ~ 3.22 | 源码装到/opt,与系统版本并存 | | 容器基础镜像 | 无 | 源码装,但镜像里别留编译依赖 | | 需要跨机器分发 | 任意 | 在最老的机器上编译,或用静态构建 |

上表最后一行是我踩过坑之后加的:图省事在新机器上编完拷过去,结果目标机器 glibc 差两个小版本,二进制直接起不来,最后还得回老机器重编一遍。

2.2 依赖清单:哪些是必须的,哪些是“顺手装上”

很多人装 cmake 会把一堆-dev包全怼上去,其实 bootstrap 默认用的是 CMake 自带的那份 curl、zlib、bzip2、lzma、expat 源码,这些开发包并不是构建 cmake 本身的硬依赖。真正必须的只有编译器、make 和(想要 ccmake 时的)ncurses。

# Debian 系:编译链是硬依赖,后面几个是给后续 C++ 项目用的 apt-get update apt-get install -y build-essential make \ libncurses-dev \ libssl-dev libcurl4-openssl-dev \ zlib1g-dev libbz2-dev liblzma-dev libexpat1-dev
# RHEL 系:注意是 gcc-c++,只装 gcc 会在 bootstrap 卡住 yum install -y gcc gcc-c++ make \ ncurses-devel \ openssl-devel libcurl-devel \ zlib-devel bzip2-devel xz-devel expat-devel

代码里分了三组,含义不一样。第一行是编译链,gcc-c++这个包名要特别注意,只装gcc的时候系统里没有g++,bootstrap 会报找不到 C++ 编译器。第二组ncurses只影响ccmake这个字符界面的配置工具,不装它命令行版 cmake 照常能用,但ccmake会报终端错误。第三组是 OpenSSL、curl、压缩库的开发头文件,构建 cmake 本身用不上,但你后面编译的 C++ 项目做find_package(OpenSSL)、find_package(ZLIB)时会去找它们,一次性装齐省得回头补。

提示:如果你明确要用--system-curl、--system-zlib这类参数,上面第三组就从“顺手装”变成“必须装”,而且版本还得对得上,否则报错点会比默认路径多得多。

2.3 校验安装包与 prefix 规划

源码包到手先做两件事:确认完整性,确认解压后的顶层目录名。

# 校验完整性,官方会提供对应的校验值,内网分发时这一步别省 sha256sum cmake-3.24.0.tar.gz # 只看前几行,确认顶层目录是不是 cmake-3.24.0 tar -tzf cmake-3.24.0.tar.gz | head -5

第二步看着多余,其实救过我一次:某次内网传包传断了,tar -tzf直接报unexpected EOF,要是直接解压再 bootstrap,报错会出现在完全不相干的地方,排查方向全歪。

prefix 我固定在/opt/cmake-3.24.0,再做一个/opt/cmake的软链指向当前版本。这样做的好处是升级时只要改软链,/etc/profile.d里的 PATH 一行都不用动:

mkdir -p /opt # 安装目录由 bootstrap 的 --prefix 创建,这里只是先把父目录备好 ls -ld /opt

3. 引导构建:bootstrap 那一行命令的参数取舍

bootstrap 是 CMake 自带的一个 shell 脚本,它先用系统编译器编出一个“最小可用的 cmake”,再用这个 cmake 去配置完整的构建。整个过程分两段,参数也分两段传,这是最容易写错的地方。

3.1 解包与独立构建目录

CMake 的 bootstrap 支持 out-of-source 构建,也就是在源码树外面单独建一个构建目录。这一点很关键:编坏了直接rm -rf构建目录重来,不用重新解包,也不会因为源码树里残留的CMakeCache.txt出现玄学问题。

# 源码统一放这里,构建目录和源码目录平级 mkdir -p /usr/local/src && cd /usr/local/src tar -xzf /root/cmake-3.24.0.tar.gz # 独立的构建目录,出问题直接删掉重来 mkdir -p /usr/local/src/cmake-3.24.0-build cd /usr/local/src/cmake-3.24.0-build

进到构建目录之后,先别急着跑完整命令,看一眼 bootstrap 支持哪些参数,比事后猜报错强:

# 列出所有支持的参数,重点看 --prefix 和 --system-* 那几行 ../cmake-3.24.0/bootstrap --help | head -40

这里要注意相对路径:../cmake-3.24.0/bootstrap指向源码目录里的脚本,脚本内部会自己定位源码根目录,所以你在哪个目录执行,构建产物就落在哪个目录。路径写错的话,报错是No such file or directory,看起来像脚本不存在,其实是层级数错了。

3.2 bootstrap 参数逐个说:哪些必须写,哪些别乱开

我实际用的一条完整命令是这样:

../cmake-3.24.0/bootstrap \ --prefix=/opt/cmake-3.24.0 \ --parallel="$(nproc)" \ --no-qt-gui \ -- -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_CXX_FLAGS="-O2 -pipe"

参数分两段,--之前是 bootstrap 自己的参数,之后是透传给生成的构建系统的 CMake 变量,这个分界线写错的话,后面的-D会被 bootstrap 当成未知选项直接退出。

| 参数 | 作用 | 我的取值理由 | |--prefix=| 最终安装根目录 | 固定/opt/cmake-3.24.0,带版本号便于共存与回滚 | |--parallel=| bootstrap 阶段自身编译的并行度 | 跟nproc走,bootstrap 阶段代码量不大,不会吃满内存 | |--no-qt-gui| 不构建 Qt 图形界面 | 服务器上永远用不到,构建它还得装 Qt 开发包 | |-DCMAKE_BUILD_TYPE=Release| 构建类型 | 不写默认是空,产出的二进制没有优化,配置大项目时明显慢 | |-DCMAKE_CXX_FLAGS="-O2 -pipe"| 追加编译选项 |-pipe减少中间文件落盘,内网小磁盘机器上体感明显 |

关于--system-curl、--system-zlib、--system-expat这一组,我的态度很明确:除非有明确的安全合规要求(比如必须链接系统打了补丁的 OpenSSL),否则一律不开。开了之后系统里的库版本、头文件路径、pkg-config 文件任何一个不对,报错都会绕到 curl 或 zlib 内部去,排查成本翻好几倍。默认用内置版本,出错点少,交叉编译时也省心。

还有一个容易被忽略的点:--parallel只影响 bootstrap 那一段,后面make的并行度是另外指定的,两者不要混为一谈。我第一次装的时候以为写了--parallel=8就万事大吉,结果make还是单线程跑了四十分钟。

3.3 make 阶段:并行度按内存算,不按核数算

bootstrap 顺利跑完的标志是最后几行出现Build files have been written,此时构建目录里已经生成了 Makefile,接下来才是真正耗时的全量编译。

# 先看内存,再决定 -j 开多少 free -g # 内存 8G 以上可以直接跟核数走 make -j"$(nproc)" # 内存 4G 以下的机器老实降并行度,否则会被 OOM killer 干掉 make -j2

并行度这件事,血泪经验是:不要迷信nproc。C++ 编译单个翻译单元的峰值内存能到 300 到 500MB,一台 4G 内存的机器开-j8,大概率在编译到一半时进程被内核杀掉,终端上只留一句Killed,什么有效信息都没有。判断方法是对着dmesg看:

# 被 OOM 杀掉的话这里会有 oom-killer 记录 dmesg -T | tail -20

如果没有 swap,临时加一块也能救急:

# 临时加 2G swap,重启失效,只用于撑过这次编译 dd if=/dev/zero of=/tmp/swapfile bs=1M count=2048 chmod 600 /tmp/swapfile mkswap /tmp/swapfile && swapon /tmp/swapfile

另外,make -j出错时多进程输出会交错在一起,真正的报错行容易被冲掉。遇到编译失败,先make -j1单线程重跑一遍拿到干净的错误信息,再去改参数,比在滚屏里翻找快得多。

4. 安装落地:prefix 布局、PATH 顺序与多版本共存

到这一步,make跑完没有报错,剩下的就是把产物落到 prefix 里,并让系统真的用上它。问题基本都出在“装是装上了,但调用的还是旧的”这个环节。

4.1 make install 与目录布局:为什么不能只拷 bin

# 装到 bootstrap 时指定的 --prefix make install # 看两个关键目录是否都在 ls /opt/cmake-3.24.0/bin ls /opt/cmake-3.24.0/share/cmake-3.24

第二条命令是重点。CMake 的安装产物不是单个可执行文件,而是一棵有固定结构的树:

| 目录 | 内容 | 缺失后果 | |bin/cmake| 主程序 | 命令不存在 | |bin/ctest、bin/cpack| 测试与打包工具 | 项目里enable_testing()后无法跑测试 | |share/cmake-3.24/Modules| 官方 Find 模块 |find_package大面积失败 | |share/cmake-3.24/Templates| 工程模板 |project()命令报错 | |share/cmake-3.24/Help| 文档,--help-*依赖它 | 帮助命令输出为空 |

主程序启动时会根据自身所在路径向上推导share/cmake-3.24的位置,这个推导逻辑是写死的。所以把bin/cmake单独拷到/usr/local/bin是典型的翻车操作,会直接报CMake Error: Could not find CMAKE_ROOT !!!。要么整个 prefix 一起搬,要么用软链。

4.2 PATH 与 shell 缓存:两个让人怀疑人生的细节

最稳的写法是往/etc/profile.d扔一个脚本,登录即生效:

# 文件名带序号前缀可以控制加载顺序,保证它排在系统 PATH 之前 cat > /etc/profile.d/cmake.sh <<'EOF' export PATH=/opt/cmake-3.24.0/bin:$PATH EOF chmod 644 /etc/profile.d/cmake.sh source /etc/profile.d/cmake.sh # 关键一步:清掉 shell 对 cmake 路径的缓存 hash -r # 确认到底有哪几个 cmake 在 PATH 里 which -a cmake

hash -r这一行是必须的。bash 会把执行过的命令路径缓存起来,你改了 PATH,它还是走缓存里的老路径,表现就是echo $PATH看着没问题,cmake --version死活是旧版本。这属于典型的“玄学问题”,其实就是缓存。

第二个细节是sudo。不少系统的 sudoers 里配了secure_path,它会把你普通用户的 PATH 整个替换掉,于是出现cmake --version是新版、sudo cmake --version是旧版的诡异现象。检查方法:

# 看 secure_path 里有没有 /opt/cmake-3.24.0/bin sudo grep -n secure_path /etc/sudoers

真要改就改 sudoers,别去sudo visudo之外的地方手改。如果不想动 sudoers,另一个方案是用update-alternatives把 cmake 挂到/usr/local/bin:

# 优先级数字越大越优先,324 只是个便于记忆的标记 update-alternatives --install /usr/local/bin/cmake cmake /opt/cmake-3.24.0/bin/cmake 324 # 切回系统版本时用这条 update-alternatives --config cmake

/usr/local/bin通常在 PATH 里排在/usr/bin前面,而且 sudo 的secure_path一般也包含它,等于绕开了上面两个坑。

4.3 CMAKE_PREFIX_PATH:别和 CMAKE_ROOT 搞混

装完之后经常有人问:要不要手动设CMAKE_ROOT?不要。这个变量由 cmake 自己根据安装路径推导,手动设了反而会让它去错误的位置找模块。真正需要你设的是CMAKE_PREFIX_PATH,它管的是“去哪儿找第三方依赖”,跟 cmake 自己住哪儿没关系。

# 多个路径用分号分隔,不要用冒号,这是 CMake 列表语法的硬要求 cmake -S . -B build \ -DCMAKE_PREFIX_PATH="/opt/openssl-3.0;/opt/boost-1.82"

分号这一条踩过的人不少。CMake 的列表分隔符是分号,写成冒号的话整串会被当成一个路径,find_package找不到任何东西,而且不报错,只是默默走到NOTFOUND分支,最后在链接阶段炸出一堆未定义符号。

5. 常见问题排查:源码装 cmake 3.24.0 最容易翻的五个坑

下面五条是我这几年重复遇到频率最高的,按“构建阶段”和“装完之后”分两组,每条都按现象、原因、解决写。

5.1 构建阶段的三个坑

坑一:bootstrap 报找不到 C++ 编译器。

现象:执行 bootstrap 后立刻退出,提示CMake requires a C++ compiler that supports C++11或cannot find a C++ compiler。

原因:只装了gcc没装g++;或者机器上残留了export CC=/usr/local/bin/gcc这类环境变量,指向的路径实际不存在。

解决:先补装gcc-c++或build-essential,再执行unset CC CXX,然后g++ --version确认能正常输出。多版本编译器共存时,显式指定CC=gcc-9 CXX=g++-9重新 bootstrap,别指望 bootstrap 自己挑。

坑二:编译到一半进程被杀,终端只剩Killed。

现象:make -j8跑到某个百分比突然退出,没有任何编译错误信息。

原因:并行度超过内存承受能力,编译进程被内核 OOM killer 回收。

解决:dmesg -T | tail确认是 oom-killer 干的,然后降到-j2或-j4重跑;内存实在小的机器临时加 swap 撑过去,编完再swapoff删掉。

坑三:ccmake一运行就报Error opening terminal: unknown。

现象:命令行版cmake一切正常,唯独ccmake打不开,或者界面花屏。

原因:一是构建时缺 ncurses 开发库,ccmake 被编成了一个残缺的可执行文件;二是当前会话的TERM变量没设或设成了不存在的类型。

解决:先echo $TERM看一眼,设成xterm-256color试;不行就补装ncurses-devel后从头重编。只用命令行的话,这个坑可以直接绕过,不影响构建。

5.2 装完之后的两个坑

坑四:cmake --version显示的仍是老版本。

现象:make install明确成功,ls /opt/cmake-3.24.0/bin/cmake也在,但版本号一直停在 3.13。

原因:三种可能——PATH 里/usr/bin排在前面;shell 的 hash 缓存没清;sudo走了secure_path。

解决:先which -a cmake把所有候选列出来定位是谁在生效,再hash -r清缓存,最后检查 sudoers 的secure_path。三步按顺序走,基本能覆盖全部情况。

坑五:报CMake Error: Could not find CMAKE_ROOT !!!。

现象:命令能找到,执行任何配置动作都失败,报找不到 CMAKE_ROOT。

原因:有人为了图快,把bin/cmake单独拷到/usr/local/bin,share/cmake-3.24目录没跟过去;或者清理磁盘时误删了 prefix 下的share目录。

解决:重新make install补齐目录结构,或者用软链代替拷贝:ln -sf /opt/cmake-3.24.0/bin/cmake /usr/local/bin/cmake。软链不会打断 cmake 对自身路径的推导,拷贝会。

6. 装完怎么验证:三条命令加一个八行工程

版本号只能证明二进制能跑,证明不了它能配置项目、能找到模块、能生成构建文件。我的验证流程分两层,先看身份,再看能力。

# 第一层:确认到底调的是哪一个,以及版本号 which -a cmake cmake --version # 第二层:让它自己吐出能力清单,确认模块目录也装到位了 cmake -E capabilities | head -c 300

第二条命令的输出是一段 JSON,里面有version、generators、fileApi这些字段。如果share/cmake-3.24/Modules缺失,这条命令往往还能跑,但下一步配置工程时会在project()那一行炸掉,所以别只看版本号就收工。

第三层用一个最小 C++ 工程做端到端验证,总共八行,两分钟能跑完:

# CMakeLists.txt cmake_minimum_required(VERSION 3.24) project(demo LANGUAGES CXX) add_executable(demo main.cpp)
// main.cpp #include <iostream> int main() { std::cout << "cmake 3.24.0 ok\n"; }
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release cmake --build build -j4 ./build/demo

-S和-B这两个参数本身就是版本信号——3.13 之前的 cmake 不认识它们,只能用mkdir build && cd build && cmake ..的老写法。所以能跑通上面这三行,基本可以确认 PATH 里生效的确实是 3.24.0,而不是某个被软链伪装过的旧版本。最后再翻一眼缓存文件,确认编译器也是你期望的那个:

# 看构建时实际用的编译器路径和版本 grep -E "CMAKE_CXX_COMPILER:|CMAKE_CXX_COMPILER_VERSION" build/CMakeCache.txt

从那以后,我每装完一台构建机,都会先跑一遍这个八行工程,再往机器台账里记一笔 prefix 路径和 glibc 版本,省得半年后重装时对着源码树发呆。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询