protoc存在很多问题。

huangapple go评论79阅读模式
英文:

Tons of issue with protoc

问题

我正在尝试让protoc将生成的文件输出到包含所有.proto文件的文件夹内的一个文件夹中。首先,我希望我们能够达成共识,这些命令很令人困惑,不太直观。我在这个文档中找到了一些解释,但是事情并没有按照预期进行。我的protobuf文件位于一个名为proto的文件夹内。我有一些导入另一个protobuf文件的protobuf文件。我是这样导入它们的:

package proto;

option go_package = "github.com/me/golang-grpc-server/proto";

import "proto/another_proto.proto";

message Proto1 {
    ...
    ...
    repeated proto.AnotherProto another_proto = 6;
}

这样应该可以工作,对吗?然而,protoc不知道该怎么处理它。如果我运行以下命令:

protoc --proto_path=proto --go_out=out --go_opt=paths=source_relative --go-grpc_out=out --go-grpc_opt=paths=source_relative proto1.proto another_proto.proto

protoc会给我报错:

proto/another_proto.proto: File not found.
proto1.proto:7:1: Import "proto/another_proto.proto" was not found or had errors.
proto1.proto:16:14: "proto.AnotherProto" is not defined.
proto1.proto:40:14: "AnotherProto" is not defined.

如果我将another_proto.proto放在proto1.proto之前,我会得到以下错误:

proto/another_proto.proto: File not found.
proto1.proto:7:1: Import "proto/another_proto.proto" was not found or had errors.
proto1.proto:16:14: "proto.AnotherProto" seems to be defined in "another_proto.proto", which is not imported by "proto1.proto".  To use it here, please add the necessary import.
proto1.proto:40:14: "proto.AnotherProto" seems to be defined in "another_proto.proto", which is not imported by "proto1.proto".  To use it here, please add the necessary import.

如果我去掉--proto_path=proto并在每个protobuf前面加上proto\,我就不会得到那些错误了,但是生成的文件会被放在out\proto文件夹中,这非常令人困惑,因为我以为--go_opt=paths=source_relative告诉protoc将输出放在protobuf所在的位置,就像这里所说的那样,而在我的情况下就是proto文件夹。

如果指定了paths=source_relative标志,输出文件将被放置在与输入文件相同的相对目录中。例如,输入文件protos/buzz.proto将生成一个输出文件protos/buzz.pb.go

我尝试使用--go_out=proto/out,但是它会将生成的文件放在proto/out/proto中。我猜这是因为protobuf前面的proto/导致的,因为前面提到的错误,我可能暂时只能使用out/proto,因为这是目前最合理的路径。

更新:

我不太确定我是否正确理解了@Daz的意思,但是这是我对你指示的理解。

所以我对Proto1做了以下更改:

package src;

option go_package = "github.com/me/golang-grpc-server/proto/src";

import "src/another_proto.proto";

message Proto1 {
    ...
    ...
    repeated src.AnotherProto another_proto = 6;
}

我将所有其他的protobuf文件移动到了proto文件夹下的一个文件夹中,并且当然也改变了每个文件的go_package以在末尾添加/src,并且还修改了导入部分以反映这些更改。然后,我将我的protoc命令修改为:

