10、离线构建模式:预下载依赖包、搭建本地镜像仓库、使用file://协议、企业级离线方案
说实话,做C++项目最怕什么?不是编译报错,而是网络不通。
我记得有一次在客户现场部署,他们的服务器完全隔离,连不上外网。CMake一执行FetchContent,直接卡死。那场面,真是尴尬到脚趾抠地。从那以后,我就把离线构建当成必修课来研究了。
今天咱们就聊聊,怎么在没有网络的环境下,照样把依赖管理玩得转。
为什么需要离线构建?
你想想看,企业级开发环境往往有严格的安全策略。生产服务器、内网开发机,很多都是物理隔离的。这时候FetchContent默认的在线下载模式,就成了最大的绊脚石。
说白了,离线构建要解决三个核心问题:
- 依赖包从哪里来 —— 提前准备好,别临时去网上下
- 怎么让CMake找到它们 —— 告诉FetchContent本地路径
- 团队怎么共享 —— 搭建一个内部仓库,大家都能用
方案一:预下载依赖包 + file://协议
这是最直接的办法。我个人的习惯是,在联网环境下先把所有依赖下载好,打包成一个压缩包。到了离线环境,解压后用file://协议指向本地路径。
来看个例子:
# 在线环境:先下载依赖
# 假设我们依赖 fmt 和 spdlog
# 手动下载源码包到 /opt/deps/ 目录
# 离线环境:使用 file:// 协议
FetchContent_Declare(
fmt
URL file:///opt/deps/fmt-10.0.0.tar.gz
URL_HASH SHA256=xxxxxxxxxxxxxxxxxxxx
)
FetchContent_Declare(
spdlog
URL file:///opt/deps/spdlog-1.12.0.tar.gz
URL_HASH SHA256=yyyyyyyyyyyyyyyyyyyy
)
FetchContent_MakeAvailable(fmt spdlog)
这里有个坑,我踩过好几次:file://协议后面跟的是绝对路径。如果你写相对路径,CMake会找不到文件。嗯,路径前面一定要加三个斜杠,比如file:///home/user/deps/xxx.tar.gz。
CMAKE_SOURCE_DIR 变量来拼接路径,这样项目迁移时不用改代码。
set(MY_DEPS_DIR "${CMAKE_SOURCE_DIR}/dependencies")
FetchContent_Declare(
fmt
URL file://${MY_DEPS_DIR}/fmt-10.0.0.tar.gz
)
方案二:搭建本地镜像仓库
预下载方案适合单机或小团队。但如果你有几十个开发人员,每人手动下载一遍依赖,那效率就太低了。
更好的做法是:在公司内网搭一个HTTP服务器,把依赖包都放上去。这样所有开发机都能通过http://协议访问,体验和在线一样,但数据完全在内网流通。
我推荐用 Nginx 或者 Python的http.server 来搭,几分钟就能搞定:
# 用 Python 快速启动一个文件服务器
# 在存放依赖包的目录下执行
python3 -m http.server 8080
# 然后 CMake 里这样写
FetchContent_Declare(
fmt
URL http://192.168.1.100:8080/fmt-10.0.0.tar.gz
URL_HASH SHA256=xxxxxxxxxxxxxxxxxxxx
)
FETCHCONTENT_BASE_URL 变量,所有依赖都走内网。
方案三:使用本地Git仓库
有些依赖是Git仓库,不是压缩包。这时候可以用 GIT_REPOSITORY 指向本地镜像:
# 先在内网 Git 服务器上镜像一份
# git clone --mirror https://github.com/example/lib.git
# CMake 里指向内网地址
FetchContent_Declare(
mylib
GIT_REPOSITORY git@internal-git:mirrors/lib.git
GIT_TAG v1.2.3
)
我个人比较推荐这种方式。因为Git自带校验,不容易出错。而且更新依赖时,只需要同步一次镜像,所有开发机都能受益。
核心逻辑:离线构建的决策流程
下面这张图,是我总结的离线构建选择路径。你可以根据团队规模和环境,快速找到适合自己的方案:
企业级离线方案的最佳实践
我在一家半导体公司待过,他们的构建环境是这样的:
- 统一依赖仓库:所有第三方库都放在内网的Nexus上,按项目分目录
- 版本锁定:每个项目都有一个
dependencies.json文件,记录所有依赖的精确版本和哈希值 - 自动化同步:每周自动从外网同步最新版本到内网仓库
- CI/CD集成:流水线里直接配置
FETCHCONTENT_BASE_URL指向内网仓库
总结一下
离线构建其实不复杂,核心就是一句话:把依赖从「在线获取」变成「本地获取」。
- 单机用
file://协议,简单直接 - 小团队搭个HTTP服务器,省心省力
- 企业上制品仓库,规范可控
不管你选哪种方案,记得把依赖的版本和哈希值写死在CMake里。这样不管环境怎么变,构建结果都是一致的。嗯,这就是我理解的「可复现构建」的精髓。