CMake 047:解锁安装阶段自定义操作,告别配置阶段提前执行坑

CMake 047:解锁安装阶段自定义操作,告别配置阶段提前执行坑

前言✨

不知道大家在使用 CMake 构建项目的时候,有没有踩过这样一个大坑🤯:

满心欢喜写下一条message()打印安装进度日志,结果执行cmake -S . -B build刚走完配置步骤,日志就一股脑全部打印出来了。等到真正执行安装命令cmake --install的时候,反而看不到任何进度提示。

究其根本,CMake 的生命周期可不是简简单单一步到位。一条完整的编译流水线被清晰地切分成了四大阶段:配置→生成→编译→安装

普通的message语句只会乖乖在【配置阶段】运行。如果你想要在最后的安装环节执行自定义逻辑,打印日志、写入文件、校验环境、拷贝额外资源,单纯依靠普通 CMake 语法已经束手无策。

今天就给大家揭秘 CMake 里一个冷门却超级好用的神器 ------install(CODE)🎉,它可以让一段 CMake 脚本延迟到安装时刻才执行,解锁超多高级玩法!

Bilibili 同步视频

CMake 047:解锁安装阶段自定义操作,告别配置阶段提前执行坑

一、先搞懂!为什么普通代码不能在安装时运行💡

先来回顾一下 CMake 标准工作流程:

  1. 配置阶段 :解析CMakeLists.txt,检查环境变量、依赖库,执行顶层直接书写的messageif判断等代码

  2. 生成阶段:生成 Makefile、VS 工程等构建脚本

  3. 编译阶段:调用编译器,把源码编译成可执行程序、静态库、动态库

  4. 安装阶段:把编译产出的头文件、库文件、二进制程序拷贝到指定目录

⚠️踩坑预警:直接写在顶层的message()会在配置阶段立刻执行,并不是等到安装的时候才触发!

举一个反面教材(错误写法❌):

cmake 复制代码
message(STATUS "开始执行项目安装!")
install(FILES a.h DESTINATION include)

运行cmake -S . -B build,这条日志直接就打印出来了。等你后续敲下cmake --install执行安装,再也不会出现这条提示,完全达不到监控安装流程的目的。

这时候install(CODE)就该闪亮登场了!

被包裹在install(CODE "")内部的所有 CMake 代码,都会延迟执行,只有当你真正运行安装命令的时候,代码才会被唤醒运行。内部可以使用几乎全部 CMake 语法,调用函数、读写文件、打印日志,自由度拉满!

二、上手实战:打印安装进度日志📝

我们先来搭建一个极简的演示工程。

新建两个空的头文件a.hb.h,模拟项目中需要被安装导出的头文件资源。

接下来编写CMakeLists.txt

cmake 复制代码
# 将两个头文件安装到输出目录下的include文件夹
install(FILES a.h DESTINATION include)
install(FILES b.h DESTINATION include)

