proto包和实际目录结构有什么关系

haf*_*n96 5 java protocol-buffers proto

我已经在线阅读了有关 protobuf 的官方文档和教程,但我仍然不太明白 proto 定义中的包名称和导入路径与实际目录结构之间的关系。

\n

为了清楚地说明我的问题,假设这是我的项目结构:

\n
root\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 project\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 protos\n\xe2\x94\x82\xc2\xa0\xc2\xa0     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 common\n\xe2\x94\x82\xc2\xa0\xc2\xa0     \xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 common.proto\n\xe2\x94\x82\xc2\xa0\xc2\xa0     \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 custom\n\xe2\x94\x82\xc2\xa0\xc2\xa0         \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 app.proto\n\xe2\x94\x82\xc2\xa0\xc2\xa0         \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 util\n\xe2\x94\x82\xc2\xa0\xc2\xa0             \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 util.proto           \n
Run Code Online (Sandbox Code Playgroud)\n

通用原型

\n
root\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 project\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 protos\n\xe2\x94\x82\xc2\xa0\xc2\xa0     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 common\n\xe2\x94\x82\xc2\xa0\xc2\xa0     \xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 common.proto\n\xe2\x94\x82\xc2\xa0\xc2\xa0     \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 custom\n\xe2\x94\x82\xc2\xa0\xc2\xa0         \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 app.proto\n\xe2\x94\x82\xc2\xa0\xc2\xa0         \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 util\n\xe2\x94\x82\xc2\xa0\xc2\xa0             \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 util.proto           \n
Run Code Online (Sandbox Code Playgroud)\n

app.proto,它引用common.proto(在另一个单独的目录中)和util.proto(在子目录中)

\n
syntax = "proto3";\n\n// how should I decide the package for this file based on my project structure?\npackage project.protos.common;\n\nmessage Common {\n    // ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n

实用程序原型

\n
syntax = "proto3";\n\npackage project.protos.custom;\n\nimport "project/protos/common/common.proto";\nimport "project/protos/custom/util/util.proto";\nimport "google/protobuf/timestamp.proto";\n\nmessage App {\n    project.protos.common.Common common = 3;\n    project.protos.custom.util.Util util = 4;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

经过多次试验,结果发现,如果我在目录下运行 protoc 命令,使用命令:

\n
syntax = "proto3";\n\npackage project.protos.custom.util;\n\noption java_multiple_files = true;\n\nmessage Util {\n    // ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n

然后导入工作正常,并且每个原始文件都编译没有错误。但是,假设我不是在根目录运行协议,而是在子目录项目运行协议,使用类似的命令:

\n
protoc -I . --java_out=. project/protos/**/*.proto\n
Run Code Online (Sandbox Code Playgroud)\n

编译将失败并出现以下错误:

\n
project/protos/common/common.proto: File not found.\nproject/protos/custom/util/util.proto: File not found.\nprotos/custom/app.proto:6:1: Import "project/protos/common/common.proto" was not found or had errors.\nprotos/custom/app.proto:8:1: Import "project/protos/custom/util/util.proto" was not found or had errors.\nprotos/custom/app.proto:19:5: "project.protos.common.Common" seems to be defined in "protos/common/common.proto", which is not imported by "protos/custom/app.proto".  To use it here, please add the necessary import.\nprotos/custom/app.proto:20:5: "project.protos.custom.util.Util" is not defined.\n
Run Code Online (Sandbox Code Playgroud)\n

由于我大部分时间都在使用 Java,所以我目前正在考虑 package & import 在 protobuf 中的工作方式(相对于实际的目录结构)类似于 package & import 在 Java 中的工作方式,尊重类路径。

\n

也就是说,如果我想编译一个包指定为“project.protos.common”的proto,那么我应该在project dir的父目录上运行protoc命令,其中project/protos/common是有效路径。

\n

同样,如果我在另一个 proto 文件中导入 proto,并将导入路径设置为“project/protos/custom/util/util.proto”,那么我还应该确保在运行 protoc 命令时的目录中,路径项目/protos/custom/util/util.proto 存在。

\n

我目前猜测无论我们输入 -I 选项的路径是什么,protoc 都会以与 Java 类路径或 python 搜索路径相同的方式对待它们。导入后,protoc 将按照 import 语句指示的相对路径在每个指定的 -I 路径上查找导入的文件。

\n

然而,在网上搜索了类似的问题并阅读了一些文章后,现在我的印象是protobuf中的package和import实际上比我想象的要灵活得多。我这里的理解有错吗?

\n

Daz*_*kin 3

我来间接回答一下:

  1. 包名与目录对应
  2. 一个包可以跨越单个目录中的多个文件
  3. (但是)即使在同一个包中,单个文件也必须导入 Protos
  4. 通过某些组织 ID 来命名空间|范围包很有用(例如googlefor timestamp
  5. 相关包的层次结构(例如,google.protobuf清单为google/protobuf/...)与其文件系统位置无关。
  6. 尽管包层次结构是固定的,但在生成特定于语言的绑定时,“放置”具有灵活性。

我将尝试提供每个示例:

protocinclude目录为例,Packagegoogle.protobuf清楚地将其标识为google表示“东西”的原始 Package protobuf

包必须在目录中表示为,google/protobuf但它在系统中的位置是任意的并且没有依赖关系(请参阅 参考资料--proto_path)。

包eg中的文件api.proto必须导入eg type.proto(使用完整的命名空间路径(即google/protobuf/type.proto),即使它们共享一个Package。

运行时protoc .... foo.proto,如果foo.proto导入google/protobug/api.protoprotoc需要一些查找包的方法,因此protoc --proto_path=path/to/google是必需的(尽管如果protoc在路径中,则include在Linux上不需要添加目录)。所有导入都必须有类似的--proto_path'd。

由于原始文件中的protoc标志,输出文件和(更重要的是)用于生成输出的命名空间具有一定的灵活性。option [langauge]_package

我不熟悉 Java,但对于 Golang,我可以使用option go_package来帮助我指定一个 Golang 模块名称 ( github.com/[org]/[repo]) 以确保生成的文件放置在存储库中,以便其他 Golang 模块正确导入它们。