1. 项目概述:为什么要在Windows上手动编译gRPC?
如果你是一名在Windows平台上使用Visual Studio 2019进行C++开发的工程师,并且项目需要引入gRPC进行高性能的RPC通信,那么你大概率会遇到一个绕不开的坎:官方预编译库的“水土不服”。gRPC官方虽然提供了一些预编译的二进制包,但在实际项目集成时,特别是需要与特定版本的第三方库(如特定版本的Protobuf、OpenSSL)搭配,或者需要开启某些自定义功能(如自定义的负载均衡器、追踪插件)时,直接使用预编译库往往捉襟见肘。更常见的情况是,官方库的编译选项(如MT/MTd、MD/MDd运行时库)与你的项目不匹配,导致链接时一堆令人头疼的LNK2038、LNK2005错误。
手动编译gRPC,听起来像是一项繁琐的底层工作,但它带来的收益是实实在在的。首先,你获得了完全的掌控权。你可以精确指定编译工具链(MSVC的版本)、运行时库类型、优化级别,以及启用或禁用哪些gRPC功能模块。其次,它能确保你的开发环境、构建环境和最终部署环境在库版本和ABI上完全一致,这是避免“在我机器上是好的”这类灵异事件的根本方法。最后,这个过程本身是对gRPC依赖生态的一次深度梳理,你会清楚地知道它依赖了哪些底层库(如zlib、c-ares、abseil-cpp等),以及它们是如何被组织在一起的,这对于后续的疑难排查和性能调优有莫大帮助。
本文将基于Windows 10/11 + Visual Studio 2019 + CMake这一经典组合,手把手带你完成gRPC及其核心依赖的完整编译过程。我不会只给你一串冰冷的命令,而是会详细解释每个步骤背后的意图、可能遇到的坑以及我趟过这些坑后总结的实战技巧。我们的目标不仅仅是得到几个.lib和.dll文件,而是构建一个稳定、可复现、与你的VS2019项目无缝集成的gRPC开发环境。
2. 环境准备与工具链选型
在开始编译之前,搭建一个干净、可控的编译环境至关重要。在Windows上,我们主要面临两种选择:使用Visual Studio自带的“开发者命令提示符”环境,或者使用像MSYS2这样的类Unix环境。我强烈推荐前者,即使用VS2019的Native Tools Command Prompt。原因很简单:我们要编译的是最终在Windows原生环境(Win32 API)下运行的库,使用微软官方的工具链可以最大程度保证兼容性,避免引入MinGW等环境可能带来的潜在链接和运行时问题。
2.1 核心工具安装与验证
你需要确保以下工具已正确安装并配置在系统路径中:
- Visual Studio 2019:安装时务必勾选“使用C++的桌面开发”工作负载,这包含了MSVC编译器、链接器、Windows SDK以及最重要的——我们即将用到的
nmake构建工具。建议安装版本为16.11(含)以上,以获得较好的C++17/20标准支持。 - CMake:这是现代C++项目的标配构建系统生成器。gRPC使用CMake作为其构建系统。请从官网下载并安装最新稳定版(如3.25+),安装时务必勾选“Add CMake to the system PATH for all users”选项。
- Git:用于克隆gRPC的源代码仓库。同样,安装时确保将Git命令添加到系统PATH。
- Active Perl 或 Strawberry Perl:编译OpenSSL等依赖时需要Perl环境来执行配置脚本。Strawberry Perl是一个集成了GCC工具链的Windows版本,对于不熟悉Perl的开发者更友好。安装后也需要将其
bin目录加入PATH。
安装完成后,打开“开始”菜单,找到“Visual Studio 2019”文件夹,在其子目录“Visual Studio Tools”中,你会看到“Developer Command Prompt for VS 2019”。请始终在这个命令行窗口中执行后续所有操作。这个环境会自动设置好cl、nmake、msbuild等工具所需的所有环境变量(如INCLUDE、LIB)。
验证环境:在打开的开发者命令提示符中,依次输入以下命令,确认版本无误:
cl cmake --version git --version perl --version如果cl命令能显示Microsoft C/C++编译器的版本信息,其他命令也能正确输出版本,说明基础环境就绪。
2.2 源码获取与目录规划
不建议从Releases页面下载源码包,因为可能会缺少最新的子模块。我们将使用Git进行克隆。
首先,规划一个清晰的工作目录。我习惯在非系统盘(如D:\)下创建一个专门用于编译第三方库的目录,例如D:\Dev\Build。在这个目录下,为本次编译单独创建一个子文件夹,比如D:\Dev\Build\grpc-vs2019。所有操作都将在这个文件夹内进行。
打开刚才的“Developer Command Prompt for VS 2019”,使用cd命令切换到这个目录:
D: cd \Dev\Build\grpc-vs2019接下来,克隆gRPC仓库。由于gRPC使用了Git子模块来管理其众多依赖(如abseil-cpp、c-ares等),我们需要使用--recursive参数进行递归克隆。这个过程会下载大量代码,请保持网络通畅。
git clone --recurse-submodules -b v1.54.0 https://github.com/grpc/grpc.git这里我指定了-b v1.54.0标签,这是为了锁定一个稳定的版本进行编译。你可以根据需要选择其他稳定版本标签(如v1.55.0,v1.56.0),但切忌使用默认的master分支,因为主分支处于持续开发中,代码状态可能不稳定。
注意:递归克隆可能会因为网络问题导致部分子模块拉取失败。如果遇到这种情况,进入克隆好的
grpc目录,再次执行git submodule update --init --recursive来补全子模块。
克隆完成后,你的目录结构应类似于:
D:\Dev\Build\grpc-vs2019\ └── grpc/ ├── CMakeLists.txt ├── include/ ├── src/ ├── third_party/ │ ├── abseil-cpp/ │ ├── cares/ │ ├── protobuf/ │ └── ... └── ...这个grpc目录就是我们的源码根目录,记为<GRPC_SOURCE_DIR>。
3. 编译策略与CMake配置解析
直接在最外层的CMakeLists.txt上运行CMake并不是最佳实践,尤其是对于gRPC这样结构复杂的项目。标准的做法是进行“外部构建”,即在源码目录外创建一个独立的构建目录。这样做的好处是:构建产生的所有中间文件、目标文件都集中在构建目录内,与源码完全分离。你可以随时删除整个构建目录来重新开始,而不会污染源码。这对于尝试不同的CMake配置选项非常方便。
3.1 创建构建目录与生成解决方案
在我们的工作目录D:\Dev\Build\grpc-vs2019下,创建两个子文件夹:build用于存放构建文件,install用于存放最终编译好的库文件和头文件。
mkdir build mkdir install cd build现在,我们位于D:\Dev\Build\grpc-vs2019\build。接下来,运行CMake来生成Visual Studio的解决方案文件。这是一条核心命令,包含了所有关键的配置选项:
cmake ../grpc ^ -G "Visual Studio 16 2019" ^ -A x64 ^ -DCMAKE_INSTALL_PREFIX=../install ^ -DgRPC_INSTALL=ON ^ -DgRPC_BUILD_TESTS=OFF ^ -DgRPC_SSL_PROVIDER=package ^ -DgRPC_ZLIB_PROVIDER=package ^ -DCMAKE_BUILD_TYPE=Release让我们逐一拆解这些参数的含义和背后的考量:
-G "Visual Studio 16 2019":指定生成器为Visual Studio 2019。CMake支持多种生成器,这个参数告诉CMake我们要生成.sln解决方案文件。-A x64:指定目标平台架构为64位。这是现代Windows应用开发的主流选择。如果你需要32位库,则使用-A Win32。-DCMAKE_INSTALL_PREFIX=../install:这是最重要的参数之一。它定义了make install或cmake --install命令执行时,编译产物的安装路径。我们将其设置为上一级的install目录,这样所有编译好的库(.lib)、动态链接库(.dll)和头文件(.h)都会被集中复制到这个目录下,结构清晰,便于后续项目引用。-DgRPC_INSTALL=ON:启用gRPC的安装规则。只有打开这个选项,后续的安装步骤才会生效。-DgRPC_BUILD_TESTS=OFF:关闭gRPC测试项目的编译。除非你需要运行gRPC的单元测试,否则强烈建议关闭,这能显著减少编译时间和生成的解决方案复杂度。-DgRPC_SSL_PROVIDER=package和-DgRPC_ZLIB_PROVIDER=package:这两个参数指定gRPC使用系统已安装的OpenSSL和zlib库,而不是编译它自带的源码。这是另一个关键决策点。gRPC的third_party目录下自带了这些依赖的源码,但直接编译它们可能会遇到版本冲突或编译问题。更稳妥的做法是,提前使用vcpkg或手动编译好这些依赖,并确保CMake能找到它们。这里设为package意味着CMake会通过find_package来查找系统环境中的这些库。为了简化首次编译,我们也可以先使用module(使用自带源码),但为了获得最佳的可控性,我建议分开管理。-DCMAKE_BUILD_TYPE=Release:指定构建类型为发布模式(优化开启,调试信息关闭)。如果你需要调试库,可以后续用--config Debug参数来构建Debug版本。
执行这条命令后,CMake会开始配置过程。它会检查编译器、查找依赖,并在当前build目录下生成grpc.sln解决方案文件以及大量的.vcxproj项目文件。
3.2 依赖管理:系统库还是自带源码?
在上面的配置中,我将SSL和ZLIB的提供者设为了package。这意味着你需要确保系统中有可被找到的OpenSSL和zlib开发库。一个在Windows上管理C++库的绝佳工具是vcpkg。如果你已经使用vcpkg,可以非常方便地安装这些依赖:
# 在vcpkg目录下执行 .\vcpkg install openssl:x64-windows zlib:x64-windows安装后,在CMake命令中需要添加参数来告诉CMake使用vcpkg的工具链文件:
cmake ../grpc [其他参数] -DCMAKE_TOOLCHAIN_FILE=[你的vcpkg目录]\scripts\buildsystems\vcpkg.cmake实操心得:对于首次编译gRPC,如果不想额外配置vcpkg,一个更简单粗暴的方法是暂时将-DgRPC_SSL_PROVIDER和-DgRPC_ZLIB_PROVIDER的值改为module。这样CMake就会去编译third_party目录下自带的openssl和zlib源码。这能避免因找不到系统库而导致的配置失败,让你先走通编译流程。但请注意,自带的版本可能不是最新的,且编译过程也可能因源码差异而出错。
4. 使用MSBuild进行编译与安装
CMake成功生成解决方案后,我们并没有得到最终的库文件。.sln文件只是一个“项目蓝图”,我们需要调用MSBuild(Visual Studio的构建引擎)来执行实际的编译和链接工作。
4.1 执行编译命令
在build目录下,执行以下命令进行编译:
cmake --build . --config Release --parallel--build .:指定在当前目录(即包含.sln文件的目录)进行构建。--config Release:指定构建Release配置。这与我们之前CMake配置时指定的CMAKE_BUILD_TYPE相对应。如果你想编译Debug版本,则使用--config Debug。--parallel:启用并行编译,充分利用多核CPU,大幅加快编译速度。
这个命令会启动MSBuild,依次编译解决方案中的所有项目,包括gRPC核心库(grpc)、gRPC++库(grpc++)、gRPC插件(grpc_cpp_plugin等)以及我们设置为module的第三方依赖。整个过程视机器性能而定,可能需要10到30分钟。期间控制台会输出大量的编译信息,只要没有出现红色的错误(error)提示,就可以耐心等待。
常见问题1:编译过程中内存不足(C1060, C1076)gRPC的某些目标(特别是
protobuf)在编译时可能会消耗大量内存,如果机器内存较小(如小于16GB),可能会遇到编译器前端c1xx.dll内存不足的致命错误。解决方法有:
- 关闭并行编译(去掉
--parallel),但会极大增加编译时间。- 在CMake配置时,添加
-Dprotobuf_MSVC_STATIC_RUNTIME=OFF(如果使用自带protobuf)并确保使用动态运行时库(MD/MDd),这有时能减少单个编译单元的内存占用。- 最根本的方法是增加系统的虚拟内存(页面文件)大小。
4.2 安装编译产物
编译成功完成后,build目录下的Release子文件夹里已经生成了我们需要的.lib和.dll文件。但为了便于管理,我们需要执行“安装”步骤,将必要的文件复制到我们预设的install目录中。
执行安装命令:
cmake --install . --config Release这个命令会根据CMake配置中定义的安装规则,将编译好的库文件、动态链接库、头文件以及CMake配置文件,复制到-DCMAKE_INSTALL_PREFIX指定的目录(即../install)下。
完成后,查看D:\Dev\Build\grpc-vs2019\install目录,你会看到一个标准的UNIX风格布局:
install/ ├── bin/ # 存放可执行文件(如grpc_cpp_plugin.exe)和动态库(.dll) ├── include/ # 存放所有头文件(.h) │ ├── grpc/ │ ├── grpcpp/ │ └── ... ├── lib/ # 存放导入库(.lib)和静态库(.lib) │ ├── CMake/ # gRPC的CMake配置文件,供其他项目find_package使用 │ ├── pkgconfig/ │ └── *.lib └── share/这个install目录就是你的“gRPC SDK”,后续在VS2019项目中引用它即可。
5. 在Visual Studio 2019项目中集成与配置
得到编译好的库之后,下一步就是将其集成到你的C++项目中。这里以创建一个新的Console应用程序为例,演示如何配置属性页。
5.1 项目属性配置
包含目录:在项目属性 -> C/C++ -> 常规 -> 附加包含目录中,添加gRPC的头文件路径。你需要添加至少以下路径:
D:\Dev\Build\grpc-vs2019\install\include(gRPC核心头文件)D:\Dev\Build\grpc-vs2019\install\include(如果你编译了自带protobuf,其头文件也在这里;如果使用外部protobuf,则添加对应路径)- (可选)其他依赖库的头文件路径,如OpenSSL的
include目录。
库目录:在项目属性 -> 链接器 -> 常规 -> 附加库目录中,添加gRPC的库文件路径:
D:\Dev\Build\grpc-vs2019\install\lib
附加依赖项:在项目属性 -> 链接器 -> 输入 -> 附加依赖项中,添加需要链接的库文件名。对于Release x64配置下的一个基础gRPC客户端/服务器程序,通常需要以下库:
grpc++.lib grpc++_reflection.lib grpc.lib gpr.lib address_sorting.lib upb.lib cares.lib re2.lib ssl.lib crypto.lib zlib.lib libprotobuf.lib注意:库的列表会根据你编译的模块和依赖的提供者(
module或package)而有所不同。一个简单的方法是去install\lib目录下查看生成了哪些.lib文件,按需添加。Debug版本通常有*d.lib后缀(如grpc++d.lib),需要对应添加。运行时库与预处理器定义:确保你的项目属性 -> C/C++ -> 代码生成 -> 运行时库的设置,与编译gRPC时使用的设置一致。如果你在CMake时没有特别指定,默认可能是
/MD(Release)和/MDd(Debug)。不一致会导致链接错误。同时,在预处理器定义中可能需要添加_WIN32_WINNT=0x0A00(Windows 10)等宏来确保Windows API版本兼容。
5.2 处理Proto文件与代码生成
gRPC服务定义使用.proto文件。你需要使用protoc编译器配合grpc_cpp_plugin插件来生成C++代码。
- 获取工具:编译完成后,
protoc.exe和grpc_cpp_plugin.exe位于install\bin目录下。确保它们在你的系统PATH中,或者你知道它们的完整路径。 - 生成代码:通常通过构建事件(预生成事件)或自定义构建工具来实现自动化。一个典型的命令如下:
例如:protoc -I=<proto文件目录> --cpp_out=<输出目录> --grpc_out=<输出目录> --plugin=protoc-gen-grpc="<grpc_cpp_plugin.exe路径>" <你的proto文件>.proto
这会在protoc -I=. --cpp_out=./generated --grpc_out=./generated --plugin=protoc-gen-grpc="D:\Dev\Build\grpc-vs2019\install\bin\grpc_cpp_plugin.exe" helloworld.proto./generated目录下生成helloworld.pb.h、helloworld.pb.cc、helloworld.grpc.pb.h、helloworld.grpc.pb.cc四个文件。 - 将生成文件加入项目:将这些生成的
.cc和.h文件添加到你的VS项目中,并像普通源文件一样参与编译。
5.3 部署注意事项:DLL处理
如果你的项目是动态链接(使用了.dll),那么在发布可执行文件时,需要将对应的.dll文件(位于install\bin)复制到你的可执行文件同级目录下。关键的DLL通常包括:
grpc++.dllgrpc.dlllibprotobuf.dlllibcrypto-1_1-x64.dll(OpenSSL)libssl-1_1-x64.dll(OpenSSL)zlib.dll
你可以使用Visual Studio的“生成事件”中的“后期生成事件”,用xcopy命令自动复制这些DLL到输出目录,避免手动操作的繁琐和遗漏。
6. 疑难排查与进阶技巧
即使按照步骤操作,编译和集成过程也可能遇到各种问题。这里记录一些典型问题的排查思路。
6.1 编译阶段常见错误
LNK2001/LNK2019: 无法解析的外部符号:这是最常见的链接错误。
- 检查库列表:首先确认“附加依赖项”中是否包含了所有必需的库。对比
install\lib目录下的文件,看是否有遗漏。 - 检查运行时库:确认项目属性中的“运行时库”(/MT、/MTd、/MD、/MDd)与gRPC库编译时使用的选项完全一致。不一致是导致此问题的首要原因。gRPC默认CMake配置通常生成的是动态运行时库(/MD或/MDd)。如果你在CMake配置时没有动过
CMAKE_MSVC_RUNTIME_LIBRARY变量,那么它很可能就是MultiThreadedDLL(对应/MD)或MultiThreadedDebugDLL(对应/MDd)。 - 检查架构:确保项目平台(x86/x64)与编译的库平台一致。
- 检查库目录:确认“附加库目录”路径正确,且该目录下确实有对应配置(Release/Debug)的库文件。
- 检查库列表:首先确认“附加依赖项”中是否包含了所有必需的库。对比
C1083: 无法打开包括文件:找不到头文件。
- 检查包含目录:仔细核对“附加包含目录”中的路径是否正确,特别是路径中是否包含中文字符或特殊字符(建议全英文路径)。
- 检查依赖项头文件:如果错误是关于
openssl/ssl.h或zlib.h,说明CMake没有找到这些依赖。你需要确保系统已安装这些库,并且其include目录被正确添加到项目的包含目录中,或者在CMake配置gRPC时正确设置了gRPC_SSL_PROVIDER和gRPC_ZLIB_PROVIDER。
CMake配置失败,找不到工具链或包:
- 确保在Developer Command Prompt for VS 2019中运行CMake。
- 如果使用vcpkg,确保
-DCMAKE_TOOLCHAIN_FILE的路径正确,并且vcpkg已安装所需的三方库(openssl:x64-windows等)。
6.2 运行时常见问题
程序无法启动,因为缺少xxx.dll:这是典型的动态链接库缺失。按照5.3节的说明,将所需的DLL复制到可执行文件目录。可以使用
Dependencies(原Dependency Walker)工具来查看你的exe具体依赖哪些DLL。程序崩溃在gRPC初始化或调用时:
- Debug/Release不匹配:最常见的原因是在Debug模式下链接了Release版本的库,或者反之。确保配置一致。
- 堆损坏:这通常是由于运行时库不匹配(如/MD链接了/MT编译的库)导致的内存分配/释放跨堆进行。彻底检查所有依赖库的编译选项。
- Protobuf版本冲突:如果你的项目中其他地方也引用了Protobuf,必须确保整个项目使用的Protobuf头文件和库版本与gRPC所使用的完全一致。版本不匹配会导致序列化/反序列化出现未定义行为。
6.3 进阶编译选项与优化
- 编译静态库:默认CMake配置生成的是动态库(DLL)。如果你想生成静态库(
.lib),可以在CMake配置时添加-DBUILD_SHARED_LIBS=OFF。注意,静态链接会显著增大最终可执行文件的体积,并且需要注意运行时库的匹配问题(静态链接gRPC通常也需要静态链接C++运行时库)。 - 自定义依赖版本:如果你想使用特定版本的第三方库(如自己编译的OpenSSL 3.0),而不是gRPC自带的或vcpkg提供的,你需要确保该库的CMake配置文件(如
OpenSSLConfig.cmake)或pkg-config文件能被CMake找到,并在CMake配置gRPC前通过环境变量或CMake变量指定其路径。 - 启用/禁用特定功能:gRPC的CMake提供了许多选项来启用或禁用功能,例如
-DgRPC_BUILD_CSHARP_EXT=OFF(禁用C#扩展)、-DgRPC_BACKWARDS_COMPATIBILITY_MODE=OFF(禁用向后兼容模式,以使用最新API)。你可以查阅gRPC源码根目录下的CMakeLists.txt或cmake目录下的文件来了解所有可用选项。
手动编译gRPC的确比直接下载预编译二进制包要复杂,但这份付出是值得的。它让你对自己的技术栈有了更深的理解和更强的掌控力。当你的服务需要处理海量并发RPC调用时,你会感激自己当初花时间搭建的这个稳固的基础。整个过程最磨人的部分往往是环境配置和依赖处理,一旦打通,后续的更新和迭代就会顺畅很多。建议将成功的编译脚本(包括CMake命令、环境设置等)保存下来,方便未来复现或迁移到新的机器上。