1. 为什么我会写下这篇Windows VS2019编译Boost库学习记录Boost 库在 C 生态里的地位不太需要我多解释。跨平台项目、网络库、文件系统操作、智能指针、正则替换、序列化、几何计算到处都能看到它的身影。但很多刚从 Linux 转到 Windows 上做开发的同事第一件事就是被 Boost 的编译卡住。Windows 上没有 apt 一言不合敲几条命令就完事也没有官方给你发一个“下一步下一步”的安装包VS2019 里打开项目后发现一堆 .h 找不到链接时 a 库 b 库报错最后整个人都麻了。这篇记录就是写给刚上手 Windows 开发或者正准备在生产项目里引入 Boost 的朋友。我最早在 Windows 上编译 Boost 可以追溯到 VS2015 的时代之后换到 VS2019 又踩了新一轮的坑。总结下来Windows 编译 Boost 本身不难难在理解“库变体”和“工具链匹配”。你只要把 b2 的参数搞明白把“Debug/Release”和“动态运行时/静态运行时”之间的关系理顺后面就是一马平川。我这个记录会按照实际操作的顺序来写从准备源码、生成 b2到用 b2 编译、配置 VS2019 项目、遇到问题怎么排查全部都是自己验证过的内容。很多初学者问得最多的一个问题是我能不能直接用别人编好的二进制包当然能前提是你找到的压缩包和你当前的编译器版本、平台位数、运行库方式完全一致。但你的项目一旦需要某个特定 Boost 模块或者你用了很新的 Boost 版本又或者你想在 CI 上从零构建环境自己编译几乎是绕不开的。自己编译还有一个好处就是你能清楚地知道自己用的是什么版本、什么配置后续出了问题也容易复现和排查。这篇记录以 Visual Studio 2019 为例所有命令都是我在本机实际敲过的环境是 Windows 10 x64VS2019 社区版安装时勾选了“使用 C 的桌面开发”工作负载。1.1 不是每个人都有现成的预编译包可用Boost 官方目前虽然提供了部分平台的安装程序但覆盖面并没有大家想象的那么全。官方推荐的方式历来是“下载源码自己编译”Windows 下的安装程序往往只覆盖了比较老的版本更新频率也慢。更关键的是Boost 的预编译包通常只会提供“某个特定配置”下的二进制文件比如只提供动态链接、Release 版本但你的项目正好需要 Debug 版本的静态库那就只能自己动手。退一步说就算预编译包的配置刚好和你匹配它和系统的 C 运行时库版本是否一致也是个问题。VS2015 到 VS2019 的 MSVC 工具链虽然二进制兼容但不同 IDE 版本的默认行为并不完全一样。个人亲身经历直接拿别人编好的 1.69 包在 VS2019 里用Debug 下可以编译一跑就崩最后发现是对方用了不同版本的 _ITERATOR_DEBUG_LEVEL 和运行时库。所以如果你打算把 Boost 长期用在项目里真的建议花点时间自己编一次。1.2 编译Boost到底要解决哪些问题在 Windows 上编译 Boost表面上是“跑一条命令”实际上是在做三件事第一确认编译器能通过环境变量被命令行工具找到第二用 b2 工具把源码编译成多种“变体”的库文件第三把生成的头文件目录和库文件目录告诉 VS2019。这三件事彼此独立又环环相扣。头文件的问题最好理解Boost 很多库是 header-only 的比如 smart_ptr、bind、function、lexical_cast这些不需要编译只要把 boost 根目录加进 VC 包含目录就行。但另一些库例如 filesystem、regex、serialization、date_time、system必须编译成静态库或动态库才能用。此时你不仅要告诉编译器头文件在哪还要告诉链接器库文件在哪并且必须确保链接的库和你当前项目的运行库配置一致。这就是为什么我常常看到有人明明把目录配对了链接还是失败——因为他用的是 Debug 项目但附加依赖项里填的是 Release 库的名字。另外Boost 自动链接机制在 Windows 上会替你选择具体文件名的库这听起来很智能但你需要知道它的选择和你的想法可能不一致。比如你想用静态链接的 Boost但项目配置里那个宏没有改它可能给你自动找了一个动态库。我的建议是不要完全依赖自动链接后面会专门聊怎么用手动配置来彻底控制库的选择。2. 版本选择与准备工作版本这个东西最重要的原则是“别太新也别太旧”。太新的 Boost 可能刚发布社区反馈还不够多和 VS2019 的某些细节存在兼容性小问题太旧则可能缺少你需要的 API。我这里使用的是 Boost 1.77.0它在 VS2019 上编译非常顺利没有出现过令人头疼的源码语法兼容问题。如果你用的是 VS2019 15.9 以上版本1.70 到 1.78 这个区间应该都不错再新的版本我也没有逐一验证过。我把我的建议整理成了一个小表方便你按自己的需求选VS2019 小版本推荐 Boost 版本说明VS2019 16.0~16.51.69~1.72早期版本对 C17 支持不够完整存在个别编译报错VS2019 16.6~16.91.70~1.76稳定性较好我自己常用的区间VS2019 16.101.77~1.78支持较好可以放心使用实际上你只要从 Boost 官网的下载页拿一个稳定的源码包就行。下载下来的文件一般是 boost_1_77_0.zip 或者 .7z。我习惯放在一个没有空格的路径下比如D:\libs\boost_1_77_0避免以后命令行和 VS 属性页里出现各种莫名其妙的解析问题。不要小看路径空格问题它会在你使用 b2 时或者在某些生成工具读取路径时突然跳出来恶心你。所以尽量全英文、无空格路径。2.1 解压后先别急着执行 bootstrap.bat你解压完成后先看一眼目录结构。顶层会有boost文件夹、libs文件夹、tools文件夹、bootstrap.bat和b2.exe有些版本没有现成的 b2.exe只有 bootstrap.bat你需要先生成它。很多新手上来就双击bootstrap.bat如果你的系统已经打开了普通命令提示符并且当前目录在 Boost 源码根目录双击也能运行成功。但问题是这样生成的 b2 工具在后续编译时会找不到 MSVC 编译器因为普通命令提示符里没有进入 VS2019 的编译环境。正确的做法是从开始菜单里找到“x64 Native Tools Command Prompt for VS 2019”并打开然后cd /d D:\libs\boost_1_77_0再执行bootstrap.bat。这样 b2 在后续所有步骤中都能看到完整的 MSVC 工具链不会出现failed to find compiler之类的报错。如果你在开始菜单里找不到这个快捷方式说明安装 VS2019 的时候没装“使用 C 的桌面开发”工作负载或者没装英语语言包。去 Visual Studio Installer 里把它勾上装完之后再回来。不要用旧的方法手动设置INCLUDE、LIB和PATH那是 VS2015 时代的老操作容易漏项我后来再也没手动配过这些环境变量。2.2 bootstrap.bat 到底做了什么执行完bootstrap.bat你会看到目录下多出一个b2.exe。它的本质是 Boost.Build 的驱动程序。这个脚本会先检测当前的编译器环境然后编译出一个最小化的 b2 可执行文件。整个生成过程很快十几秒到一分钟不等取决于你的机器。如果这个步骤报错重点检查你是否真的在 x64 Native Tools 命令行中以及 VS2019 的 C 工具集是否安装完整。生成完 b2.exe 之后我们可以先跑一下b2.exe --version确认它能正常执行。我当时的输出类似B2 version 4.8.0 Boost.Build toolset: msvc-14.2注意这里显示的 toolset 是 msvc-14.2对应 VS2019。如果你这里显示的是比 14.2 更老或者不认识的名字多半是命令行的环境变量没加载对这时候不要急着继续先回过头去看编译器环境。2.3 预留出足够大的临时空间Boost 全库编译产生的中间文件非常大一般需要 20GB 到 40GB 磁盘空间具体看你编译多少个变体和多少库。很多人一开始只编 Boost 的几个常用库结果因为默认是全库编译磁盘爆掉进程中断。我在第一次完整编译时就遇到过No space left on device这种经验教训。所以你在编译之前最好先确认磁盘剩余空间。另外可以把中间文件目录和最终输出目录分开设置。b2 的中间文件默认放在源码目录下的bin.v2里我通常用--build-dir参数把它指到源码目录之外的临时目录这样源码目录看起来干净也方便以后直接删掉临时文件。最终输出的库文件则用--stagedir指定比如D:\libs\boost_bin下面会生成lib文件夹存放所有 .lib 和 .dll。3. b2命令行参数的拆解与一套可用的编译方案Boost 编译的核心就是b2.exe的一串参数。它们看起来密密麻麻实际上每一条都是可以直接映射到最终库文件名上的。不理解这些参数你就不知道为什么生成的库文件叫libboost_filesystem-vc142-mt-x64-1_77.lib而不是boost_filesystem.lib。理解了之后你能一眼从文件名判断出它是什么配置的产物。3.1 你一定会见到的几个参数toolsetmsvc-14.2指定使用 Visual Studio 2019 的 v142 工具集。如果你的 VS 是其他版本这里的数字会变化。architecturex86与address-model64指定目标 CPU 架构和地址模型。这里不要搞混architecturex86不是说生成 32 位而是指 x86 指令集家族配合address-model64就是生成 64 位程序。不要被名字骗了。linkstatic或linkshared生成静态库还是动态库。如果两者都要可以用逗号连接linkstatic,shared。runtime-linkstatic或runtime-linkshared决定 Boost 库如何链接 C/C 运行时库。shared对应用/MDstatic对应用/MT。threadingmulti启用多线程支持。现在基本不会再用单线程模式直接写multi就行。variantdebug,release同时生成 Debug 和 Release 两种版本。--with-库名或--without-库名限定编译模块范围。比如--with-filesystem --with-system只编这两个库。这些参数不复杂但组合起来就可能生成出十几二十种库文件。很多人不想理解只想“能编译就行”于是直接执行默认命令一次性把全库都编了。这么做也不是不行只是耗时长、占空间而且很多库你可能一年都用不上。我更推荐先限定模块后续有需要再补充编译。我们生产项目里其实用到 Boost 的地方并不算多先编 filesystem、system、regex 这几个就够了后面加库再单独补。3.2 我推荐的一套编译命令下面这条命令是我在 VS2019 下常用的完整编译配置适合绝大多数需要 Debug/Release、动态运行时的项目。我把它拆成两行写实际执行时你可以合并成一行b2.exe toolsetmsvc-14.2 address-model64 ^ --stagedirD:\libs\boost_bin ^ --build-dirD:\libs\boost_build ^ linkstatic,shared ^ runtime-linkshared ^ threadingmulti ^ variantdebug,release ^ --with-filesystem --with-system --with-date_time注意^是 Windows 命令提示符下的换行符如果你在 PowerShell 里执行换行要用反引号或者干脆全部写成一行。我给出的配置同时生成静态库和动态库、Debug 和 Release但运行库都是动态的。为什么要这么编因为 VS2019 默认的 C 项目是“多线程 DLL (/MD)”也就是动态运行时。你编译 Boost 时让runtime-linkshared就能和默认项目完全匹配。如果你希望我的项目使用“多线程 (/MT)”静态运行库那调用方项目也要在代码生成设置里改成 /MT并且这里编译时就要写runtime-linkstatic。这里有一个很容易踩的坑VS 项目里的“多线程 DLL (/MD)”对应 Boost 的“共享运行时”“多线程 (/MT)”对应 Boost 的“静态运行时”。很多同学把“静态库”和“静态运行库”搞混导致 VS 里设置/MT却去链接runtime-linkshared编出来的库最后要么链接报错要么编译通过但运行时崩溃。3.3 生成的文件命名规则编译完以后到D:\libs\boost_bin\lib下看看你会看到一堆名称特别长的文件。以 filesystem 库为例Boost 编译配置库文件名示例静态库 共享运行时 Releaselibboost_filesystem-vc142-mt-x64-1_77.lib静态库 共享运行时 Debuglibboost_filesystem-vc142-mt-gd-x64-1_77.lib静态库 静态运行时 Releaselibboost_filesystem-vc142-mt-s-x64-1_77.lib静态库 静态运行时 Debuglibboost_filesystem-vc142-mt-sgd-x64-1_77.lib动态库导入库 共享运行时 Releaseboost_filesystem-vc142-mt-x64-1_77.lib动态库 DLL shared 运行时 Releaseboost_filesystem-vc142-mt-x64-1_77.dll注意动态库的导入库没有lib前缀但其扩展名仍然是.lib。当你把linkshared的导入库加到项目里时链接器会用这个.lib定位对应的.dll。如果链接时提示找不到boost_filesystem-vc142-mt-x64-1_77.dll要把 DLL 放到 exe 所在目录或加入系统 PATH否则程序一运行就报缺少 DLL。这是 Windows 开发的特点Linux 下没有这么麻烦。静态库则全部打包在 .lib 里运行时无须额外 DLL但需要你去处理 Boost 本身的依赖关系。比如boost::filesystem会依赖boost::system所以即使你写了--with-filesystemb2 也可能会同时编出 system 库或者你在链接时仍要把 system 库也加到附加依赖项里。我在最初配置时只加了 filesystem 库结果链接一堆 unresolved external symbol后来才发现还要加 system、date_time 等依赖库。4. VS2019 项目配置与最小验证库编译好了剩下的就是让 VS2019 项目能正确找到它们。这一步涉及两类路径和一组预处理器宏。很多项目里大家习惯把 Boost 路径直接写进 VC 目录但我更推荐在项目属性里独立配置因为一个解决方案中可能有多个项目有的项目要用静态库有的要用动态库全局统一配置反而容易相互干扰。4.1 如何设置包含目录和库目录打开项目属性页选择顶部配置为“所有配置”平台为“x64”。然后进入“VC 目录”选项卡“包含目录”添加D:\libs\boost_1_77_0也就是 boost 源码根目录不是boost子目录。因为代码里通常写#include boost/format.hpp头文件查找规则是先从包含目录下找boost\format.hpp。“库目录”添加D:\libs\boost_bin\lib这是 b2 输出库文件的位置。有人可能会问既然 Boost 有自动链接机制我是不是不用在“链接器-附加依赖项”里添加任何东西Boost 某些版本的 headers 里确实会根据宏定义自动#pragma comment(lib, ...)选择正确库文件。但这要求你启用BOOST_AUTOLINK默认开了并且它解析出的名字必须刚好在库目录里存在。一旦你的 Boost 库编译配置比较特殊自动链接可能选错所以我建议关闭自动链接也就是添加BOOST_ALL_NO_LIB预处理器定义然后在“附加依赖项”里手动写清楚要链接的库文件名。这样虽然麻烦一点但每次看项目设置都能一眼知道链接了哪些东西排错也直观。4.2 最小验证代码我写了一个最小的测试工程用来验证 Boost.Filesystem 是否编译、链接、运行正常。代码很简单但能覆盖头文件查找、静态库链接、依赖库链接三个关键环节。#include boost/filesystem.hpp #include iostream int main() { boost::filesystem::path p(D:/Test); if (boost::filesystem::exists(p)) { std::cout exists: p std::endl; } else { std::cout not exists: p std::endl; } return 0; }在项目“链接器-输入-附加依赖项”里需要添加的文件名取决于你选择链接静态库还是动态库。我上面的 b2 命令同时生成了两者。假如我使用 Release 动态库则添加boost_filesystem-vc142-mt-x64-1_77.lib boost_system-vc142-mt-x64-1_77.lib如果使用 Debug 模式则把名字里的-mt-改成-mt-gd-。如果你开启了BOOST_ALL_NO_LIB漏任何依赖库编译就会在链接阶段报 unresolved external symbol。根据报错信息里的函数符号所属的 Boost 库把对应 lib 补上即可。在这条最小代码里filesystem 会用到 system 的错误处理代码所以 system 库是必需的。4.3 静态库与动态库的选择思路很多人在 Windows 下纠结选静态库还是动态库。我的建议要看你项目的发布策略。如果你的产品是直接给客户的一堆 exe 和 DLL不希望安装环境中存在一堆额外的 Boost DLL那优先用静态库。静态库虽然会让 exe 体积变大但部署省心不用担心目标机器缺少 Boost 运行库。如果你的产品本身就以多个模块 Dll 形式存在或者团队内部有多个程序共用 Boost那么使用 Boost 动态库会让总磁盘占用更小Patch 时也可能只替换 Boost DLL 即可。两种方式没有绝对优劣只是匹配你项目本身的架构风格。我最常被问到的问题里有一半人是因为项目里同时混用了静态 Boost 库和动态 Boost 动态库导致一份代码里出现两个 Boost 版本实例最终在静态变量初始化顺序、单例对象上踩坑。所以明确一个原则整个项目统一用一种链接方式不要混用。4.4 配置完直接编译运行配置好路径和依赖项后我建议先用 Debug x64 编译一次再切到 Release x64 编译一次。两次都编译通过后运行一下程序确认没有出现缺 DLL 或者崩溃。如果 Debug 能跑但 Release 出现链接错误八成是库文件名里的-gd-被漏掉或者多写了。这类“Debug/Release 不匹配”问题在 Boost 中极其常见你在 VS 错误列表里会看到类似error LNK1104: cannot open file libboost_filesystem-vc142-mt-x64-1_77.lib提示找不到的文件名是 Release 库而当前项目正是 Debug 模式明显是名字写错了。我个人的习惯是在“附加依赖项”里使用宏和条件表达式Debug 用boost_filesystem-vc142-mt-gd-x64-1_77.libRelease 用boost_filesystem-vc142-mt-x64-1_77.lib。在属性页中你可以分别给“Debug | x64”和“Release | x64”两个配置单独填写Visual Studio 会保留各自的设置。这是最稳妥的方式不需要在#pragma comment(lib)里做字符串拼接也不必担心项目配置串了。5. 编译中我碰到过的那些坑与排查方法这部分记录可能比前面的步骤更有价值。我编译 Boost 的次数不少几乎每次换环境、换版本都会冒出一些小问题。把它们写下来不是说让你完全避免踩坑而是让你看到问题时不至于慌。5.1 运行bootstrap.bat时报“Failed to build b2”这个问题几乎都出在命令提示符环境不对。如果你的命令行不是从 VS2019 的开发者命令行工具打开的系统里可能没有cl.exe的环境变量或者找不到msbuild。解决方法是打开“x64 Native Tools Command Prompt for VS 2019”然后检查cl命令是否可用。在命令行输入cl回车如果提示找不到命令说明环境没加载。另外如果你开启了代理工具或者反病毒软件它们偶尔会拦截生成的临时 exe导致 b2.exe 生成失败这种情况下把源码目录加白名单再跑一次。5.2 b2编译时出现“is not a valid identifier for parameter”出现这个报错通常是你把参数写成了/link:static或者--link static这种格式。Boost.Build 的参数语法中布尔类参数用linkstatic或--linkstatic都可以但必须保持等号形式不能写成linkstatic,shared之外的其他样式。如果你在命令行写linkstatic sharedb2 会把shared当成一个无效的命令名。回头看我的命令示例多个参数之间用空格分隔但每个参数的“key”和“value”之间一定是等号。另外当你想同时指定多个值时用逗号variantdebug,release。不要加空格加了就变成两个参数。5.3 “cannot find -lboost_system”这类链接错误这是 Linux 的 gcc 链接风格报错。Windows 的 MSVC 不会用-l语法你看到的通常是LNK1104。如果链接器说找不到boost_system-vc142-mt-x64-1_77.lib先检查这个文件到底存不存在。当初我犯过的错误是把--stagedir指向了源码目录里的stage\lib但每次重新编译时b2 默认会清空 stage 里的内容。后来我统一把--stagedir指到源码目录之外避免被意外清理。如果你在库目录下发现了同名但版本号不同的库比如1_77和1_76说明上一次编译残留没有清理干净。编译前最好把--stagedir指定目录下的旧lib文件夹手工删掉再重新编译。5.4 Debug版链接了Release版库导致的诡异崩溃这类问题非常隐蔽。程序编译链接都成功运行到某些函数时出现“访问违规”或“堆栈损坏”。发生这种情况的常见原因是 Boost 的头文件声明里某些内联函数依赖调试迭代器级别而链接的库文件中在实现时用了不同的_ITERATOR_DEBUG_LEVEL。VS2019 中 Debug 模式默认_ITERATOR_DEBUG_LEVEL2Release 默认0。如果 Debug 项目链接 Release Boost 库就可能出现这种不兼容。解决方式就是保证库版本和项目配置严格一致Debug 用带-gd-的库Release 用不带-gd-的库。这个规则比什么都重要。5.5 编译太慢怎么办Boost 全库完整编译在普通台式机上可能要三十分钟以上有些老机器两个多小时都很正常。如果只是日常使用我建议不要全库编译。你可以先执行一次b2 --with-filesystem --with-system --with-date_time ...之类的限定编译。如果后来项目中用到了别的库编译新库也很快。加-j8可以并行编译这里的 8 表示并行任务数通常设为 CPU 核数就可以。我习惯先用-j4编译限制模块确认没有源码兼容错误后再大规模并行。还有一个隐藏坑如果你机器上同时安装了 VS2019 和 VS2022b2 有可能会自动选中 VS2022 的编译器。你明明用 VS2019 打开开发者命令行但 b2 仍然检测到系统里有新版本编译器这可能造成链接时 CRT 版本不一致。解决办法是在 b2 命令中每次明确写toolsetmsvc-14.2并且检查 b2 输出的toolset行。如果真被 VS2022 抢过去可以考虑在环境变量里临时屏蔽新版本编译器路径或者在属性页中设置“平台工具集”保持为 v142。这个问题的本质是 Boost.Build 会在 PATH 中优先找最新 MSVC 工具集而非当前命令行绑定的那个。5.6 使用Boost.Asio时遇到一堆“重定义”或“宏冲突”Asio 是一个 header-only 库理论上不需要编译。但在 Windows 上使用 Asio 时如果同时引入了winsock2.h而你没有在 Boost.Asio 之前定义WIN32_LEAN_AND_MEAN就会出现winsock.h和winsock2.h的头文件重定义冲突。通常解决办法是在项目预处理器定义中加入WIN32_LEAN_AND_MEAN并且在包含 Boost.Asio 之前先包含boost/asio.hpp。这类问题不算编译 Boost 库本身的问题更像 Windows 下网络编程的经典坎但既然你的项目要配 Boost大概率会遇到所以放在这里一并提醒。6. 最后再聊一点我个人编译Boost的使用习惯编译这件事第一次是最痛苦的后面基本就成了肌肉记忆。我个人现在的流程基本固定先确认目标 VS 版本然后打开对应的 x64 Native Tools 命令行解压 Boost 源码到无空格路径执行 bootstrap 生成 b2再用一条限定模块的 b2 命令编译。整个过程大约五分钟操作剩下时间就是等编译跑完。这在 Windows 上不算复杂只要抓住工具链一致性和库变体匹配这两个核心什么问题都能迎刃而解。我在实际项目中真正长期用到的 Boost 库其实屈指可数但每次新成员加入项目我都会让他们自己动手编译一次 Boost而不是直接拷贝我给的头文件和 lib。因为自己编译过一遍才能理解后面出现的链接问题是怎么回事也才能在 CI 流水线上顺利复现构建。如果你现在正卡在某一步建议你把 b2 的完整命令先跑起来再说不要怕报错。报错并不可怕可怕的是搞不清为什么报错。最后再分享一个小技巧不要在源码目录里直接运行全库编译后就把整个源码目录打包扔给下一个同事。那个源码目录里可能残留了bin.v2、stage等几十 GB 的临时文件传到别人那里浪费磁盘和时间。我已经养成了习惯把编译产物通过--stagedir统一放到外部目录源码目录永远保持着解压后的干净状态。这样即使某个配置编错了删除外部目录重新编一次也很方便不需要重新下载源码。希望这篇文章能让你少走一点弯路也欢迎你在编译过程中遇到奇怪问题时回来对照一下大概率能找到答案。