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

How to use a package of generated protobuf inside a go module?

我遇到了 Go Module 管理和生成 protobuffers 的问题(使用 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 submodule)。
原型如下:

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)

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

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

生成的 Proto 对象,包括 other 命名空间对象,具有导入路径 import other "company.tld/proj/projpb/other"(由 go_package 选项带来,但这是错误的,因为它不是现有的 module - go mod tidy/vendor 抱怨找不到它。

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

→ 与--go_opt=module=company.tld/proj

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

生成的 Proto 对象,包括 other 命名空间对象,仍然具有导入路径 import other "company.tld/proj/projpb/other"(仍然由 go_package选项并且仍然是错误的,因为这仍然是一个不存在的 module - 这些是生成的文件...为什么我要创建这些的 module?)。

很酷的是,有了这个 go_opt,访问生成的类型看起来更正常
import pb "company.tld/proj/subproj/projpb".

终于,我试过了

replace (
    company.tld/proj/projpb => ./projpb
    company.tld/proj/projpb/other => ./projpb/other
)

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

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


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

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

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

最终原型 «header»(尊重 go_package 选项中的完整 puth)

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(为生成的原型文件创建一个 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 重定向到项目范围内的本地文件夹)

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 很高兴,不要尝试在远程存储库中搜索 module。
生成的 *.pb.go 文件具有正确的导入路径:company.tld/proj/projpb(子包为 company.tld/proj/projpb/other)。
并且使用生成的原型的导入语句在主项目中工作正常。

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

抱歉,感谢那些考虑过的人!