ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Windows平台gRPC C++库手动编译指南:从源码到VS2019项目集成

Windows平台gRPC C++库手动编译指南:从源码到VS2019项目集成 1. 项目缘起为什么要在Windows上手动编译gRPC如果你是一个在Windows平台上用Visual Studio 2019搞C开发的工程师并且项目里需要用到gRPC那你大概率会遇到一个灵魂拷问我是直接用vcpkg或者NuGet安装预编译的二进制包还是自己动手从源码编译官方文档和很多教程会告诉你用vcpkginstall grpc一条命令搞定这确实是最快的方式。但我在实际接手一个对性能和依赖有严格要求的跨平台服务项目时发现这条路走不通。预编译的库要么版本对不上要么链接时出现诡异的ABI不兼容问题要么就是需要定制编译选项比如关闭异常、使用静态CRT、链接特定的OpenSSL版本。这时候从源码编译就成了唯一可靠的选择。手动编译gRPC听起来就像是个“脏活累活”网上教程零零散散坑却一个不少。很多人卡在环境配置、第三方依赖下载失败或者最终生成的lib/dll无法在自己的VS2019工程里正常链接。这个过程本质上是在Windows上复现一套类Unix的构建流程gRPC主要使用CMake和Ninja需要处理好Visual Studio的工具链、Windows特有的路径问题以及网络环境对子模块下载的影响。今天我就把最近一次在Windows 10 VS2019环境下成功编译gRPC C库静态/动态Debug/Release的完整过程、核心原理和踩过的所有坑给你彻底讲明白。目标不只是让你能编译通过更要让你理解每一步背后的原因下次遇到问题自己能解决。2. 编译前的战略准备工具链与源码的抉择在敲下任何命令之前正确的准备工作能避免一半的失败。编译gRPC不是单纯地git clone然后cmake你需要一个清晰的工具链地图和源码获取策略。2.1 核心工具链的安装与验证首先确保你的系统满足以下基础条件这不仅仅是“有就行”版本和配置是关键Visual Studio 2019这是我们的编译器和IDE环境。必须安装“使用C的桌面开发”工作负载。重点在于单个MSVC工具集版本。打开VS2019 Installer确保你安装了例如“MSVC v142 - VS 2019 C x64/x86 生成工具”。避免同时安装多个VS版本如VS2017、VS2022的编译器这可能导致CMake生成器选择错误。验证方法打开“Developer Command Prompt for VS 2019”输入cl应能看到类似“Microsoft (R) C/C Optimizing Compiler Version 19.xx.xxxxx for x64”的输出。CMakegRPC的构建系统。版本必须≥ 3.13。建议直接从官网下载最新稳定版安装程序如3.28并勾选“Add CMake to the system PATH for all users”。安装后在命令行输入cmake --version验证。一个关键细节CMake的安装路径最好不要有空格或中文虽然现代CMake处理能力增强了但一些老的第三方脚本可能仍有问题。Git用于克隆源码和其庞大的子模块。同样需要添加到系统PATH。建议使用git version 2.x。在后续克隆时我们将使用--recursive参数这是gRPC源码树包含abseil-cpp、c-ares、re2、protobuf等子模块所必需的。Ninja推荐或Visual Studio Generator这是CMake的“生成器”。Ninja是一个专注于速度的小型构建系统比VS Solution编译更快命令行操作也更简洁。你可以通过pip install ninja安装或从GitHub releases页面下载二进制文件放到PATH中。我们将主要使用Ninja。当然你也可以直接用-G Visual Studio 16 2019生成.sln解决方案文件然后在VS IDE里编译但那会失去一些灵活性且编译时间更长。Active Perl 和 Go可选但常备编译某些依赖如OpenSSL如果你选择从源码编译它或工具如protobuf的测试套件时需要。虽然gRPC的CMake脚本有时能自动下载预编译的工具但提前安装可以避免网络超时导致的失败。可以从Strawberry Perl和Go官网下载安装。2.2 源码获取浅克隆与深度克隆的权衡gRPC的仓库很大因为它包含了所有子模块。直接git clone https://github.com/grpc/grpc.git会下载大量历史记录。推荐的做法是使用浅克隆并指定分支这能节省大量时间和磁盘空间git clone --recurse-submodules -b v1.60.0 --depth 1 https://github.com/grpc/grpc.git cd grpc--recurse-submodules这是核心参数必须加上。它会同时初始化并更新仓库中定义的子模块如third_party/abseil-cpp, third_party/protobuf等。没有这个后续cmake会失败。-b v1.60.0指定一个稳定的发布版本分支。强烈建议不要使用master分支它可能处于不稳定状态。去GitHub Releases页面选择一个与你项目兼容的版本如1.60.0, 1.59.0等。--depth 1只克隆最近一次提交而不是整个历史。如果克隆子模块时网络失败特别是从GitHub克隆abseil-cpp等这是最常见的问题。你可以尝试配置Git代理如果你有稳定代理。分步操作先git clone -b v1.60.0 --depth 1 https://github.com/grpc/grpc.git然后进入目录手动修改.gitmodules文件将url https://github.com/abseil/abseil-cpp.git中的https改为git协议git://github.com/abseil/abseil-cpp.git有时git协议更快。然后再执行git submodule update --init --recursive。终极方案如果某个子模块始终无法下载可以去该子模块的仓库如abseil-cpp单独下载Release的zip包解压到third_party/abseil-cpp目录下并确保目录结构正确通常解压后是一个带版本号的文件夹需要重命名或调整。2.3 编译目录规划保持源码干净永远不要在源码目录内直接进行编译即“in-source build”。这会把生成的中间文件、目标文件污染源码树难以清理。标准的做法是“out-of-source build”D:\Projects\ ├── grpc\ # 源码目录 (只读) │ ├── CMakeLists.txt │ ├── include\ │ └── third_party\ └── grpc_build\ # 编译输出目录 (可随意删除) ├── x64_vs2019_debug\ # 一种配置 ├── x64_vs2019_release\ # 另一种配置 └── install\ # 最终安装头文件和库的位置我们将在grpc_build下为不同的配置如Debug/Release动态库/静态库创建独立的子目录。3. CMake配置实战参数详解与生成器选择进入核心的CMake配置阶段。这里每一步的选择都直接影响最终生成的库文件是否能在你的项目里用起来。3.1 基础CMake命令与关键参数解析打开“Developer Command Prompt for VS 2019”导航到你的编译输出目录例如D:\Projects\grpc_build\x64_vs2019_release_static。然后执行CMake配置命令。下面是一个功能齐全的示例我们逐行拆解cmake -G Ninja ^ -DCMAKE_BUILD_TYPERelease ^ -DCMAKE_INSTALL_PREFIX./install ^ -DgRPC_SSL_PROVIDERpackage ^ -DgRPC_ZLIB_PROVIDERpackage ^ -DgRPC_CARES_PROVIDERpackage ^ -DgRPC_RE2_PROVIDERpackage ^ -DgRPC_ABSL_PROVIDERpackage ^ -DgRPC_PROTOBUF_PROVIDERpackage ^ -DgRPC_BUILD_GRPC_NODE_PLUGINOFF ^ -DgRPC_BUILD_GRPC_OBJECTIVE_C_PLUGINOFF ^ -DgRPC_BUILD_GRPC_PHP_PLUGINOFF ^ -DgRPC_BUILD_GRPC_PYTHON_PLUGINOFF ^ -DgRPC_BUILD_CSHARP_EXTOFF ^ -DCMAKE_MSVC_RUNTIME_LIBRARYMultiThreadedDLL ^ -DProtobuf_USE_STATIC_LIBSON ^ -DBUILD_SHARED_LIBSOFF ^ ../grpc-G Ninja指定使用Ninja生成器。如果你想生成VS的.sln文件则用-G Visual Studio 16 2019 -A x64。但Ninja在命令行编译效率上完胜。-DCMAKE_BUILD_TYPERelease这是单配置生成器如Ninja, Make必需的参数指定构建类型为Release。对于多配置生成器如Visual Studio这个参数无效而是在编译时用--config Release指定。-DCMAKE_INSTALL_PREFIX./install指定make install或ninja install时的安装路径。编译完成后执行安装命令所有头文件、库文件、CMake配置文件都会复制到这个目录方便你集成到自己的项目。-DgRPC_*_PROVIDERpackage这是最关键的一组参数。它告诉gRPC的构建系统如何获取依赖。package表示让CMake通过find_package()在系统上寻找已安装的库比如你用vcpkg安装的。另一个选项是module表示从源码目录third_party里编译。这里我强烈建议对于abseil、c-ares、re2、zlib使用package并提前用vcpkg安装它们。原因在于gRPC自带的这些子模块版本可能较旧且从源码编译会极大增加整体编译时间和复杂度。唯独-DgRPC_PROTOBUF_PROVIDER可以设为module因为gRPC和Protobuf版本绑定紧密用自带的版本最保险。-DgRPC_BUILD_GRPC_*_PLUGINOFF关闭你不需要的语言插件。如果你只用C那么把Node.js、Objective-C、PHP、Python、C#这些插件全部关掉能显著减少编译目标加快速度。-DCMAKE_MSVC_RUNTIME_LIBRARYMultiThreadedDLL指定使用动态链接的MSVC运行时库/MD 或 /MDd。这是为了与你之后自己的项目设置匹配。如果你的项目要求静态链接运行时库/MT则改为MultiThreaded。必须保持gRPC库与你应用程序的运行时库设置一致否则链接时会出现“_ITERATOR_DEBUG_LEVEL”不匹配等致命错误。-DProtobuf_USE_STATIC_LIBSON当使用module方式提供protobuf时此选项控制编译出的是静态库libprotobuf.lib还是动态库protobuf.dll。这里设为ON编译静态库。-DBUILD_SHARED_LIBSOFF全局控制gRPC核心库如grpc、grpc的链接类型。OFF表示构建静态库.libON表示构建动态库.dll。选择静态库可以简化部署但会增大最终可执行文件的体积。动态库则需要随程序分发dll。3.2 依赖管理vcpkg的巧妙集成如前所述将abseil等依赖的PROVIDER设为package是优化编译体验的秘诀。而vcpkg是Windows上管理C库依赖的绝佳工具。安装vcpkg如果尚未安装git clone https://github.com/microsoft/vcpkg.git cd vcpkg .\bootstrap-vcpkg.bat .\vcpkg integrate install # 将vcpkg集成到VS2019和CMake安装所需依赖.\vcpkg install abseil:x64-windows c-ares:x64-windows re2:x64-windows zlib:x64-windows openssl:x64-windows这会为x64架构的Windows安装动态库版本。如果你需要静态库使用x64-windows-static三元组注意静态库链接更复杂需要处理一堆_DEBUG定义。在CMake命令中告知vcpkg工具链 为了让CMake能找到vcpkg安装的包需要在CMake命令中附加vcpkg的工具链文件cmake -G Ninja ^ -DCMAKE_TOOLCHAIN_FILED:/vcpkg/scripts/buildsystems/vcpkg.cmake ^ ... (其他参数同上) ... ../grpc这样当CMake遇到find_package(absl REQUIRED)时就会自动去vcpkg的目录里找而不是搜索系统路径。3.3 配置过程常见错误与解决执行CMake命令后你会看到大量输出。成功的话最后会显示“Configuring done”和“Generating done”。如果失败常见原因有找不到GitCMake在配置初期可能需要Git信息。确保git在PATH中。下载失败即使设置了packageCMake在配置protobuf或某些工具时仍可能尝试下载预编译文件尤其是protoc。错误信息通常包含“Downloading... failed”。解决方案手动下载。根据错误日志中的URL用浏览器或下载工具下载对应文件放到CMake缓存目录通常是C:\Users\[你的用户名]\AppData\Local\Temp或源码目录下的cmake\build子目录中它期望的位置。有时需要关闭杀毒软件或防火墙。vcpkg集成问题如果提示找不到abseil等包检查-DCMAKE_TOOLCHAIN_FILE路径是否正确以及vcpkg是否确实安装了对应架构的包。可以用.\vcpkg list查看已安装的包。4. 编译、安装与项目集成全流程配置成功后你会在编译目录下看到build.ninja文件如果使用Ninja。4.1 执行编译与安装编译所有目标ninja或者如果你只想编译特定目标如grpcninja grpc这个过程会消耗一些时间并占用大量CPU和内存。编译成功后进行安装ninja install这会将所有必要的文件复制到CMAKE_INSTALL_PREFIX指定的目录本例中是./install。查看该目录结构通常如下install/ ├── bin/ # 可执行文件如grpc_cpp_plugin.exe, protoc.exe ├── include/ # 头文件如grpcpp/, google/protobuf/, absl/ └── lib/ # 库文件如grpc.lib, grpc.lib, libprotobuf.lib, abseil的.lib文件等4.2 在你的VS2019项目中集成现在你可以在自己的C项目中使用了。假设你的项目使用CMake集成变得非常简单。在你的项目CMakeLists.txt中cmake_minimum_required(VERSION 3.13) project(MyGrpcApp) set(CMAKE_CXX_STANDARD 17) # 关键告诉CMake去哪里找我们编译安装的gRPC set(gRPC_ROOT D:/Projects/grpc_build/x64_vs2019_release_static/install) list(APPEND CMAKE_PREFIX_PATH ${gRPC_ROOT}) # 查找包 find_package(gRPC CONFIG REQUIRED) find_package(Protobuf CONFIG REQUIRED) find_package(absl CONFIG REQUIRED) # 添加你的可执行文件 add_executable(my_app main.cpp) # 链接库 target_link_libraries(my_app PRIVATE gRPC::grpc gRPC::grpc gRPC::grpc gRPC::gpr protobuf::libprotobuf absl::base absl::strings # ... 其他需要的absl组件 ) # 如果你有.proto文件使用protobuf的函数生成代码 protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS my_proto.proto) target_sources(my_app PRIVATE ${PROTO_SRCS} ${PROTO_HDRS})如果你的项目不使用CMake而是直接用VS2019的.sln/.vcxproj则需要手动配置包含目录在项目属性 - C/C - 常规 - 附加包含目录中添加install/include。库目录在链接器 - 常规 - 附加库目录中添加install/lib。附加依赖项在链接器 - 输入 - 附加依赖项中添加grpc.lib;grpc.lib;gpr.lib;libprotobuf.lib;以及所有abseil的.lib文件如absl_base.lib等。注意Debug配置要链接带d后缀的库如grpcd.lib。预处理器定义确保你的项目运行时库设置/MD, /MDd, /MT, /MTd与编译gRPC时使用的CMAKE_MSVC_RUNTIME_LIBRARY设置完全一致。将protoc和grpc_cpp_plugin添加到路径为了编译.proto文件你需要将install/bin目录添加到系统的PATH环境变量或者在项目预生成事件中调用绝对路径。4.3 编译中的典型错误排查即使CMake配置成功编译过程中也可能出错。C1189错误无法打开源文件“openssl/ssl.h”这通常是因为OpenSSL未找到。即使你通过vcpkg安装了OpenSSLgRPC的CMake脚本也可能因为路径问题找不到。确保-DgRPC_SSL_PROVIDERpackage并且vcpkg的OpenSSL已安装。你还可以显式指定OpenSSL路径-DOPENSSL_ROOT_DIRD:/vcpkg/installed/x64-windows。链接错误 LNK2005/LNK1169符号重复定义这通常是因为链接了冲突的库。例如既链接了静态的abseil库你的项目或其他依赖又包含了abseil的头文件实现某些abseil组件是header-only的但有些不是。确保所有依赖的链接类型一致全静态或全动态并且没有重复包含库。编译错误 C语言标准不兼容gRPC需要C11或更高标准。确保你的项目属性中“C语言标准”设置为“ISO C17标准”或更高。ninja: build stopped: subcommand failed.这是一个通用错误需要向上滚动日志找到第一个真正的错误通常是红色的C编译错误或链接错误。重点关注错误发生前的最后几条信息。5. 高级话题定制化编译与性能考量当你掌握了基础编译流程后可以根据项目需求进行深度定制。5.1 编译静态库与动态库的抉择前文我们用-DBUILD_SHARED_LIBSOFF编译了静态库。静态库的优点是部署简单一个.exe文件包含所有代码版本依赖问题少。缺点是文件体积大且如果多个动态库都链接了同一个静态gRPC可能会造成代码重复和状态不一致。编译动态库DLL则将-DBUILD_SHARED_LIBSON。同时为了确保导出的符号正确你可能需要定义-DgRPC_BUILD_SHARED_LIBSON有些项目用这个。编译后你会得到.dll文件和对应的.lib导入库。使用动态库时你必须将.dll文件放置在与可执行文件相同的目录或系统PATH能找到的位置。动态库有利于模块化更新和减少内存占用如果多个进程使用同一个DLL。一个关键陷阱如果你编译的是动态库那么所有依赖如protobuf、abseil最好也使用动态库版本否则在链接时可能会遇到“静态库与动态库混用”导致的链接警告或运行时错误。这就是为什么之前建议用vcpkg安装动态库版本x64-windows的依赖。5.2 禁用不需要的组件以加速编译gRPC包含大量你可能用不到的组件例如测试和示例-DgRPC_BUILD_TESTSOFF-DgRPC_BUILD_EXAMPLESOFF。这能大幅减少编译目标。代码覆盖率-DgRPC_BUILD_CODEGENOFF如果你不需要从.proto文件生成代码的插件但通常需要。特定传输层如果你确定只用一种传输方式可以关闭其他的但通常不需要。5.3 针对生产环境的优化编译对于Release版本除了CMake的Release配置外还可以考虑链接时优化LTO在CMake命令中添加-DCMAKE_INTERPROCEDURAL_OPTIMIZATIONON。这会让编译链接过程更慢但可能生成更小更快的代码。控制符号可见性-DCMAKE_CXX_VISIBILITY_PRESEThidden-DCMAKE_VISIBILITY_INLINES_HIDDENON。这可以减小动态库的体积并可能提升加载速度。使用更快的链接器如果你使用Visual Studio生成器可以在项目属性中启用“快速链接”/DEBUG:FASTLINK进行开发。但对于最终发布还是需要完整链接。5.4 交叉编译与多配置生成虽然本文聚焦x64但CMake也支持为ARM64等架构交叉编译。你需要对应的工具链。对于Visual Studio生成器你可以使用-A ARM64。更复杂的情况需要编写工具链文件。对于需要同时生成Debug和Release库的情况使用Visual Studio生成器-G Visual Studio 16 2019 -A x64会更方便。因为它生成一个.sln文件里面包含了所有配置。编译时使用cmake --build . --config Release cmake --build . --config Debug然后分别安装到不同的目录。6. 从编译到验证编写一个简单的测试客户端库编译安装好了怎么验证它是可用的最好的方法就是写一个最简单的gRPC客户端和服务器。但由于篇幅这里给出一个极简的验证思路确保链接没有问题。创建一个最简单的C控制台项目。仅包含gRPC头文件并链接库不调用任何复杂函数只进行初始化。// test_link.cpp #include grpcpp/grpcpp.h int main() { // 仅仅测试头文件是否找到和链接器是否工作 grpc::ChannelArguments args; // 尝试创建一个简单的通道不实际连接 auto channel grpc::CreateChannel(localhost:50051, grpc::InsecureChannelCredentials()); // 如果程序能编译链接并运行到此没有抛出异常或立即崩溃说明基础库链接成功。 // 注意这里没有实际发起RPC所以即使服务器没开也不会出错。 std::cout gRPC library linked successfully! std::endl; return 0; }按照4.2节的方法配置项目包含目录和库目录。编译并运行。如果能看到输出并且没有出现“找不到grpc.dll”或“无法定位程序输入点...”之类的运行时错误说明你的gRPC库编译和配置基本成功。手动编译gRPC的确比一键安装要繁琐但它赋予了你对依赖版本、编译选项和最终二进制文件的完全控制权。对于追求稳定、可复现的构建环境或者需要定制化功能的生产项目这份投入是值得的。整个过程的核心在于理解CMake的配置参数、管理好第三方依赖善用vcpkg以及保持你自己项目与gRPC库的编译设置特别是运行时库一致。希望这份详尽的指南能帮你扫清Windows上编译gRPC的所有障碍。
返回列表