protoc --proto_path=proto/src --go_out=./out --go_opt=paths=source_relative --go-grpc_out=./out --go-grpc_opt=paths=source_relative proto/src/*.proto

这里的*很棒。同样的事情,在这里进行了修改以反映对文件夹结构的更改。不幸的是,这些更改不起作用,从protobuf本身开始。import似乎不承认go_packagepackage的更改。如果我使用"src/another_proto.proto"作为导入路径,linter会说路径“未找到或存在错误”。然后,当我使用"proto/src/another_proto.proto"时,错误就会消失,但是当我运行protoc时,它会抛出之前的“未找到...”错误。

更新:

嗨,这是一个非常令人恼火的情况,可能是由于protoc go的一个错误或缺失的功能引起的。所以我试图将命令调整为将存根放置在一个更合理的目录中,所以我修改了protoc命令,使--proto_path更长,因为我认为这样做可以减少最终输出路径中的文件夹数量。每次我向--proto_path添加一个文件夹时,我也修改了protobufgo_packageimport,因为会出现关于“未找到”protobuf的错误。在我的当前命令中,protobuf看起来像这样:

package src;

option go_package = "github.com/me/golang-grpc-server/proto/src";

import "another_proto.proto";

message Proto1 {
    ...
    ...
    repeated AnotherProto another_proto = 6;
}

看起来很漂亮。但是,linter不喜欢它。我猜这是因为,正如@Daz提到的,protobuf实际上并没有使用Golang的包系统,所以linter不知道如何引用这个import。现在,我有一个我认为应该构建的项目,但有2个文件有错误。这是我的当前命令,它将存根放置在与proto文件夹相同级别的另一个文件夹中:

protoc --proto_path=proto/src --go_out=out --go_opt=paths=source_relative --go-grpc_out=out --go-grpc_opt=paths=source_relative proto/src/*.proto
英文:

I'm trying to make protoc output the generated files to a folder inside the folder where all my .proto files are. First of all, I hope we can agree that this commands are confusing and isn't very intuitive. I stumbled upon this documentation which seems to explain everything, but things doesn't work. So my protobuf files are inside a folder called proto. I have a few protobuf files that imports another protobuf file. I import them like this:

package proto;

option go_package = "github.com/me/golang-grpc-server/proto";

import "proto/another_proto.proto";

message Proto1 {
    ...
    ...
    repeated proto.AnotherProto another_proto = 6;
}

That should work right? Well, protoc doesn't know what to do with it. If I run this command.

protoc --proto_path=proto --go_out=out --go_opt=paths=source_relative --go-grpc_out=out --go-grpc_opt=paths=source_relative proto1.proto another_proto.proto

protoc would give me these errors:

proto/another_proto.proto: File not found.
proto1.proto:7:1: Import "proto/another_proto.proto" was not found or had errors.
proto1.proto:16:14: "proto.AnotherProto" is not defined.
proto1.proto:40:14: "AnotherProto" is not defined.

If I then put another_proto.proto in front of proto1.proto, I get these errors:

proto/another_proto.proto: File not found.
proto1.proto:7:1: Import "proto/another_proto.proto" was not found or had errors.
proto1.proto:16:14: "proto.AnotherProto" seems to be defined in "another_proto.proto", which is not imported by "proto1.proto".  To use it here, please add the necessary import.
proto1.proto:40:14: "proto.AnotherProto" seems to be defined in "another_proto.proto", which is not imported by "proto1.proto".  To use it here, please add the necessary import.

Then if I remove --proto_path=proto and add proto\ infront of every protobuf, I won't get those errors, but the generated files would be placed inside out\proto which is very confusing because I thought --go_opt=paths=source_relative tells protoc to place the outputs inside where the protobufs are placed, as stated here which in my case is the folder proto.

> If the paths=source_relative flag is specified, the output file is
> placed in the same relative directory as the input file. For example,
> an input file protos/buzz.proto results in an output file at
> protos/buzz.pb.go.

I tried using --go_out=proto/out, but annoyingly it placed the generated files inside proto/out/proto. I'm guessing it's because of the proto/ infront of the protobufs, because of the previously mentioned errors, I might have to stick to out/proto for now as it's the currently most sensible path right now.

UPDATE:

I'm not quite sure if I understand @Daz correctly, but here's what my interpretation of your instructions.

So I made this changes to Proto1:

package src;

option go_package = "github.com/me/golang-grpc-server/proto/src";

import "src/another_proto.proto";

message Proto1 {
    ...
    ...
    repeated src.AnotherProto another_proto = 6;
}

I moved all the other protobufs inside a folder under proto and of course changed the go_package of each one to have /src at the end and changed the import parts as well to reflect the change. Then I modified my protoc command to this:

protoc --proto_path=proto/src --go_out=./out --go_opt=paths=source_relative --go-grpc_out=./out --go-grpc_opt=paths=source_relative proto/src/*.proto

That * was awesome. Same thing here, modified it to reflect the changes made to the folder structure. Unfortunately, the changes doesn't work, starting from the protobufs themselves. import doesn't seem to acknowledge the change in go_package or the package. If I use "src/another_proto.proto" as the import path, the linter would say that the path was "not found or has errors". Then when I use "proto/src/another_proto.proto", the errors would be gone, but when I run protoc, it would throw the previous "not found ..." errors.

UPDATE:

Man, this is some pretty infuriating situation which might be caused by a bug or just missing functionality on protoc go. So I was trying to push the command to place the stubs to a more logical I guess directory and so I modified the protoc command to have a longer --proto_path because I thought doing that could shave folders to the final output path. Each time I added a folder to the --proto_path, I modified the protobuf's go_package and import as well because errors about "not found" protobuf would appear. On my current command, the protobufs would look like this:

package src;

option go_package = "github.com/me/golang-grpc-server/proto/src";

import "another_proto.proto";

message Proto1 {
    ...
    ...
    repeated AnotherProto another_proto = 6;
}

which looks quite pretty. But, linter doesn't like it. I guess because of the fact, as @Daz mentioned, that protobuf doesn't really use Golangs package system, the linter doesn't know how to reference the import. Now, I have a project that I think should build, but has 2 files with errors. Here's my current command, this places the stubs to another folder in the same level as the proto folder:

protoc --proto_path=proto/src --go_out=out --go_opt=paths=source_relative --go-grpc_out=out --go-grpc_opt=paths=source_relative proto/src/*.proto

答案1

得分: 4

是的,这是具有挑战性的。

好消息是:一旦你理解了这个过程,它就会一直有效。我在Linux上使用Golang、Rust和Python采用以下方法,效果很好。

1. Protobuf源文件、包和文件夹

Protobuf源文件定义了一个包层次结构。

我将以Google的Well-known Types (WKT)为例。这些文件位于一个名为google.protobuf的Protobuf包中。当你安装protoc时,通常这些文件也会被安装在一个名为include的文件夹中,每个WKT(例如Timestamp.proto)存在于一个从package名称派生的文件系统文件夹中:

{SOME-FOLDER}/include/google/protobuf/timestamp.proto

因此,Protobuf包路径映射到文件系统文件夹路径,但它们必须以某个文件系统文件夹(例如,在这种情况下是include)为锚点。这个根文件夹{SOME-FOLDER}/includeprotoc中的proto_path值。其他希望在这个根文件夹下导入proto的proto源文件中的包引用是相对于这个proto_path值的根路径,例如import "google/protobuf/timestamp.proto";(没有include,但文件夹路径必须反映这个根路径)。

2. 编译后的proto,即"stubs",包和文件夹

Protobuf支持编译成多种语言,每种语言都有其独特的特点。由于你问的是关于Golang的,我将提供Golang的答案。

Protobuf和Golang在包命名上有相似之处,但重要的是要记住,在许多情况下,Protobuf源文件中的package并不一定映射到语言的包。

对于Golang,你必须告诉protoc你想要使用的Golang包:

option go_package="example.com/path/to/package";

WKT Timestampgoogle.golang.org/protobuf/types/known/timestamppb 包中由Google的 Timestamppb 实现,这与 google.protobuf 完全无关。

所以...

在你的例子中,我认为你应该删除你的protobuf源文件中的proto引用,因为这个文件夹代表你的项目层次结构,而不是protobuf的package层次结构。

要么像你已经有的那样,文件系统文件夹proto的父文件夹成为你的proto_path,因为你的包层次结构从proto开始(或者你有proto/proto,这会让人困惑):

package proto;

import "proto/another_proto.proto";

要么,如我建议的那样,选择一个不同的包名foo(或者删除包名),继续以${SOME_FOLDER}/proto作为proto_path的锚点,并修正导入机制:

proto
└── foo
    ├── another_proto.proto
    └── some.proto

以及 some.proto

package foo;

// Golang包名是 {GO-MODULE}/{GO-PACKAGE}
// 通常 GO-PACKAGE={PROTO_PATH}/{PACKAGE},但不一定是这样
// 你可以在这里随意更改 "${MODULE}/some/random/bar"
option go_package="github.com/me/golang-grpc-server/proto/foo";

// `another_proto.proto` 与同一个 `package` 和同一个文件夹
import "foo/another_proto.proto";

// Proto1 是一个...
message Proto1 {
    ...
    ...
    // 类型引用必须包括完整的 `package` 路径,例如 `foo`
    repeated foo.AnotherProto another_proto = 6;
}

使用 --go_opt=paths=source_relative 是常见的,但我更喜欢以下方式(主要是因为对我来说更有意义):

# Golang模块名
# Protobuf源文件将是 `option go_package="${MODULE}/proto/foo";`
MODULE="github.com/me/golang-grpc-server"

go mod init ${MODULE}

# proto_path 将源文件定位在 "proto" 上
# 单个proto反映了文件夹中的包
# 使用 "foo" 作为 `package` 名称需要 proto/foo/...
protoc \
--proto_path=${PWD}/proto
--go_out=${PWD} \
--go_opt=module=${MODULE} \
${PWD}/proto/foo/some.proto \
${PWD}/proto/foo/another_proto.proto

因为一切都在 ${PWD}/proto/foo 下,你也可以使用 ${PWD}/proto/foo/*.proto

结果为:

.
├── go.mod
├── go.sum
└── proto
    └── foo
        ├── another_proto.pb.go
        ├── another_proto.proto
        ├── some.pb.go
        └── some.proto
英文:

Yes, it's challenging.

The good news: once you understand the process, it will work consistently. I use the following approach with Golang, Rust and Python on Linux and it has worked well for me.

1. Protobuf sources, packages and folders

Protobuf source files define a package (!) hierachy.

I'll use Google's Well-known Types (WKT) by way of example. These files are in a protobuf (!) package google.protobuf. When you install protoc, generally these files are installed too in a folder called include and each WKT e.g. Timestamp.proto exists in a file-system folder derived from the package name (!):

{SOME-FOLDER}/include/google/protobuf/timestamp.pro

So Protobuf package paths map to file system folder paths but they must be anchored on some file-system folder (e.g. include in this case). This root folder {SOME-FOLDER}/include is the proto_path value in protoc. And package references from other proto sources that wish to import protos under this root folder are relative to this proto_path value as the root e.g. import "google/protobuf/timestamp.proto"; (no include but the folder path from this root must be reflected).

2. Compiled protos aka "stubs", packages and folders

Protobuf supports compilation to multiple languages and each has its unique quirks. Since you asked about Golang, I'll provide the Golang answer.

Protobuf and Golang share similarities in the package naming but it's important to remember that -- in many cases -- the protobuf source (!) package does not necessarily map to the language's packages.

For Golang, you must tell protoc the Golang packages that you want to use:

option go_package="example.com/path/to/package";

The WKT Timestamp is implemented by Google Timestamppb in google.golang.org/protobuf/types/known/timestampb package (entirely unrelated to google.protobuf

So...

In your example, I think you should remove on the proto references in your protobuf sources since this folder represents your project hierarchy not the protobuf package hierarchy.

Either as you have (!) the parent folder (!) of the file system folder proto becomes your proto_path because your package hierarchy starts at proto (or you have proto/proto which would be confusing):

package proto;

import "proto/another_proto.proto";

Or, as I recommend (!) you choose either a different package foo (or remove the package), continue to anchor proto_path on ${SOME_FOLDER}/proto and correct the import mechanism:

proto
└── foo
    ├── another_proto.proto
    └── some.proto

And some.proto:

package foo;

// Golang package is {GO-MODULE}/{GO-PACKAGE}
// Generally GO-PACKAGE={PROTO_PATH}/{PACKAGE} but it need not be
// You can change "${MODULE}/some/random/bar" here without problem
option go_package="github.com/me/golang-grpc-server/proto/foo";

// `another_proto.proto` is in the same `package` and thus same folder
import "foo/another_proto.proto";

// Proto1 is a message that ...
message Proto1 {
    ...
    ...
    // Type references must include the full `package` path e.g. `foo`
    repeated foo.AnotherProto another_proto = 6;
}

Using --go_opt=paths=source_relative is common but my preference (mostly because it makes sense to me) is the following:

# Golang Module name
# Protobuf sources will be `option go_package="${MODULE}/proto/foo";`
MODULE="github.com/me/golang-grpc-server"

go mod init ${MODULE}

# proto_path roots the source on "proto"
# Individual protos reflect the package in the folder under "proto"
# Using "foo" as the `package` name requires proto/foo/...
protoc \
--proto_path=${PWD}/proto
--go_out=${PWD} \
--go_opt=module=${MODULE} \
${PWD}/proto/foo/some.proto \
${PWD}/proto/foo/another_proto.proto

Because everything is under ${PWD}/proto/foo, you can just ${PWD}/proto/foo/*.proto too.

Yields:

.
├── go.mod
├── go.sum
└── proto
    └── foo
        ├── another_proto.pb.go
        ├── another_proto.proto
        ├── some.pb.go
        └── some.proto

答案2

得分: 0

最后,感谢 @DazWilkin 的初始答案,我终于找到了我想要的命令。下面是命令:

protoc --proto_path=proto/src --go_out=./proto/out --go_opt=paths=source_relative --go-grpc_out=./proto/out --go-grpc_opt=paths=source_relative proto/src/*.proto

我认为这里最重要的部分是 --proto_path。将其设置为我的 protobuf 所在的确切文件夹,使我能够使用任何我想要的 --go_out。因此,如果 --proto_path 不是确切的文件夹,protoc 就不需要添加额外/前导文件夹。

然后,在你的 protobuf 文件中,你需要这样做:

package src;

option go_package = "github.com/me/golang-grpc-server/proto/src";

import "another_proto.proto";

message Proto1 {
...
...
repeated AnotherProto another_proto = 6;
}

正如我在上次更新中提到的,linter 似乎不喜欢这样,可能是因为它不知道如何处理这种设置中的 protobuf

英文:

Finally, thanks to @DazWilkin's initial answer, I finally found the command to do what I wanted to do. Here it is:

protoc --proto_path=proto/src --go_out=./proto/out --go_opt=paths=source_relative --go-grpc_out=./proto/out --go-grpc_opt=paths=source_relative proto/src/*.proto

I think the most important bit here is the --proto_path. Making it have the exact folder where my protobufs are, made me able to use whatever --go_out that I wanted. So protoc won't need to add the additional/leading folder that is missing in the --proto_path if it isn't the exact one.

Then on your protobufs, you'll have to do this:

package src;

option go_package = "github.com/me/golang-grpc-server/proto/src";

import "another_proto.proto";

message Proto1 {
    ...
    ...
    repeated AnotherProto another_proto = 6;
}

As mentioned on my last update, linter doesn't seem to like this because it doesn't know how to handle protobuf in this kind of setup probably.

huangapple
  • 本文由 发表于 2023年5月21日 00:24:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/76296212.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定