简介面向 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视情况部署环境已有同版本时可 aptcurlHTTP 客户端libcurl4-openssl-dev可 apt注意 OpenSSL 版本匹配hiredisRedis 客户端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 和 makecmake负责后续工程配置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 \ linkstatic \ runtime-linkshared \ threadingmulti \ variantrelease \ cxxflags-fPIC \ -j$(nproc)--prefix决定头文件和库文件装到哪里后面 CMake 的BOOST_ROOT要指向同一路径。--with-*只编译需要的组件减少编译时间如果上层工程还用到 regex 或 program_options再追加对应项。linkstatic表示 Boost 库本身编成静态库runtime-linkshared表示运行时链接系统 C/C 运行时这是常见组合如果上层要求全静态把runtime-link改成static但要确认 glibc 静态链接的边界。threadingmulti对应多线程场景variantrelease只编 release调试时需要另编 debug 或改用variantdebug,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_TYPERelease \ -DCMAKE_INSTALL_PREFIX$DEP_PREFIX \ -DCMAKE_POSITION_INDEPENDENT_CODEON \ -DCMAKE_CXX_STANDARD17 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_CODEON对应-fPIC凡是可能被共享库链接的组件都建议打开。CMAKE_CXX_STANDARD17是否必须取决于组件要求如果依赖库仓库没有明确要求不要随意抬高抬高后可能和上层工程的编译标准冲突。安装完成后用一条命令检查前缀里的目录结构确认include、lib、bin都存在find $DEP_PREFIX -maxdepth 2 -type d | sort正常应看到include下按库名分目录lib下是.a或.sobin下是可执行工具。如果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_TYPERelease \ -DCMAKE_PREFIX_PATH$DEP_PREFIX \ -DBOOST_ROOT$DEP_PREFIX \ -DBoost_NO_SYSTEM_PATHSONCMAKE_PREFIX_PATH是 CMake 查找包的总入口find_package会按这个路径下的lib/cmake、share/cmake等位置搜索配置。BOOST_ROOT专门给 FindBoost 模块用指向 Boost 的安装前缀。Boost_NO_SYSTEM_PATHSON的作用是阻止 FindBoost 去/usr下找系统 Boost避免前面说的混用。三个参数同时给看起来重复但各自作用范围不同CMAKE_PREFIX_PATH管所有包BOOST_ROOT管 BoostBoost_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 foundldd输出里出现not found就是运行时找不到库先看缺失的库名再回到DEP_PREFIX/lib下确认文件是否存在。如果文件存在但ldd仍找不到检查文件名是否带版本号、是否有软链接断裂。注意不要用sudo ldconfig把独立前缀塞进系统库搜索路径。这样做会污染全局环境其他项目可能加载到不该加载的版本。优先用 RPATH 或临时LD_LIBRARY_PATH。4.3 编译验证从最小 target 到完整工程依赖接好后不要直接编整个 WonderTrader。先编一个最小 target确认头文件、库文件、链接顺序都正确再逐步扩大范围。最小 target 可以是一个只包含 Boost 头文件和一次boost::filesystem调用的可执行文件也可以先用工程里已有的小工具 target。命令上先跑配置再跑单 targetcmake -S . -B build \ -DCMAKE_BUILD_TYPERelease \ -DCMAKE_PREFIX_PATH$DEP_PREFIX \ -DBOOST_ROOT$DEP_PREFIX \ -DBoost_NO_SYSTEM_PATHSON 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_PATHSON配置。不要用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_PATHSON -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再跑readelfldd ./verify_boost | grep boost readelf -d ./verify_boost | grep -E RPATH|RUNPATH ./verify_boostldd输出里 Boost 库应指向$DEP_PREFIX/lib不能出现/usr/libreadelf应能看到 RPATH 或 RUNPATH 包含该路径。如果ldd显示not found回到第 4 章的 RPATH 设置如果版本宏和库文件名对不上说明头文件和二进制库不是同一套必须回到依赖前缀重新编译不要试图用软链接掩盖。这套验证我一般会固化成脚本放在依赖库仓库根目录换机器先跑一遍再编主工程。脚本里只做三件事检查$DEP_PREFIX/include/boost/version.hpp是否存在用上面的命令编译并运行验证程序最后用ldd过滤系统路径。从那以后我每次换构建机都强制走一遍这个自检宁可多花两分钟也不要在完整工程链接到一半时才发现 Boost 混用。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?