Ubuntu下WonderTrader依赖库配置与Boost链接避坑
简介:WonderTrader 依赖库包面向需要在 Ubuntu 环境下编译 WonderTrader 的 C++ 开发者,基于 gcc 11.4.0(Ubuntu 11.4.0-1ubuntu1~22.04)预编译整理,主要用于解决第三方依赖获取繁琐、版本不匹配以及链接失败等问题。压缩包共 2000 个文件,以 1856 个 hpp 头文件和 144 个 h 头文件为主,整体约 14.5MB,内容集中于 C++ 模板实现、接口声明与兼容头,覆盖 Boost 及 JSON 解析、字符串格式化、时间日期处理等基础组件,便于直接纳入本地依赖目录,减少逐个编译依赖所耗时间。使用时可将环境变量 MyDependsGcc 指向 /deps/wtcpp,并调整 src/CMakeLists.txt 中 MyDeps 的赋值与状态提示,使构建系统准确定位依赖路径,降低路径配置和版本冲突带来的排错成本。已有 240 人学习或下载,适合量化交易系统、C++ 后端开发及需要快速复现 WonderTrader 编译环境的中高级开发者参考。
1. WonderTrader 依赖库装不上,多半出在 Boost 版本没挑对
我遇到过不止一次这样的情况:代码git clone下来,cmake跑完,make -j到 60% 时链接阶段突然中断,报错信息是undefined reference to boost::filesystem::...。查了半天才发现,系统里装着 apt 源里的旧 Boost,环境变量里又指向源码编译的另一套,CMake 在两套之间来回选择,最终链接到一个缺少符号的版本。
WonderTrader 在 Ubuntu 上属于依赖面比较宽的一类 C++ 工程:Boost 提供文件系统、线程、正则、时间等基础组件,zlib 处理压缩数据,libcurl 负责行情与交易接口的通信,OpenSSL 为 curl 提供 TLS 能力,sqlite3 支撑本地存储。只要其中一个的版本或编译选项对不上,编译期不一定报错,运行期却可能直接崩溃。这篇笔记面向第一次在 Ubuntu 上准备这套环境的人,也面向那些编译过但没跑通、想弄清楚问题出在哪一步的人。
2. 环境盘点与版本选型:Ubuntu 22.04 上 Boost 1.74 和 1.78 怎么取舍
先把机器上的东西理清楚,再动手装。这一步省下的时间,后面会以成倍的返工还回来。我习惯在干净的 Ubuntu 20.04 或 22.04 上做这套准备,24.04 也能跑,只是 apt 源里的 Boost 版本更新,部分第三方库的编译选项要跟着调整。
2.1 编译链、CMake 与系统库的最低清单
WonderTrader 是 C++11 起步的工程,但实际源码里用到了不少 C++14 特性,GCC 版本别低于 9。Ubuntu 20.04 自带 GCC 9.4,22.04 自带 GCC 11.4,两者都够用,不需要额外装新版编译器,除非你打算把整个工程静态链接分发出去。
CMake 建议 3.16 以上,Ubuntu 20.04 源里正好是 3.16.3,22.04 是 3.22。低于 3.10 的版本会在target_link_libraries的写法上报错,这对新手来说是最容易卡住的一关,因为报错信息完全不提版本问题。
基础工具链一次性装齐:
# 编译工具链与版本控制 sudo apt update sudo apt install -y build-essential cmake git pkg-config # WonderTrader 依赖的系统库 sudo apt install -y zlib1g-dev libssl-dev libcurl4-openssl-dev libsqlite3-dev # 排查时常用的工具,非必需 sudo apt install -y lsof stracebuild-essential里包含 gcc、g++、make、libc6-dev,是编译任何 C++ 工程的起点。pkg-config容易被忽略,但 libcurl 和 sqlite3 的 CMake 模块都会调用它去找头文件和库路径,缺了会导致Could NOT find CURL这类误报。libcurl4-openssl-dev和libcurl4-gnutls-dev二选一,前者对接 OpenSSL,后者对接 GnuTLS,选错了在 TLS 握手阶段会出现很隐蔽的证书校验失败,我一般固定用 openssl 版本。
装完之后确认一下版本,顺手记在笔记里:
gcc --version | head -n1 cmake --version | head -n1 pkg-config --modversion libcurl三条输出分别对应编译器、构建系统和 curl 版本。后面报错时回看这三个数字,能省掉一半的猜测。有个细节:pkg-config --modversion libcurl输出的是 curl 的版本号,不是开发包版本,两者可能不一致,看到 7.81.0 这种数字属于正常。
2.2 Boost 版本选择:apt 源里的现成包还是源码编译
这是整篇笔记里最关键的一次取舍。
apt 源里的libboost-all-dev是最省事的方案,Ubuntu 20.04 给的是 1.71,22.04 给的是 1.74。优势是十分钟装完,头文件和库路径都由 dpkg 管好,CMake 不用任何额外参数就能找到。劣势有两个:一是版本被锁死,某些第三方库对 Boost 最低版本有明确要求,1.71 可能不满足;二是 apt 包的编译选项是发行版统一决定的,_GLIBCXX_USE_CXX11_ABI这类开关跟你自己编译的模块不一定一致。
源码编译的优势是可控。我能指定版本、指定安装前缀、指定静态还是动态、指定只编译需要的几个组件。缺点是编译时间长,而且一旦路径没配好,就会出现开头说的那种「两套 Boost 互相干扰」的情况。
我的判断标准很简单:如果只是编译 WonderTrader 核心和它自带的模块,用 apt 的包大概率能跑通;如果还要接自己写的 C++ 策略模块,或者要把编译产物打包到另一台机器上,就走源码编译,并且固定版本。我一般选 1.78,这个版本在 Ubuntu 20.04 和 22.04 上都能干净编译,组件齐全,且不像 1.8x 之后那样对 C++14 有更高的强制要求。
不管选哪条路,有一条原则必须守住:一台机器上只保留一套 Boost 参与编译。装了两套就要靠 CMake 变量精确指定,不然就会出问题。
2.3 目录规划与 git 拉源码的位置
在拉代码之前,先想清楚三件事分别放在哪:依赖库安装在哪个前缀、源码放在哪个目录、编译产物输出到哪个目录。我的习惯是把它们分开:
| 用途 | 路径 | 说明 |
|---|---|---|
| 自编译依赖 | /opt/deps | Boost 等源码编译库的统一前缀 |
| 源码 | ~/src/wondertrader | 和依赖分开,方便重新 clone |
| 构建产物 | ~/build/wondertrader | 独立构建目录,不污染源码树 |
把依赖装到/opt/deps而不是/usr/local,是为了以后卸载方便。/usr/local一旦被多个版本污染,清理起来就是一场灾难,我吃过这个亏。构建目录独立则是 CMake 的标准做法,源码目录里出现一堆CMakeCache.txt之后再想切 Debug 和 Release,就得删目录重来。
拉代码这一步本身不难,难在分支和子模块:
mkdir -p ~/src && cd ~/src # 拉主仓库,--depth 1 只取最新一次提交,省带宽 git clone --depth 1 https://example.com/wondertrader.git # 进入源码目录,确认当前分支 cd wondertrader git branch -a--depth 1在只需要编译、不需要追溯历史时很实用,能省掉大量下载时间和磁盘占用。代价是后续想切到某个历史提交时要先git fetch --unshallow。git branch -a是为了确认你拉到的默认分支和文档描述一致,有的仓库默认分支不是 master,直接照抄教程会拉错。
如果仓库带子模块,还要补一条:
# 初始化并递归拉取子模块 git submodule update --init --recursive子模块的坑在于,主仓库更新了子模块指针但你没执行这条命令,编译时会出现「头文件存在但函数签名对不上」的怪异错误,因为子模块还停在旧提交上。养成 clone 完立刻执行一次的习惯,能避免很多说不清的问题。
3. 从零装依赖:Boost 源码编译与系统库补齐
这一章是整套环境的主体。顺序不能乱:先系统库,再 Boost,最后才是 WonderTrader 本身。顺序颠倒的后果是,你会在编译主工程时反复撞到「找不到某个符号」,然后再回头补装,前面的构建缓存全部作废。
3.1 先补齐系统库:zlib、curl、openssl、sqlite3
apt 那条命令已经装了开发包,但装完之后要逐项验证,不能假设它一定成功。验证方式是用pkg-config查版本和编译参数:
# 逐个确认依赖是否可被 pkg-config 识别 for p in zlib openssl libcurl sqlite3; do echo "== $p ==" pkg-config --modversion "$p" 2>/dev/null || echo "NOT FOUND" done循环里每项输出一个版本号,任何一项打印NOT FOUND,就说明开发包没装上或者.pc文件路径没进PKG_CONFIG_PATH。zlib 的 pc 文件名叫zlib,openssl 的叫openssl,sqlite3 的叫sqlite3,curl 的叫libcurl,名字写错也会报 NOT FOUND。
pkg-config --cflags libcurl和--libs libcurl分别给出编译和链接参数,手工排查链接错误时非常有用。比如 CMake 报找不到 curl,直接跑这两条就能判断是库本身缺失,还是 CMake 的查找路径有问题。
这里补充说明 sqlite3 的角色:WonderTrader 的本地数据落盘依赖它,缺了之后编译能过,但运行时会抛出无法打开数据库的错误,属于典型的「编译期无感、运行期翻车」。我一般会在装完之后直接用命令行建一个临时库验证:
# 建一个临时 sqlite 库并写入一行,验证库可用 sqlite3 /tmp/_wt_check.db "create table t(id int); insert into t values(1); select * from t;" rm -f /tmp/_wt_check.db输出一行1就说明 sqlite3 的库和命令行工具都正常。
3.2 源码编译 Boost:bootstrap 和 b2 参数逐条说
决定走源码编译,就把 Boost 压缩包下到/opt/src下面解压。目录结构建议是/opt/src/boost_1_78_0解压出源码,安装前缀用/opt/deps/boost-1.78,版本号写进路径,将来换版本就是换一个目录,互不干扰。
第一步执行 bootstrap,生成 b2 构建工具:
cd /opt/src/boost_1_78_0 # 指定安装前缀,只生成需要的几个组件的构建配置 ./bootstrap.sh \ --prefix=/opt/deps/boost-1.78 \ --with-libraries=system,filesystem,thread,chrono,atomic,regex,date_time--prefix决定b2 install的落地位置。--with-libraries是关键的性能参数:Boost 全量编译上百个组件,动辄半小时以上,而 WonderTrader 实际用到的就是文件系统、线程、时间、原子操作、正则、日期这几块。裁剪之后编译时间能压到几分钟。少写了某个组件,后面链接才会报错,所以这份清单要按实际用到的头文件来定,宁可多带一个date_time也别漏。
第二步执行 b2 编译安装:
# -j 并行度按 CPU 核数走,link 同时产出静态库和动态库 ./b2 install \ -j"$(nproc)" \ link=static,shared \ threading=multi \ variant=release \ --layout=system \ --build-dir=/tmp/boost-build逐个参数说清楚。-j$(nproc)用满所有核心,nproc会自动读取 CPU 核数,比手工写-j8靠谱,换机器不用改。link=static,shared同时产出.a和.so,后期排查问题时两种都能试。threading=multi生成多线程版本,单线程版本在链接时会缺符号。variant=release只编 release,如果你需要调试 Boost 内部逻辑才加 debug,否则链接时 CMake 可能优先选中 debug 库,导致运行时性能和预期不符。
--layout=system这个参数值得单独讲。Boost 默认用 versioned 布局,产出的库文件名带上编译器标签和版本号,比如libboost_filesystem-gcc11-mt-x64-1.78.so。这种名字 CMake 的 FindBoost 模块能识别,但你手工写-lboost_filesystem就找不到。改成 system 布局后产出的是干净的libboost_filesystem.so,二进制分发时也更友好。代价是同一前缀下不能并存多个 Boost 版本,所以前缀里带上版本号是必须的。
--build-dir把中间产物扔到/tmp,源码目录保持干净,将来重新编译不用先b2 --clean-all。
提示:
b2 install中途失败(比如磁盘满、内存不足被 OOM Killer 杀掉),重新执行前建议先rm -rf /tmp/boost-build,残留的中间状态会让第二次编译出现莫名其妙的错误。
3.3 让链接器找到 Boost:ldconfig 与 CMake 变量
安装完成不等于能被找到。动态库要进链接器的搜索路径和运行时的加载路径,这是两件不同的事,很多人只做了前者。
先看链接期。CMake 找 Boost 靠的是BOOST_ROOT和Boost_NO_SYSTEM_PATHS两个变量,前者指向前缀,后者告诉 FindBoost 别去/usr/include瞎找。这两个变量在配置阶段传,不写进任何配置文件,好处是换版本只改命令行:
# 配置时精确指定 Boost 位置,避免找到系统里的旧版本 cmake -S ~/src/wondertrader -B ~/build/wondertrader \ -DCMAKE_BUILD_TYPE=Release \ -DBOOST_ROOT=/opt/deps/boost-1.78 \ -DBoost_NO_SYSTEM_PATHS=ON \ -DBoost_USE_STATIC_LIBS=OFFBoost_USE_STATIC_LIBS=OFF强制用动态库。静态库在 curl 和 OpenSSL 的依赖链上容易出问题,因为静态库里不包含它自己依赖的符号,链接顺序稍有不对就是一堆 undefined reference。动态库把依赖关系交给运行时处理,排错成本低得多。
再看运行期。动态库装到/opt/deps/boost-1.78/lib之后,系统加载器默认不认识这个路径。有两种做法,一种是写/etc/ld.so.conf.d/再ldconfig,另一种是在启动脚本里设LD_LIBRARY_PATH。我倾向前者用于常驻环境,后者用于临时验证:
# 注册动态库搜索路径,一次写入长期生效 echo "/opt/deps/boost-1.78/lib" | sudo tee /etc/ld.so.conf.d/wt-boost.conf sudo ldconfig # 确认加载器已经能看到目标库 ldconfig -p | grep boost_filesystemldconfig -p的输出里能看到库名和完整路径,如果搜不到,说明前面的安装或路径配置有问题,别急着编译主工程。这一步验证花十秒,能省掉后面半小时的困惑。
注意:
ldconfig缓存更新后,已经启动的进程不会重新加载。改完路径要重启相关进程,否则还是旧的加载结果。
3.4 git 拉取 WonderTrader 与依赖顺序确认
源码到手之后别急着make。先花两分钟看目录结构,确认依赖是以源码形式内嵌在仓库里,还是要求外部提供。WonderTrader 的仓库里有独立的依赖目录,通常放的是 Windows 下预编译好的第三方库,Linux 下走的是系统库或者你自编译的库,这两条路径要分清楚。
# 查看仓库顶层结构,确认依赖目录和构建脚本位置 ls -1 ~/src/wondertrader # 查看构建脚本对 Boost 的引用方式 grep -rn "find_package(Boost" ~/src/wondertrader --include=CMakeLists.txt第二条命令是关键。输出会告诉你工程要求 Boost 的哪些组件、最低版本是多少。如果输出的组件列表比你在--with-libraries里带的更全,现在补编译还来得及;如果版本要求高于你装的,只能换版本重来。这个检查比任何文档都准,因为它是构建系统真正执行的逻辑。
拉取时机上还有一个小技巧:先把依赖全部装完并通过验证,再 clone 源码,可以避免源码长时间放着、回头发现拉的不是最新提交。这个顺序不是硬性规定,但从减少返工的角度看更省心。
4. 构建排序:先验证依赖可用,再编译 WonderTrader 主工程
编译阶段最容易犯的错是「一口气跑完」。依赖装完直接make -j16,出错了只看最后几行日志,然后开始猜。正确的做法是把构建拆成可验证的小步,每一步都有明确的成功判据。
4.1 依赖顺序:为什么不能一上来就编译主工程
CMake 的配置阶段只检查头文件和库是否存在,编译阶段只保证语法通过,真正暴露依赖问题的是链接阶段。这意味着即使配置和编译都过了,也可能在最后一步全盘失败。所以我会在正式编译之前,先写一个十几行的测试程序把 Boost 和 curl 都用一遍,确认它们能被正确找到和链接。
// /tmp/boost_probe.cpp —— 最小依赖探针,验证头文件与链接 #include <boost/filesystem.hpp> #include <boost/thread.hpp> #include <curl/curl.h> #include <zlib.h> #include <cstdio> int main() { // filesystem: 检查当前目录是否存在 bool ok = boost::filesystem::exists("."); // curl: 取版本字符串,验证库能正常初始化 const char* cv = curl_version(); // zlib: 取版本号,验证 zlib 链接正常 const char* zv = zlibVersion(); std::printf("fs=%d curl=%s zlib=%s\n", ok, cv, zv); return 0; }这个探针的价值在于把「能不能链接」这件事从几千行工程里剥离出来。编译命令要显式带上所有依赖:
# 编译探针,路径和库名按自己的安装前缀调整 g++ -std=c++14 /tmp/boost_probe.cpp -o /tmp/boost_probe \ -I/opt/deps/boost-1.78/include \ -L/opt/deps/boost-1.78/lib \ -lboost_filesystem -lboost_system -lboost_thread -lpthread \ -lcurl -lz-I指定头文件搜索路径,-L指定库搜索路径,-l按依赖顺序列出。注意-lboost_filesystem必须排在-lboost_system前面,因为前者依赖后者,链接器从左到右解析,顺序反了就是 undefined reference。这个细节在 CMake 里由target_link_libraries自动处理,手工编译时得自己记住。
跑通之后输出类似fs=1 curl=7.81.0 zlib=1.2.11,说明三个核心依赖都没问题。探针挂在这一步,比在 WonderTrader 里挂掉好排查十倍。
4.2 CMake 配置项与 Release/Debug 取舍
正式配置主工程时,参数要写全。CMake 有一个很容易被忽略的行为:缓存。第一次配置用的变量会写进CMakeCache.txt,第二次改命令行参数时,如果某个变量没被显式覆盖,它会沿用缓存里的旧值。所以换 Boost 路径或者切构建类型,最稳的做法是删掉构建目录重来。
# 清理旧构建目录,避免缓存干扰 rm -rf ~/build/wondertrader # 完整配置,显式指定所有关键变量 cmake -S ~/src/wondertrader -B ~/build/wondertrader \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/opt/wondertrader \ -DBOOST_ROOT=/opt/deps/boost-1.78 \ -DBoost_NO_SYSTEM_PATHS=ON \ -DBoost_USE_STATIC_LIBS=OFF \ -DCMAKE_CXX_STANDARD=14 \ -DCMAKE_CXX_FLAGS="-O2 -fPIC"CMAKE_BUILD_TYPE=Release会追加-O3 -DNDEBUG,对量化这类计算密集的场景影响明显。CMAKE_INSTALL_PREFIX指定make install的落地位置,装到/opt/wondertrader而不是默认的/usr/local,理由和前面一样:可卸载、可并存。
-fPIC是给动态库用的。如果你后面要把 WonderTrader 的模块编译成.so供其他程序加载,非 PIC 编译的目标文件在链接时会报 relocation 错误,提示信息是recompile with -fPIC。这个编译选项加上去没有坏处,代价是寄存器占用略增,性能损失在小数点后第二位,可以忽略。
CMAKE_CXX_STANDARD=14是硬性约束。工程里用到了泛型 lambda 和std::make_unique,标准低于 14 直接编译失败,而 CMake 默认可能是 11,报错信息里只会说某个语法不认识。
配置成功后,摘要输出里会列出找到的 Boost 版本和库路径,逐个核对一遍:
# 从缓存里读回关键变量做核对 grep -E "Boost_(INCLUDE_DIR|LIBRARY_DIR|VERSION)" ~/build/wondertrader/CMakeCache.txtBoost_INCLUDE_DIR应该是/opt/deps/boost-1.78/include,Boost_LIBRARY_DIR应该是/opt/deps/boost-1.78/lib。任何一个指向/usr/include或/usr/lib/x86_64-linux-gnu,说明系统里那套旧 Boost 还在干扰,得把Boost_NO_SYSTEM_PATHS的作用范围再确认一次。
4.3 编译、安装与产物的推演
配置通过之后才是编译。这一步反而简单,但并行度要给够:
# 并行编译,日志同时写文件,方便出错回看 cmake --build ~/build/wondertrader -j"$(nproc)" 2>&1 | tee /tmp/wt-build.log # 只看错误行,过滤掉大量的编译进度输出 grep -nE "error:|Error [0-9]" /tmp/wt-build.log | head -n 302>&1 | tee这个组合是为了同时看屏幕和留档。make -j的报错是交错的,多个任务同时失败时错误行会混在一起,事后 grep 日志比在终端往回滚屏靠谱得多。grep只取前 30 条错误,是因为第一个错误往往会引发连锁反应,后面的几十条都是它的衍生品,看多了反而抓不住重点。
编译通过后执行安装:
# 安装到指定前缀 cmake --install ~/build/wondertrader # 确认关键产物落地 ls -1 /opt/wondertrader/bin /opt/wondertrader/lib产物目录里应该有可执行文件和动态库。如果bin是空的但构建没报错,多半是安装规则没配全,检查 CMakeLists 里的install(TARGETS ...)段落。这种情况不算常见,但遇到了要知道往哪看。
5. 避坑与排查:五条从链接失败里换来的经验
下面五条都是我在真实环境里撞过的,按发生频率排序。每条按现象、原因、解决三段写,遇到相似的报错可以对照着定位。
5.1 现象:undefined reference to boost::filesystem 一串符号
原因是链接顺序或者库根本不匹配。CMake 报这个错时,先看它到底链接了哪个路径的 Boost。最快的判断方式是打开详细输出:
# 显示完整链接命令行,看 Boost 库的真实路径 cmake --build ~/build/wondertrader --verbose 2>&1 | grep -m1 "boost_filesystem"输出里如果出现/usr/lib/x86_64-linux-gnu/libboost_filesystem.so,说明找到的是系统包,你自编译的那套没生效。解决方式是删掉构建目录,重新配置时把BOOST_ROOT和Boost_NO_SYSTEM_PATHS一起带上。只写前者不写后者,FindBoost 仍然会优先搜系统路径。
还有一种更隐蔽的情况:库找到了,版本也对,但符号确实不存在。那是因为--with-libraries里漏了某个组件,比如漏了chrono而工程里用了boost::chrono。补编译对应组件即可,不用全量重来。
5.2 现象:CMake 配置成功,运行时报 cannot open shared object file
这个错误发生在运行期,不是编译期。原因在于ldconfig没配或者配了没生效。先用ldd确认:
# 查看可执行文件的动态库依赖,找 not found 的项 ldd /opt/wondertrader/bin/<可执行文件名> | grep -i "not found"有not found就说明加载器找不到。解决方式是补/etc/ld.so.conf.d/里的路径再ldconfig,或者临时export LD_LIBRARY_PATH=/opt/deps/boost-1.78/lib:$LD_LIBRARY_PATH验证。临时方案能跑通、写成配置文件后跑不通,说明 conf 文件里的路径写错了,或者ldconfig没执行。
ldd的输出还会暴露另一类问题:同一个库被加载了两个不同路径的版本。这在混装 apt 包和源码包时特别常见,症状是运行一段时间后随机崩溃,因为没有崩溃栈可看,只能靠ldd | sort | uniq -d反查。
5.3 现象:curl 相关符号缺失,报 SSL_ 开头的一串
原因是 curl 和 OpenSSL 的链接关系断了。Ubuntu 上libcurl4-openssl-dev和libcurl4-gnutls-dev是互斥的,装了后者,curl 底层走 GnuTLS,但工程里如果直接调用了 OpenSSL 的 API,符号就对不上。检查方式:
# 查看当前安装的 curl 开发包是哪个后端 dpkg -l | grep libcurl | grep dev输出里应该是libcurl4-openssl-dev。如果是 gnutls 版本,先卸掉再装 openssl 版本,然后pkg-config --libs libcurl的输出里会带上-lssl -lcrypto。缺了这两个,链接时 curl 内部引用的 SSL 函数就找不到实现。
5.4 现象:编译报 GLIBCXX 版本不够,或者 ABI 相关的奇怪错误
_GLIBCXX_USE_CXX11_ABI是 C++11 之后引入的开关,值为 1 时std::string用新的实现(带 SSO),为 0 时用旧的。GCC 5 以后默认是 1。问题出在跨编译器或者跨预编译库的场景:你用 GCC 11 编译,但链接的某个第三方.a是用 GCC 4.8(ABI=0)编的,运行时std::string的内存布局对不上,表现是随机崩溃,甚至可能表现为数据乱码。
判断方法是看报错符号名里有没有__cxx11:
# 检查目标文件里的符号用的是哪套 ABI nm -C /opt/wondertrader/lib/<某个库>.so | grep -m5 "std::__cxx11"如果自己的库里有__cxx11而第三方库里没有,就是 ABI 混用了。解决只有两条路:要么把第三方库用同样的编译器重编,要么统一给所有代码加-D_GLIBCXX_USE_CXX11_ABI=0。后者是权宜之计,长期看还是重编更干净。
5.5 现象:b2 编译到一半被 killed,或者磁盘写满
这是资源问题,不是配置问题。b2 install -j$(nproc)在 Boost 全量编译时峰值内存能到几个 G,小内存机器直接触发 OOM Killer。日志里会看到Killed或者c++: fatal error: Killed signal terminated program cc1plus。
解决方式是把并行度降下来,或者干脆串行:
# 内存紧张时降低并行度,牺牲时间换稳定 ./b2 install -j4 link=static,shared threading=multi variant=release --layout=system磁盘方面,Boost 全量编译中间产物能占 10G 以上。用--build-dir=/tmp/boost-build把中间产物挪到/tmp是个好习惯,但/tmp如果是 tmpfs 挂在内存上,反而加剧内存压力。这种情况改成指向磁盘上的普通目录。编译前df -h看一眼剩余空间,比编到一半失败再回头查省事。
6. 装完怎么验证:三个自检把「编译通过」变成「确实能跑」
编译成功只证明语法和链接没问题,不证明这套环境能用。我习惯在交付前跑一遍下面三段自检,都过了才算装完。
第一段是依赖清单自检,把每个依赖的版本和路径打出来存档:
# 依赖快照,出问题时对比这份记录最快 { echo "== 系统 =="; lsb_release -ds echo "== 编译器 =="; gcc --version | head -n1 echo "== Boost =="; grep -E "BOOST_LIB_VERSION" /opt/deps/boost-1.78/include/boost/version.hpp echo "== curl =="; pkg-config --modversion libcurl echo "== 动态库 =="; ldconfig -p | grep -c boost } | tee ~/wt-env-snapshot.txtBOOST_LIB_VERSION这个宏在version.hpp里,输出的字符串形如1_78,比路径名更可信,因为路径是可以随便起的。最后一行统计系统里能看到的 boost 库数量,数字明显偏大说明有多套并存。
第二段是最小链接测试,就是前面那个探针程序,编译并运行:
# 探针编译并运行,输出三个版本号 g++ -std=c++14 /tmp/boost_probe.cpp -o /tmp/boost_probe \ -I/opt/deps/boost-1.78/include -L/opt/deps/boost-1.78/lib \ -lboost_filesystem -lboost_system -lboost_thread -lpthread -lcurl -lz \ && /tmp/boost_probe能打印出fs=1和一串版本号,说明头文件、库文件、运行时加载三件事全通。
第三段是目标程序启动验证。这一步不是看能不能长期运行,而是看启动阶段有没有立刻报错:
# 前台启动,观察前若干秒的输出,Ctrl+C 退出 cd /opt/wondertrader/bin LD_LIBRARY_PATH=/opt/deps/boost-1.78/lib ./<可执行文件名> 2>&1 | head -n 40重点看有没有cannot open shared object、symbol lookup error、database is locked这三类。前两类是依赖问题,第三类多半是 sqlite3 的路径或权限问题。启动日志能过 40 行还没异常,基本可以认为环境是通的。
| 自检项 | 通过判据 | 不通过时的第一检查点 |
|---|---|---|
| 依赖快照 | 五个版本号齐全且符合预期 | pkg-config --modversion |
| 探针链接 | 输出 fs=1 与版本字符串 | 链接顺序与-L路径 |
| 启动验证 | 前 40 行无加载类错误 | ldd与ldconfig -p |
这三段我固定按顺序跑,任何一段不过就不往下走。以前图快跳过探针直接启动主程序,结果在几百行日志里翻了半小时才定位到一个库路径写错,从那以后我每次装新机器都强制先跑探针再碰主工程,宁可多花两分钟。希望这套顺序能帮你少走一遍我走过的弯路。
本文还有配套的精品资源,点击获取