如何解决如何在 go 模块中使用生成的 protobuf 包?
我遇到了 Go 模块管理和生成 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 子模块)。
原型如下:
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)
因此,取决于与 protoc 编译器一起使用的选项:
→ 用--go_opt=paths=import
文件夹树 company.tld/proj/projpb 由 protoc 在项目的根目录创建。每个对象都在名为 projpb 或 other 的包中,位于子包 other 中。
生成的 Proto 对象,包括 other 命名空间-d 对象,具有导入路径 import other "company.tld/proj/projpb/other"
(由 go_package
选项带来,但这是错误的,因为它不是现有模块 - go mod tidy/vendor 抱怨它找不到它)。
普通项目文件需要以下导入路径才能到达生成的原型对象:import pb "company.tld/proj/subproj/company.tld/proj/projpb"
这看起来很奇怪,而且不是正确的做法。
→ 与 --go_opt=module=company.tld/proj
一个文件夹 projpb 由 protoc 在项目的根目录创建,每个生成的 .pb.go 都有包 projpb 或 other,在子包其他。
生成的 Proto 对象,包括 other 命名空间-d 对象,仍然具有导入路径 import other "company.tld/proj/projpb/other"
(它仍然由 go_package
选项带来并且仍然是错误的因为这仍然是一个不存在的模块 - 这些是生成的文件......为什么我要创建这些模块?)。
很酷的事情是,有了这个 go_opt,访问生成的类型看起来更正常了import pb "company.tld/proj/subproj/projpb"
。
我终于试过了
- 在 .proto 文件中的 go_package 选项上使用本地导入路径(在构建时被拒绝,因为生成的 protobuffer 对象中会有一个
import other "./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 对象,或者在包中生成包,我只是想使用它们。它们不是模块,所以请为生成的对象提供正确的导入路径,让我在我的代码中使用它们»。
[更新 01]
我尝试了 go_opt=paths=source_relative
(受此 ticket 的启发)。
我在Makefile中创建了文件夹,protoc在里面生成文件。
备注:
- 生成的原型使用由
go_package
选项指定的完整路径来相互关联。 - 只要
go_package
选项需要完整路径,Go (go mod tidy/vendor) 就会在创建的文件夹中搜索 go.mod 文件,其中包含生成的原型。
告诉 Go 我不是在寻找模块,但仍然满足 protobuffer 文件中 go_package 选项的完整路径约束的正确方法是什么?
解决方法
在 proto 文件中多次更改 go_package
选项后,更改 protoc 编译器命令上的 go_opt
,这是我发现使用生成的 protobuffers 编译项目的唯一方法,尊重每个 Go 约束都是通过动态创建 go.mod 文件...
final proto «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(为生成的 proto 文件创建一个 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
)。
使用生成的原型的导入语句在主项目中运行良好。
我希望有一个更简单、更漂亮的解决方案,但唉...
对不起,感谢那些考虑过它的人!
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。