简介:面向 C++ 量化交易框架 WonderTrader 的预编译依赖库合集,适配 Ubuntu 22.04 与 GCC 11.4.0 环境,主要解决换机重编第三方库耗时长、版本难对齐的问题,适用于量化研究、回测系统部署与多机环境一致性维护等场景。压缩包共 2000 个文件、约 14.5MB,由 1856 个 hpp 与 144 个 h 头文件组成,涵盖 Boost、格式化输出、JSON 解析、字符编码转换等组件,并保留各库完整的头文件目录层级,可直接作为 include 路径挂载使用。资源描述中给出了通过环境变量 MyDependsGcc 指向 /deps/wtcpp,并在 src/CMakeLists.txt 中改写 MyDeps 变量、同步调整状态提示输出的具体做法,读者照此修改即可跳过逐库编译环节,把时间留给策略编写与回测验证。已有 240 人浏览学习,适合需要快速复现编译环境的中高级 C++ 开发者参考。
1. 在 Ubuntu 上编译 WonderTrader 卡在依赖库:先看清 Boost 与 git 子模块这两道坎
在 Ubuntu 上从源码构建 WonderTrader 时,最先让人停下来的一般不是策略逻辑,而是 WonderTrader 依赖库:CMake 报找不到 Boost,或者找到了却链接不上;git clone 下来的目录里子模块是空的;运行时又提示找不到动态库。这份依赖库面向的就是这类场景,把 WonderTrader 需要的第三方库按可复现的方式拉取、编译、安装到独立前缀,再交给上层工程消费。它适合需要在本机或构建机上从源码搭起 WonderTrader 的开发者,也适合要把编译过程固化成脚本的运维同学;如果你只用预编译产物,或者只关心策略层接口,这份依赖库的收益不会太明显。
2. 依赖库清单与选型逻辑:Boost、yaml-cpp、spdlog 各自承担什么
2.1 从 git 仓库看依赖分层:核心库、工具库、通信库
WonderTrader 依赖库不是一个单一大库,而是一组第三方组件的集合。把它按角色拆开,后面编译和排错会顺很多。我一般分三层看:第一层是核心基础库,Boost 的 system、filesystem、thread、chrono、date_time 这几个组件最常被上层直接或间接引用;第二层是工具库,yaml-cpp 负责配置解析,spdlog 和 fmt 负责日志与格式化,sqlite3、hiredis 这类按模块需要引入;第三层是通信与网络库,czmq、zmq、curl 等在不同部署形态下出现。分层不是为了好看,而是决定编译顺序和链接顺序:基础库先装,工具库其次,通信库最后,CMake 查找时也按这个顺序排查。
拿到依赖库仓库后,先别急着编译,先用 git 看清目录结构和子模块状态。常见做法是仓库根目录放一个thirdparty或deps目录,每个依赖一个子目录,部分目录通过 git submodule 引入。命令不需要复杂,关键是确认子模块是否真的拉下来了:
# 进入依赖库仓库根目录后执行 git submodule status # 如果输出行首是 "-",说明子模块未初始化 git submodule update --init --recursivegit submodule status的输出行首字符很关键:-表示未初始化,+表示提交号与记录不一致,空格开头才是正常。git submodule update --init --recursive会按.gitmodules里的记录逐层拉取,--recursive不能省,因为有些依赖自己还带子模块。这里不需要记具体提交号,也不要去改子模块的 remote 地址;依赖库仓库已经把可用组合固定下来,乱换分支是后面 ABI 翻车的常见起点。
提示:如果
git submodule update很慢,先检查仓库根目录的.gitmodules是否完整,而不是急着换源。子模块路径写错时,命令不会报“找不到”,只会留下空目录,后面 CMake 才会以“头文件缺失”的形式暴露出来。
2.2 为什么 Boost 版本不能随便升:ABI 与头文件搜索顺序
Boost 是 WonderTrader 依赖库里最容易出问题的一环。Ubuntu 的 apt 源里通常有一个发行版自带的 Boost,版本与依赖库仓库期望的不一定一致。直接apt install libboost-all-dev看起来省事,但会带来两个隐患:一是头文件搜索顺序不可控,系统路径/usr/include和独立前缀下的include同时存在时,编译器可能先命中系统头文件,再链接到独立前缀的库,编译期和链接期用的不是同一套 Boost;二是 ABI 差异,Boost 某些组件在不同版本之间会调整符号或默认特性,上层工程编译通过、运行时崩溃,查起来像黑匣子。
我一般不建议在依赖库构建阶段依赖系统 Boost,而是把 Boost 编译到独立前缀,例如$HOME/.local/wondertrader-deps或项目目录下的deps/install。这样做的代价是首次编译时间长,收益是路径可控、可复制、换机器时不用猜系统里装了什么。判断是否踩了混用坑,可以看编译命令里-I的顺序:独立前缀的include必须排在/usr/include前面;链接时-L也要把独立前缀的lib放在前面。CMake 工程里则看Boost_INCLUDE_DIR和Boost_LIBRARY_DIRS是否指向同一个前缀。
注意:不要用“先 apt 装一个,再源码装一个”的方式并行维护两套 Boost。短时间内可能都能编过,但一旦某个 target 的 include 路径被缓存,后面很难定位到底用了哪一套。
2.3 用 apt 还是源码编译:一张对照表定取舍
依赖库里的组件并不都需要从源码编译。判断标准不是“源码更高级”,而是“版本是否被上层工程约束、是否需要特定编译选项、是否要装到独立前缀”。下面这张表是我在 Ubuntu 上常用的取舍方式,具体包名以当前发行版为准,表里只写角色,不写版本号。
| 组件 | 在 WonderTrader 里的角色 | 常见 apt 包 | 是否建议源码构建 | 理由 |
|---|---|---|---|---|
| Boost | 文件系统、线程、时间、系统错误码 | libboost-all-dev | 建议 | 版本与 ABI 需要和上层一致,独立前缀可控 |
| yaml-cpp | 解析 YAML 配置 | libyaml-cpp-dev | 视情况 | 系统版本满足要求时可 apt,否则源码 |
| spdlog / fmt | 日志与格式化 | libspdlog-dev、libfmt-dev | 视情况 | 头文件库居多,注意 fmt 的 ABI 兼容 |
| sqlite3 | 本地数据存储 | libsqlite3-dev | 可 apt | 接口稳定,系统包通常够用 |
| czmq / zmq | 消息通信 | libczmq-dev、libzmq3-dev | 视情况 | 部署环境已有同版本时可 apt |
| curl | HTTP 客户端 | libcurl4-openssl-dev | 可 apt | 注意 OpenSSL 版本匹配 |
| hiredis | Redis 客户端 | libhiredis-dev | 可 apt | 接口简单,系统包足够 |
这张表不是让你全部照抄,而是给一个决策顺序:先看上层工程有没有明确要求某个组件必须用源码前缀,再看系统包版本是否能和 Boost 的编译选项对齐。Boost 放第一行建议源码,是因为它同时涉及头文件和二进制库,混用成本最高。其他组件如果系统包能编过、运行时不报符号缺失,就不必为了“统一”全部重编;全部源码编译会把构建时间拉长,也会让依赖关系更难排查。
3. 在 Ubuntu 上从零拉取并构建依赖:git 分支、编译参数与安装前缀
3.1 准备编译环境:apt 包与 CMake 版本检查
Ubuntu 上构建这套依赖,先要把编译工具链补齐。不要一上来就 clone,缺pkg-config或libssl-dev时,CMake 会在某个子依赖里报一个看起来毫不相关的错。下面这组包覆盖了常见场景:编译器、CMake、git、pkg-config、压缩库、SSL、Python 头文件。具体包名在不同发行版略有差异,Ubuntu 下一般可以直接用:
sudo apt update sudo apt install -y \ build-essential \ cmake \ git \ pkg-config \ libssl-dev \ zlib1g-dev \ libbz2-dev \ liblzma-dev \ python3-dev \ autoconf \ automake \ libtool这段命令的逻辑是先把“能编译 C/C++ 工程”和“能处理压缩包、SSL、Python 扩展”的基础能力装齐。build-essential提供 gcc/g++ 和 make,cmake负责后续工程配置,pkg-config用于查找系统库的编译参数,libssl-dev和几个压缩库是 curl、Boost 等组件的常见依赖,autoconf、automake、libtool则用于少数带 autotools 构建脚本的依赖。装完后不要只看命令有没有报错,还要确认 CMake 能正常执行:
cmake --version g++ --version pkg-config --version三条命令分别确认 CMake、编译器、pkg-config 可用。CMake 版本不需要追求最新,但不要低于依赖库仓库说明的最低要求;如果系统自带的 CMake 太旧,常见做法是用官方二进制包或源码装到独立前缀,而不是直接覆盖/usr/bin/cmake。覆盖系统 CMake 会影响其他项目,后面排错时又多一个变量。
3.2 用 git 拉取依赖仓:目录布局与子模块处理
依赖库通过 git 分发时,建议固定一个工作目录,不要把仓库散落在不同路径。我的习惯是在$HOME/work下建一个deps-src放源码,再建一个deps-install放安装结果,两者分开,清理源码不影响已安装产物。下面命令里的仓库地址用占位符表示,实际使用时换成依赖库提供的地址即可:
# 目录规划:源码与安装前缀分离 export WORK_ROOT="$HOME/work" export DEP_SRC="$WORK_ROOT/deps-src" export DEP_PREFIX="$WORK_ROOT/deps-install" mkdir -p "$DEP_SRC" "$DEP_PREFIX" # 拉取依赖库仓库,注意 --recursive git clone --recursive <依赖库仓库地址> "$DEP_SRC/wondertrader-deps" cd "$DEP_SRC/wondertrader-deps" # 确认子模块状态,行首为 "-" 的需要初始化 git submodule statusgit clone --recursive会在克隆主仓库后立即拉取子模块,比先 clone 再补submodule update少一步。DEP_SRC和DEP_PREFIX分开的好处是:源码目录可以随时删掉重拉,安装前缀里的头文件和库文件不受影响;反过来,想换一套编译参数时,换一个DEP_PREFIX就能并行存在两套依赖,不会互相覆盖。git submodule status用来确认子模块是否完整,如果输出里有-开头,执行git submodule update --init --recursive补拉。
提示:不要把
DEP_PREFIX设成/usr/local之后又用sudo安装。独立前缀的意义就是不需要提权,所有文件归当前用户,后面 CMake 查找和删除都简单。用sudo装进去,清理时容易误删系统文件。
3.3 Boost 的编译参数:b2 的 link、runtime-link、cxxflags 怎么定
Boost 的编译是这套依赖里最值得单独说的一步。进入 Boost 源码目录后,先执行./bootstrap.sh生成b2,再用b2 install安装。参数不需要背全,关键是四个:安装前缀、链接方式、运行时链接方式、线程模型。下面是一组常用配置,具体组件按上层工程需要增减:
cd "$DEP_SRC/wondertrader-deps/thirdparty/boost" # 生成 b2,指定安装前缀 ./bootstrap.sh --prefix="$DEP_PREFIX" # 编译并安装指定组件 ./b2 install \ --prefix="$DEP_PREFIX" \ --with-system \ --with-filesystem \ --with-thread \ --with-chrono \ --with-date_time \ link=static \ runtime-link=shared \ threading=multi \ variant=release \ cxxflags="-fPIC" \ -j"$(nproc)"--prefix决定头文件和库文件装到哪里,后面 CMake 的BOOST_ROOT要指向同一路径。--with-*只编译需要的组件,减少编译时间;如果上层工程还用到 regex 或 program_options,再追加对应项。link=static表示 Boost 库本身编成静态库,runtime-link=shared表示运行时链接系统 C/C++ 运行时,这是常见组合;如果上层要求全静态,把runtime-link改成static,但要确认 glibc 静态链接的边界。threading=multi对应多线程场景,variant=release只编 release,调试时需要另编 debug 或改用variant=debug,release。cxxflags="-fPIC"在依赖要被编进共享库时很重要,缺了它后面链接共享库会报重定位错误。
-j"$(nproc)"用满 CPU 核数,编译时间能明显缩短。Boost 编译失败时不要只看最后一行,往上翻到第一个error:,常见原因是缺少libbz2-dev、liblzma-dev或 Python 头文件;如果只编 system/filesystem/thread 这几个组件,这些依赖通常不会触发,但一旦加上 iostreams 或 python,就要把对应的开发包补齐。
3.4 安装到独立前缀:其他库的 CMake 构建与路径规划
Boost 之外,依赖库里通常还有一批用 CMake 构建的组件。它们的构建套路比较统一:指定源码目录、构建目录、安装前缀,开启位置无关代码,然后 build 和 install。下面以某个 CMake 组件为例,实际目录名按仓库结构替换:
cd "$DEP_SRC/wondertrader-deps/thirdparty/<组件目录>" cmake -S . -B build \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX="$DEP_PREFIX" \ -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ -DCMAKE_CXX_STANDARD=17 cmake --build build -j"$(nproc)" cmake --install build-S . -B build把源码和构建产物分开,后面清理只删build目录,不碰源码。CMAKE_INSTALL_PREFIX必须和 Boost 的前缀一致,这样最终所有依赖都在同一个DEP_PREFIX下,CMake 查找时只需要设一个CMAKE_PREFIX_PATH。CMAKE_POSITION_INDEPENDENT_CODE=ON对应-fPIC,凡是可能被共享库链接的组件都建议打开。CMAKE_CXX_STANDARD=17是否必须取决于组件要求,如果依赖库仓库没有明确要求,不要随意抬高,抬高后可能和上层工程的编译标准冲突。
安装完成后,用一条命令检查前缀里的目录结构,确认include、lib、bin都存在:
find "$DEP_PREFIX" -maxdepth 2 -type d | sort正常应看到include下按库名分目录,lib下是.a或.so,bin下是可执行工具。如果lib为空,说明安装步骤没有真正执行,或者cmake --install的权限/前缀写错。这个检查比直接去编上层工程更快,能在依赖阶段把问题截住。
4. 把依赖接进 WonderTrader 构建:CMake 查找、链接与运行时库路径
4.1 CMakeLists 里 find_package 的搜索顺序与 BOOST_ROOT
依赖装到独立前缀后,上层工程的 CMake 需要知道去哪里找。最直接的方式是在配置命令里传CMAKE_PREFIX_PATH,而不是改系统环境变量。下面是一段常见的配置命令,DEP_PREFIX沿用上一章的路径:
cd <WonderTrader 工程目录> cmake -S . -B build \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_PREFIX_PATH="$DEP_PREFIX" \ -DBOOST_ROOT="$DEP_PREFIX" \ -DBoost_NO_SYSTEM_PATHS=ONCMAKE_PREFIX_PATH是 CMake 查找包的总入口,find_package会按这个路径下的lib/cmake、share/cmake等位置搜索配置。BOOST_ROOT专门给 FindBoost 模块用,指向 Boost 的安装前缀。Boost_NO_SYSTEM_PATHS=ON的作用是阻止 FindBoost 去/usr下找系统 Boost,避免前面说的混用。三个参数同时给看起来重复,但各自作用范围不同:CMAKE_PREFIX_PATH管所有包,BOOST_ROOT管 Boost,Boost_NO_SYSTEM_PATHS是保险丝。
如果工程使用find_package(Boost REQUIRED COMPONENTS system filesystem thread chrono),配置成功后可以在CMakeCache.txt里核对几个变量:Boost_INCLUDE_DIR、Boost_LIBRARY_DIRS、Boost_SYSTEM_LIBRARY_RELEASE。它们应该全部指向$DEP_PREFIX下,不能出现/usr/include或/usr/lib。一旦发现混入系统路径,先删掉build目录再重配,不要手动改缓存变量;缓存里可能还留着上一次的搜索结果,手改容易留下不一致状态。
4.2 动态库运行时路径:RPATH 与 LD_LIBRARY_PATH 的取舍
编译链接通过不代表运行通过。依赖库如果编成了共享库,运行时动态链接器需要找到它们。两种常见做法:设置LD_LIBRARY_PATH,或者在可执行文件里写入 RPATH。LD_LIBRARY_PATH适合临时调试,缺点是每次开终端都要设,容易忘;RPATH 写进二进制,部署时更稳,但要注意路径是绝对路径还是相对路径。CMake 里可以用CMAKE_INSTALL_RPATH控制:
# 在顶层 CMakeLists.txt 中设置安装 RPATH set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib") set(CMAKE_BUILD_WITH_INSTALL_RPATH OFF) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)$ORIGIN表示可执行文件所在目录,$ORIGIN/../lib适合可执行文件在bin、库在lib的布局。CMAKE_BUILD_WITH_INSTALL_RPATH OFF让构建期使用构建树路径,安装后再替换成安装 RPATH,避免构建期和安装期路径混淆。CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE会把链接时用到的库路径追加进 RPATH,适合依赖不在标准位置的场景。如果只是本机调试,临时用环境变量更快:
export LD_LIBRARY_PATH="$DEP_PREFIX/lib:${LD_LIBRARY_PATH}" ldd build/<可执行文件> | grep "not found"ldd输出里出现not found就是运行时找不到库,先看缺失的库名,再回到DEP_PREFIX/lib下确认文件是否存在。如果文件存在但ldd仍找不到,检查文件名是否带版本号、是否有软链接断裂。
注意:不要用
sudo ldconfig把独立前缀塞进系统库搜索路径。这样做会污染全局环境,其他项目可能加载到不该加载的版本。优先用 RPATH 或临时LD_LIBRARY_PATH。
4.3 编译验证:从最小 target 到完整工程
依赖接好后,不要直接编整个 WonderTrader。先编一个最小 target,确认头文件、库文件、链接顺序都正确,再逐步扩大范围。最小 target 可以是一个只包含 Boost 头文件和一次boost::filesystem调用的可执行文件,也可以先用工程里已有的小工具 target。命令上先跑配置,再跑单 target:
cmake -S . -B build \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_PREFIX_PATH="$DEP_PREFIX" \ -DBOOST_ROOT="$DEP_PREFIX" \ -DBoost_NO_SYSTEM_PATHS=ON cmake --build build --target <最小target> -j"$(nproc)"--target指定只编一个目标,失败时错误信息聚焦,不会淹没在几十个编译单元里。最小 target 通过后,再编完整工程:
cmake --build build -j"$(nproc)"完整编译阶段如果出现undefined reference,先看链接行里库的顺序。CMake 通常会自动处理依赖顺序,但手写target_link_libraries时,被依赖的库要放在依赖它的库后面;Boost 的system经常需要放在filesystem、thread后面。这个顺序问题在最小 target 里不容易暴露,完整工程链接多个库时才会出现。
5. 避坑与排查:Boost 头文件混用、git 子模块为空、链接顺序翻车
5.1 现象:编译报 boost/version.hpp 找不到
现象很直接:编译某个源文件时提示fatal error: boost/version.hpp: No such file or directory,但明明已经装过 Boost。原因通常不是没装,而是 CMake 或编译命令里的 include 路径没有指到独立前缀,或者指到了但排在系统路径后面。Ubuntu 上/usr/include是默认搜索路径,如果$DEP_PREFIX/include没有出现在编译命令里,编译器不会主动去那里找。另一种情况是装 Boost 时--prefix写错,头文件实际落在别处。
解决分三步:先确认文件存在,ls "$DEP_PREFIX/include/boost/version.hpp";再确认 CMake 缓存里的Boost_INCLUDE_DIR是否指向该目录;最后看编译命令里-I的顺序。如果缓存不对,删掉build目录,重新用-DBOOST_ROOT="$DEP_PREFIX" -DBoost_NO_SYSTEM_PATHS=ON配置。不要用export CPLUS_INCLUDE_PATH硬顶,那个变量会影响所有编译过程,后面更难排查。
5.2 现象:链接时 undefined reference 到 boost::system
编译能过,链接报undefined reference to boost::system::system_category()或类似符号。原因通常是只包含了 Boost 头文件,没有链接对应的二进制库,或者链接顺序不对。Boost.System 在较新版本里可能是头文件实现的一部分,但依赖库如果按静态库方式编译,仍然需要显式链接boost_system。另一种常见原因是 CMake 的target_link_libraries里写了Boost::filesystem但漏了Boost::system,而filesystem内部依赖system。
解决方式是检查find_package(Boost REQUIRED COMPONENTS system filesystem thread chrono)是否包含system,并在target_link_libraries里按依赖顺序写:Boost::filesystem Boost::thread Boost::system。如果是手写-l,把-lboost_system放在-lboost_filesystem后面。改完不要只增量编译,链接错误有时是旧对象文件残留,先清理目标再重链。
5.3 现象:git clone 后子模块目录为空
git clone成功,进入依赖库目录却发现某个thirdparty/xxx是空的,或者只有一个.git文件。原因是仓库用了 git submodule,而克隆时没有加--recursive。子模块不会随主仓库自动拉取内容,只会在主仓库里留下一个提交号记录。CMake 配置时不会提示“子模块为空”,只会报某个头文件找不到,容易误判成安装不完整。
解决命令是git submodule update --init --recursive,在依赖库根目录执行。执行后再用git submodule status确认行首不再是-。如果某个子模块反复拉取失败,先检查.gitmodules里的路径是否与本地目录一致,再检查网络和权限;不要直接删掉子模块目录重新git clone主仓库,那样会丢掉主仓库记录的提交号。
5.4 现象:运行时找不到 libboost_*.so
编译链接都通过,运行可执行文件时报error while loading shared libraries: libboost_system.so...: cannot open shared object file。原因是动态链接器运行时找不到共享库,编译期的-L路径不会自动带进运行时。常见于把 Boost 编成共享库、安装到独立前缀,但没有设置 RPATH 或LD_LIBRARY_PATH。
解决方式优先用 RPATH:在 CMake 里设置CMAKE_INSTALL_RPATH,重新安装可执行文件,再用readelf -d <可执行文件> | grep RPATH确认路径写入。临时调试可以用export LD_LIBRARY_PATH="$DEP_PREFIX/lib:$LD_LIBRARY_PATH",然后ldd检查是否还有not found。如果ldd显示库找到了但版本不对,检查$DEP_PREFIX/lib下是否有多个版本共存,软链接是否指向正确文件。
5.5 现象:CMake 找到系统 Boost 而不是指定前缀
配置阶段没有报错,但编译时用的头文件来自/usr/include,链接的库来自/usr/lib。原因是 CMake 缓存里保留了上一次FindBoost的搜索结果,或者Boost_NO_SYSTEM_PATHS没有生效。CMakeCache.txt是罪魁祸首的概率很高,尤其是先在系统装过 Boost、后来又指定独立前缀的场景。
解决方式是删掉整个build目录,重新配置,并在命令行显式带上-DBOOST_ROOT="$DEP_PREFIX" -DBoost_NO_SYSTEM_PATHS=ON -DCMAKE_PREFIX_PATH="$DEP_PREFIX"。配置后打开CMakeCache.txt搜Boost_INCLUDE_DIR和Boost_LIBRARY_DIR,确认没有/usr路径。如果仍然混入,检查环境变量里是否有BOOST_ROOT或BOOST_INCLUDEDIR指向系统路径,环境变量的优先级有时会覆盖命令行参数。
6. 进阶验证:用一个小程序确认依赖 ABI 与线程库链接
依赖库装完、工程编过,并不等于这套组合在运行时一定稳。我习惯在换机器或换依赖前缀后跑一个最小验证程序,专门检查 Boost 的头文件版本、二进制库版本和线程链接是否一致。程序不需要复杂,能调用boost::filesystem和boost::system::error_code就够了:
// verify_boost.cpp #include <boost/version.hpp> #include <boost/filesystem.hpp> #include <boost/system/error_code.hpp> #include <iostream> int main() { std::cout << "Boost version: " << BOOST_LIB_VERSION << std::endl; boost::system::error_code ec; boost::filesystem::path p = boost::filesystem::temp_directory_path(ec); if (ec) { std::cerr << "temp_directory_path failed: " << ec.message() << std::endl; return 1; } std::cout << "temp dir: " << p.string() << std::endl; return 0; }这段代码只做两件事:打印编译期看到的 Boost 版本,调用一次文件系统接口触发boost_system的运行时符号。BOOST_LIB_VERSION是头文件里的宏,能和ldd看到的库版本做对照;temp_directory_path带error_code参数,避免异常路径干扰。编译时显式指定前缀,不要依赖系统默认路径:
g++ verify_boost.cpp -o verify_boost \ -I"$DEP_PREFIX/include" \ -L"$DEP_PREFIX/lib" \ -lboost_filesystem \ -lboost_system \ -Wl,-rpath,"$DEP_PREFIX/lib" \ -pthread-I和-L都指向独立前缀,-Wl,-rpath把运行时路径写进二进制,-pthread对应 Boost.Thread 的线程模型。编译后先跑ldd,再跑readelf:
ldd ./verify_boost | grep boost readelf -d ./verify_boost | grep -E "RPATH|RUNPATH" ./verify_boostldd输出里 Boost 库应指向$DEP_PREFIX/lib,不能出现/usr/lib;readelf应能看到 RPATH 或 RUNPATH 包含该路径。如果ldd显示not found,回到第 4 章的 RPATH 设置;如果版本宏和库文件名对不上,说明头文件和二进制库不是同一套,必须回到依赖前缀重新编译,不要试图用软链接掩盖。
这套验证我一般会固化成脚本,放在依赖库仓库根目录,换机器先跑一遍再编主工程。脚本里只做三件事:检查$DEP_PREFIX/include/boost/version.hpp是否存在,用上面的命令编译并运行验证程序,最后用ldd过滤系统路径。从那以后我每次换构建机都强制走一遍这个自检,宁可多花两分钟,也不要在完整工程链接到一半时才发现 Boost 混用。希望帮到你。
本文还有配套的精品资源,点击获取