# install(CODE)内部脚本,延迟至安装阶段执行
install(CODE "
message(STATUS "================Begin install================")
message(STATUS "✅ a.h 文件开始安装")
message(STATUS "✅ b.h 文件开始安装")
message(STATUS "================Install Success================")
")

执行构建 & 安装命令

bash 复制代码
# 1.配置&生成构建目录
cmake -S . -B build
# 2.执行安装,所有文件输出到out文件夹下
cmake --install build --prefix out

运行结果:

配置生成的时候控制台安安静静,没有任何安装日志输出;当执行安装命令的一瞬间,进度提示文字齐刷刷打印到终端!🎉

💡小知识点:字符串转义问题

在双引号包裹的CODE脚本块中,如果要读取 CMake 变量,必须添加反斜杠进行转义 ****${变量名}

如果忘记转义,变量会在【配置阶段】就被提前解析替换掉,等到安装阶段变量早已失效,这是新手高频踩坑点!

三、进阶玩法:记录每次安装的时间日志⏱️

仅仅打印控制台提示远远不够。我们更进一步,实现一个小功能:每一次执行安装操作,自动把当前安装时间追加写入日志文件 **install_log.txt**。

使用 CMake 内置指令string(TIMESTAMP)获取系统时间戳,搭配file(APPEND)追加写入文件。

file(APPEND)file(WRITE) 的核心区别:

  • WRITE:新建文件写入内容,如果文件存在就覆盖原有内容

  • APPEND:在文件末尾追加新内容,旧日志永久保留,完美适配日志场景

完整 CMake 示例代码:

cmake 复制代码
install(FILES a.h DESTINATION include)
install(FILES b.h DESTINATION include)

install(CODE "
# 获取当前系统时间戳,格式化:年‑月‑日 时:分:秒
string(TIMESTAMP now "%Y‑%m‑%d %H:%M:%S")
message(STATUS "🕒本次项目安装时间:${now}")
# 将时间追加写入日志文件,换行分隔每一条记录
file(APPEND install_log.txt "${now}n")
")

多次执行安装命令:

bash 复制代码
cmake --install build --prefix out

打开生成出来的install_log.txt,就可以看到一条条按顺序记录下来的安装时间:

Plain 复制代码
2026‑08‑14 14:20:10
2026‑08‑14 14:21:33
2026‑08‑14 14:22:05

完美实现安装行为留痕!

四、拓展思考:C++ 场景下还能做什么?💻

install(CODE)不仅仅只能打印日志、记录时间。在大型 C++ 工程中,它还有超多实用的落地场景,这里给大家列举几个高频方案:

  1. 安装后资源校验:安装完成之后,自动校验目标路径下头文件、动态库是否拷贝成功,缺失文件立刻抛出警告

  2. 动态库部署 :Windows 平台安装阶段拷贝 dll 依赖文件,Linux 下执行rpth路径修改脚本

  3. 生成版本配置文件 :安装的时候生成version.h版本头文件,写入编译时间、版本号

  4. 执行外部程序:安装阶段调用脚本、第三方工具,完成额外的初始化工作

这里给大家演示一个 C++ 读取刚才生成的install_log.txt日志文件的示例代码,读取并打印全部安装记录:

cpp 复制代码
#include <iostream>
#include <fstream>
#include <string>

int main()
{
    std::ifstream logFile("install_log.txt");
    if (!logFile.is_open())
    {
        std::cout << "日志文件打开失败!" << std::endl;
        return -1;
    }

    std::string line;
    std::cout << "=======项目所有安装记录=======n";
    while (std::getline(logFile, line))
    {
        std::cout << line << std::endl;
    }
    logFile.close();
    return 0;
}

五、干货总结📋

关键点 说明
install(CODE "") 脚本延迟至安装阶段运行,不会在配置阶段提前执行
变量转义 代码块内部读取变量必须书写${var},防止提前解析
string(TIMESTAMP) CMake 内置函数,快速获取格式化系统时间戳
file(APPEND) 追加写入文件,适合生成日志、记录流水

善用install(CODE)就相当于你拿到了 CMake 安装流程的 "后门控制权",当你以后遇到需要在安装那一刻执行自定义逻辑的需求,不妨掏出这个宝藏 API 试一试~

本文演示项目完整源码可自行复制 CMake 脚本动手实操,欢迎大家在评论区交流你解锁过哪些 CMake 奇技淫巧😜

相关推荐
Ai思想家1 小时前
一次模型调用会留下什么:聚合平台的日志留存与数据边界
大数据·服务器·网络·人工智能
筝筝ba1 小时前
linux查询某个网卡是否插了对应网线的命令
linux·运维·网络
小宏运维有点菜1 小时前
Raid-1安装Ubuntu 24.04 LTS
linux·运维·服务器
姚不倒1 小时前
HAProxy 系列(二):健康检查与 SSL 卸载 — 保障可用性与性能
运维·网络·网络协议·ssl·haproxy
似水এ᭄往昔1 小时前
【QT】--常用控件(QWidget的核心属性)
服务器·开发语言·qt
2601_962100151 小时前
网络工程师的python之路pdf_网络工程师的Python之路:网络运维自动化实战
运维·自动化·实战·网络工程师·python之路
Best-Wishes1 小时前
去除vmdk linux镜像密码
linux·运维·服务器
xianyuCcCcCCCcc1 小时前
OpenStack 核心组件原理与架构深度剖析
linux·运维·openstack