我遇到了 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 在项目根目录下创建。每个对象都位于名为 projpb 或 other 的子包 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 都有包 projpb 或 other,位于子包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/