windows中使用vcpkg安装gprc并在clion中调试过程总结

本文也可以认为是vcpkg安装包并在cmake中使用的教程,其中踩了很多坑,记录一下。

1.安装vcpkg

参考链接:

在D盘新建一个目录 D:dev用于安装vcpkg和存储后续安装的库,然后打开git bush,拿去vcpkg库:

git clone https://github.com/microsoft/vcpkg.git

然后执行命令:

# windows
.\bootstrap-vcpkg.bat
# 设置全局包含
.\vcpkg.exe integrate install
#linux
./bootstrap-vcpkg.sh
./vcpkg integrate install

结束后会在 D:\dev\vcpkg目录下看到一个 vcpkg.exe的可执行文件,将目录添加到环境变量path中,然后就可以在任意地方使用vcpkg命令了。

2.安装grpc

在这一步,需要注意的点就是选择合适的生成工具链,特别是在windows上。一般来说,vcpkg会选择默认工具链作为生成工具链,如果你的电脑上只有一种开发环境,比如vs2019,那么默认工具链就是vs2019。但是,如果电脑上安装有多个开发环境,比如安装vs2022,但同时安装了vs2019的开发环境,那么默认情况下会选择vs2022用作生成工具链,如果你的需求是使用vs2019生成动态库并与其他人一起开发,那么就会遇到问题,因为vs2022的工具链生成的动态库无法被vs2019工具链编译,在链接阶段会报错,因此混合开发环境的情况下需要手动指定生成工具链。

2.1永久修改工具链

#指定生成器为ninja
VCPKG_CMAKE_GENERATOR = Ninja
#指定生成工具链为vs2019
VCPKG_PLATFORM_TOOLSET = v142
#指定默认的编译环境为x64-windows
VCPKG_DEFAULT_TRIPLET = x64-windows

2.2临时修改工具链

set VCPKG_CMAKE_GENERATOR=Ninja
set VCPKG_PLATFORM_TOOLSET=v142
set VCPKG_DEFAULT_TRIPLET=x64-windows

2.3安装grpc

安装grpc会自动安装protobuf,因此只需要安装grpc即可:

vcpkg install grpc:x64-windows

注意,在安装过程中可能遇到下载文件卡壳的情况,这时可以检查是否是链接本身错误,或者手动下载文件,然后放到 D:\dev\vcpkg\downloads目录下,再重新安装。

安装完成后,在 D:\dev\vcpkg\installed\x64-windows目录下可以看到grpc的安装目录,其中包含了grpc的库文件和头文件。

3.在clion中使用grpc

文件内容放到文末。

在cmake中配置vcpkg中的包,需要做出额外的配置,否则会报错找不到库文件。

假设在安装包的时候选择的vs2022工具链,那么在clion中需要添加如下配置:

1758015099068

  1. 首先,将工具链调整到编译库对应的工具链;
  2. 然后,在cmake选项中添加如下命令(指定生成器为ninja,工具链为vs,最后指定vcpkg包目录):
     -G Ninja -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -DCMAKE_TOOLCHAIN_FILE=D:/dev/vcpkg/scripts/buildsystems/vcpkg.cmake

为什么要做第二步的配置?

  1. 修改工具链到编译库时使用的工具链,确保项目能正常进行库链接;
  2. clion对vs生成器的兼容存在问题,导致无法高亮显示代码,因此需要指定生成器为ninja,这样clion就能正常高亮显示代码了。

4.项目文件

4.1 test.proto

syntax = "proto3";

package test;

