go - 如何在go模块中使用生成的protobuf包?

标签 go protocol-buffers go-modules protoc

我遇到了 Go 模块管理和 protobuffer 生成的问题(使用 go1.16、protoc-gen-go@latest)。

我有这个项目结构:

subproj
├── go.mod         (module company.tld/proj/subproj)
├── subproj.go     (entry point : package main)
├── proto          (folder containing .proto files)
├── packageFolder
|   └── file1.go   (package packageFolder)
└── Makefile       (used to generate *.pb.go and build subproj binary)

proto 文件夹被其他项目使用(显然......)(通过 git 子模块)。
Proto 如下所示:

syntax = "proto3"
option csharp_namespace = "Proj.Proto";
option go_package = "company.tld/proj/projpb";
package entity.proj
...

由于消息版本不同,很少有 protobuffer 文件需要位于另一个“命名空间”中:

option go_package = "company.tld/proj/projpb/other";
package entity.proj.other

在我的 Makefile 中,我尝试在正确的位置生成正确的 *.pb.go:

# Proto sources
PROTO= $(wildcard ${PROTODIR}/*.proto)
PBGO=  $(PROTO:.proto=.pb.go)

MODULE_NAME=company.tld/proj
GO_OPT_FLAG=   --go_opt=module=${MODULE_NAME}     
GRPC_OPT_FLAG= --go-grpc_opt=module=${MODULE_NAME}
#GO_OPT_FLAG=   --go_opt=paths=import
#GRPC_OPT_FLAG= --go-grpc_opt=paths=import

.PHONY: clean install proto

## Builds the project
build: proto
    go build ${LDFLAGS} -o ${BINARY}

$(PROTOBUF_GO_PLUGIN):
    go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

$(GRPC_GO_PLUGIN):
    go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

%.pb.go: %.proto | $(PROTOBUF_GO_PLUGIN) $(GRPC_GO_PLUGIN)
    protoc --proto_path=${PROTODIR} --go_out=. ${GO_OPT_FLAG} --go-grpc_out=. ${GRPC_OPT_FLAG} $<

proto: $(PBGO)

因此,取决于协议(protocol)编译器使用的选项:
→ 使用 --go_opt=paths=import

文件夹树 company.tld/proj/projpb 由 protoc 在项目根目录下创建。每个对象都位于名为 projpbother 的子包 other 中。

生成的 Proto 对象(包括其他命名空间-d 对象)具有导入路径导入其他“company.tld/proj/projpb/other”(即由 go_package 选项带来,但这是错误的,因为它不是现有模块 - go mod tidy/vendor 提示它找不到它)。

普通项目文件需要以下导入路径才能到达生成的 Proto 对象:
导入pb“company.tld/proj/subproj/company.tld/proj/projpb”
这看起来很奇怪,而且不是正确的方法。

→ 使用 --go_opt=module=company.tld/proj

protoc 在项目根目录创建一个文件夹 projpb,每个生成的 .pb.go 都有包 projpbother,位于子包other。

生成的 Proto 对象(包括其他命名空间-d 对象)仍然具有导入路径导入其他“company.tld/proj/projpb/other”(其中仍然是由 go_package 选项带来的,并且仍然是错误的,因为这仍然是一个不存在的模块 - 这些是生成的文件...为什么我要创建这些模块?)。 p>

最酷的事情是,通过这个 go_opt,访问生成的类型看起来更加正常
导入pb“company.tld/proj/subproj/projpb”

我终于尝试了

  • 在 .proto 文件中的 go_package 选项上使用本地导入路径(在构建时被拒绝,因为在生成的 protobuffer 对象中会有导入其他“./projpb/other”)
  • 在 go.mod 文件中使用 replace 指令,如下所示:
replace (
    company.tld/proj/projpb => ./projpb
    company.tld/proj/projpb/other => ./projpb/other
)

(但是 go mod tidy/vendor 提示它无法在生成的文件夹 ./projpb 中找到 go.mod 文件)

有人遇到过类似的问题吗?或者我是否缺少一个命令选项来告诉 Go,“我在包中生成 protobuffer 对象,或者在包中生成 protobuffer 对象,而我只是想使用它们。它们不是模块,因此请提供生成对象的正确导入路径,并让我在代码中使用它们»。


[更新01]
我尝试了 go_opt=paths=source_relative (受此启发 ticket )。
我在Makefile中创建了该文件夹,protoc在里面生成文件。
备注:

  • 生成的原型(prototype)使用由 go_package 选项指定的完整路径来相互关联。
  • 只要 go_package 选项需要完整路径,Go (go mod tidy/vendor) 就会在创建的文件夹中搜索包含生成的原型(prototype)的 go.mod 文件。

告诉 Go 我不是在寻找模块,但仍然满足 protobuffer 文件中 go_package 选项的完整路径约束的正确方法是什么?

最佳答案

在多次更改 proto 文件中的 go_package 选项后,更改 protoc 编译器命令上的 go_opt ,这是我发现编译项目的唯一方法使用我生成的原型(prototype)缓冲区,尊重每个 Go 约束,是通过即时创建 go.mod 文件...

最终原型(prototype)“header”(尊重go_package选项中的完整内容)

syntax = "proto3";
option csharp_namespace = "Proj.Proto";
option go_package = "company.tld/proj/projpb";
// or for subpackages...
option csharp_namespace = "Proj.Proto.Other";
option go_package = "company.tld/proj/projpb/other";

我的 Makefile(为生成的原型(prototype)文件创建 go.mod 文件)


# Proto sources
PROTO= $(shell find ${PROTODIR} -type f -name '*.proto')
PBGO=  $(PROTO:.proto=.pb.go)

DEST_DIR=.
MODULE_NAME=company.tld/proj
GO_OPT_FLAG=   --go_opt=module=${MODULE_NAME}
GRPC_OPT_FLAG= --go-grpc_opt=module=${MODULE_NAME}

PROTO_PKG_DIR=projpb
PROTO_MODULE_NAME=${MODULE_NAME}/${PROTO_PKG_DIR}
PROTO_GOMOD_FILE=${PROTO_PKG_DIR}/go.mod

.PHONY: clean install proto gomod

build: proto gomod
    go build ${LDFLAGS} -o ${BINARY}

%.pb.go: %.proto | $(PROTOBUF_GO_PLUGIN) $(GRPC_GO_PLUGIN) $(DEST_DIR)
    ${PROTOC} --proto_path=${PROTODIR} --go_out=${DEST_DIR} ${GO_OPT_FLAG} --go-grpc_out=${DEST_DIR} ${GRPC_OPT_FLAG} $<

proto: $(PBGO)

gomod: ${PROTO_GOMOD_FILE}

${PROTO_GOMOD_FILE}:
    cd ${PROTO_PKG_DIR} && go mod init ${PROTO_MODULE_NAME} && cd ..

我的主 go.mod 文件(将动态创建的模块重定向到项目范围内的本地文件夹)

module company.tld/proj/subproj

go 1.16

require (
    // ...
    company.tld/proj/projpb v0.0.0
)

replace company.tld/proj/projpb v0.0.0 => ./projpb

感谢 replace 指令,go mod tidy/vendor 很高兴,并且不会尝试在远程存储库中搜索模块。
生成的 *.pb.go 文件具有正确的导入路径:company.tld/proj/projpb(以及子包的company.tld/proj/projpb/other)。
使用生成的原型(prototype)的导入语句在主项目中运行良好。

我希望有一个更简单、更漂亮的解决方案,但是唉......

对任何人表示歉意,并感谢那些考虑过这个问题的人!

关于go - 如何在go模块中使用生成的protobuf包?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68119052/

相关文章:

go - S3 存储桶命名策略问题并尝试将 SDK 生成的请求发送到实现 S3 API 的 API

go - 如何在测试服务器中注入(inject)特定的 IP 地址?戈朗

go - 如何在 go.mod 中最好地声明 golang 依赖版本?

go - 拥有 vendor 文件夹有什么好处?

google-app-engine - 如何使用 go 模块在 GAE SE Go 1.11 上导入私有(private)存储库?

go - 如何从 "go"中编写的控制台应用程序转移到另一个控制台应用程序?

go - 解释这个 Go 语句

protocol-buffers - 将 Protocol Buffer 转换为 POJO

go - 从 --go_out=plugins 切换到 -go-grpc_out PATH 问题

objective-c - 序列化为 JSON 的可编译 IDL