英文:
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_package
或package
的更改。如果我使用"src/another_proto.proto"
作为导入路径,linter会说路径“未找到或存在错误”。然后,当我使用"proto/src/another_proto.proto"
时,错误就会消失,但是当我运行protoc
时,它会抛出之前的“未找到...”错误。
更新:
嗨,这是一个非常令人恼火的情况,可能是由于protoc go
的一个错误或缺失的功能引起的。所以我试图将命令调整为将存根放置在一个更合理的目录中,所以我修改了protoc
命令,使--proto_path
更长,因为我认为这样做可以减少最终输出路径中的文件夹数量。每次我向--proto_path
添加一个文件夹时,我也修改了protobuf
的go_package
和import
,因为会出现关于“未找到”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 protobuf
s 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 protobuf
s, 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 protobuf
s 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}/include
是protoc
中的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 Timestamp
在 google.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 protobuf
s 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 protobuf
s, 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论