service TestService {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

4.2 server.cpp

#include <grpcpp/grpcpp.h>
#include <iostream>
#include "test.grpc.pb.h"

using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using test::HelloRequest;
using test::HelloReply;
using test::TestService;

class ServiceImpl final : public TestService::Service {
    Status SayHello(ServerContext* context, const HelloRequest* request,
                    HelloReply* reply) override {
        std::string prefix("Hello from server ");
        reply->set_message(prefix + request->name());
        return Status::OK;
    }
};

void RunServer() {
    std::string server_address("0.0.0.0:50051");
    ServiceImpl service;

    ServerBuilder builder;
    builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
    builder.RegisterService(&service);

    std::unique_ptr<Server> server(builder.BuildAndStart());
    std::cout << "Server listening on " << server_address << std::endl;
    server->Wait();
}

int main() {
    RunServer();
    return 0;
}

4.3 client.cpp

#include <grpcpp/grpcpp.h>
#include <iostream>
#include "test.grpc.pb.h"

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using test::HelloRequest;
using test::HelloReply;
using test::TestService;

class TestClient {
public:
    TestClient(std::shared_ptr<Channel> channel)
        : stub_(TestService::NewStub(channel)) {}

    std::string SayHello(const std::string& name) {
        HelloRequest request;
        request.set_name(name);

        HelloReply reply;
        ClientContext context;

        Status status = stub_->SayHello(&context, request, &reply);

        if (status.ok()) {
            return reply.message();
        }
        else {
            return "RPC failed: " + status.error_message();
        }
    }

private:
    std::unique_ptr<TestService::Stub> stub_;
};

int main() {
    TestClient client(grpc::CreateChannel(
        "localhost:50051", grpc::InsecureChannelCredentials()));

    std::string name("World");
    std::string response = client.SayHello(name);
    std::cout << "Response: " << response << std::endl;

    return 0;
}

4.4 CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(my_grpc_project)

# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 查找依赖
find_package(protobuf REQUIRED)
find_package(gRPC REQUIRED)  

message(STATUS "gRPC found: ${gRPC_FOUND}")
message(STATUS "gRPC version: ${gRPC_VERSION}")
message(STATUS "Protobuf found: ${protobuf_FOUND}")
message(STATUS "Protobuf version: ${protobuf_VERSION}")

# 生成 protobuf 和 gRPC 代码
set(PROTO_FILE "test.proto")
get_filename_component(PROTO_PATH ${PROTO_FILE} ABSOLUTE)

# 查找 protoc 和插件
find_program(PROTOC protoc)
find_program(GRPC_CPP_PLUGIN grpc_cpp_plugin)

# 手动生成代码
set(PROTO_SRCS "${CMAKE_CURRENT_BINARY_DIR}/test.pb.cc")
set(PROTO_HDRS "${CMAKE_CURRENT_BINARY_DIR}/test.pb.h")
set(GRPC_SRCS "${CMAKE_CURRENT_BINARY_DIR}/test.grpc.pb.cc")
set(GRPC_HDRS "${CMAKE_CURRENT_BINARY_DIR}/test.grpc.pb.h")

add_custom_command(
        OUTPUT ${PROTO_SRCS} ${PROTO_HDRS} ${GRPC_SRCS} ${GRPC_HDRS}
        COMMAND ${PROTOC}
        ARGS --grpc_out=${CMAKE_CURRENT_BINARY_DIR}
        --cpp_out=${CMAKE_CURRENT_BINARY_DIR}
        --plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN}
        -I ${CMAKE_CURRENT_SOURCE_DIR}
        ${PROTO_FILE}
        DEPENDS ${PROTO_FILE}
)

# 创建可执行文件
add_executable(server server.cpp
        ${PROTO_SRCS} ${PROTO_HDRS}
        ${GRPC_SRCS} ${GRPC_HDRS}
)

target_include_directories(server
        PRIVATE
        ${CMAKE_CURRENT_BINARY_DIR}
)

# 链接库
target_link_libraries(server PRIVATE
        protobuf::libprotobuf
        protobuf::libprotoc
        gRPC::grpc++
        gRPC::grpc++_reflection
)

# 创建可执行文件
add_executable(client client.cpp
        ${PROTO_SRCS} ${PROTO_HDRS}
        ${GRPC_SRCS} ${GRPC_HDRS}
)

target_include_directories(client
        PRIVATE
        ${CMAKE_CURRENT_BINARY_DIR}
)

# 链接库
target_link_libraries(client PRIVATE
        protobuf::libprotobuf
        protobuf::libprotoc
        gRPC::grpc++
        gRPC::grpc++_reflection
)