protoc –go_opt=paths=source_relative vs –go-grpc_opt=paths=source_relative

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

protoc --go_opt=paths=source_relative vs --go-grpc_opt=paths=source_relative

问题

我正在努力弄清楚protoc命令和Go插件的使用方法。

以下是这些选项的区别:

  • --go_out=.:指定编译器将Go输出写入的目录。
  • --go_opt=paths=source_relative:指定生成的Go文件中导入路径的相对路径。
  • --go-grpc_out=.:指定编译器将Go gRPC输出写入的目录。
  • --go-grpc_opt=paths=source_relative:指定生成的Go gRPC文件中导入路径的相对路径。

如果使用--go_opt选项,将生成<name>.pb.go文件;如果使用--go-grpc_opt选项,将生成<name>_grpc.pb.go文件。那么为什么还需要--go_out选项呢?

关于--go-grpc_opt选项,protoc文档中没有提到。而且,protoc -h命令中也没有将Go列为OUT_DIR选项。

注意:你可以按照这个文档进行安装。

英文:

I am having a hard time figuring out protoc command and go plugin.

What is the different between:

protoc \
   # Directory where you want the compiler to write your Go output.
   --go_out=.
   # vs ?
   --go_opt=paths=source_relative
   # vs ?
   --go-grpc_out=.
   # vs ?
   --go-grpc_opt=paths=source_relative

If --go_opt generate

  • &lt;name&gt;.pb.go file

and --go-grpc_opt generate

  • &lt;name&gt;_grpc.pb.go file

why even have --go_out?

Can you shed some light on protoc - the doc do not say anything about --go-grpc_opt?

And and protoc -h do not even list go as an OUT_DIR?

Note: I install using this doc

答案1

得分: 15

为什么要使用--go_out?

首先要理解的是,gRPC和Protocol Buffers不是同一回事,gRPC使用Protocol Buffers,但还有其他框架也在使用它们。所以我们需要生成两者的代码。

现在,为了生成与Protocol Buffers相关的代码,你需要使用--go_out,就像你提到的那样。但是对于gRPC代码,你需要使用--go-grpc_out

--go-grpc_opt生成_grpc.pb.go文件吗?

不是,是--go-grpc_out生成的。

你能解释一下protoc吗?文档中没有提到--go-grpc_opt?

在生成代码之前,你可以传递一些选项,这就是--go_opt--go-grpc_opt的作用。第一个选项用于Protobuf生成,第二个选项用于gRPC生成。选项比较隐晦,没有官方的完整列表,但你使用了source_relative(告诉protoc使用相对路径)作为路径,还有module选项(帮助protoc知道要在哪个文件夹中生成go模块名称)。

protoc -h甚至没有列出go作为OUT_DIR?

最后,protoc并不官方支持将Go作为输出语言,你需要安装一个外部插件,这就是为什么protoc -h不显示--go_out的原因。相关讨论可以在这里找到。

英文:

> why even have --go_out?

So, the thing to understand here is that gRPC is not the same as Protocol Buffers, gRPC uses Protocol Buffers but there are other frameworks that are using them as well. So we need to generate both.

Now, in order to generate the Protocol buffer related code, you need to use --go_out as you mentioned. but for the gRPC code you need to use --go-grpc_out.

> and --go-grpc_opt generate <name>_grpc.pb.go file

No, --go-grpc_out does.

> Can you shade some light on protoc - the doc do not stay anything about --go-grpc_opt?

Then, before the generation of code you can pass some options and that's what --go_opt and --go-grpc_opt are for. The first one passes options for Protobuf generation and the second one for gRPC generation. Options are pretty obscure, and there is not official list of all of them, but you used source_relative (which tell protoc to use relative paths) for path and there is also the module option (which help protoc know the go module name to generate in proper folder)

> And and protoc -h do not even list go as an OUT_DIR?

And finally, protoc doesn't officially support Go as output, you need to install an external plugin and that's why protoc -h doesn't show --go_out. A related discussion can be found here.

答案2

得分: 1

protoc编译器支持不同的标志或选项,你在命令行上使用的标志决定了生成的Go代码的放置位置。

关于这些标志的官方文档(至少对于paths=source_relativemodule=$PREFIX)并不是很清楚,可能很难理解。

paths=source_relative

官方文档中的解释如下:

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

上述说明可能会让人感到困惑,因为它没有给出完整的上下文和文件在磁盘上的文件夹布局。

对我来说,这个标志的意思是,当使用时,它会在由--go_out指定的目录中生成Go代码,并确保生成的Go代码文件的目录树结构与proto文件的目录树结构相匹配。

假设我们有以下目录结构:

❯ tree
.
├── go.mod
└── src
    └── protos
        ├── bar
        │   └── baz.proto
        └── foo.proto

5 directories, 3 files

考虑以下示例:

protoc --proto_path=src/protos --go_out=. --go_opt=paths=source_relative foo.proto bar/baz.proto

❯ ls -l
drwxrwxr-x  3 rbhanot  staff    96 Jul  5 20:52 bar
-rw-rw-r--  1 rbhanot  staff  3912 Jul  5 20:52 foo.pb.go
-rw-rw-r--  1 rbhanot  staff    45 Jul  5 16:12 go.mod
drwxr-xr-x  3 rbhanot  staff    96 Jul  5 18:12 src

❯ ls -l bar
-rw-rw-r--  1 rbhanot  staff  4053 Jul  5 20:52 baz.pb.go

在上面的示例中,我们设置了--proto_path=src/protos,这意味着实际proto文件的目录路径为foo.protobar/baz.proto,而pb文件被创建在当前目录(--go_out=.)中,分别命名为foo.pb.gobar/baz.pb.go

现在让我们将上面命令中的--proto_path更改为src,看看会发生什么。

❯ protoc --proto_path=src --go_out=. --go_opt=paths=source_relative protos/foo.proto protos/bar/baz.proto

❯ ls -l
-rw-rw-r--  1 rbhanot  staff   45 Jul  5 16:12 go.mod
drwxrwxr-x  4 rbhanot  staff  128 Jul  5 21:00 protos
drwxr-xr-x  3 rbhanot  staff   96 Jul  5 18:12 src

~/dummy_go  
❯ ls -l protos
drwxrwxr-x  3 rbhanot  staff    96 Jul  5 21:00 bar
-rw-rw-r--  1 rbhanot  staff  4158 Jul  5 21:00 foo.pb.go

这次在protos目录下创建了一个新的protos目录,其中包含生成的go文件,为什么?因为当我们将--proto-path=src更改为src时,proto文件的目录结构变为protos/foo.protoprotos/bar/baz.proto

现在让我们最后在这里也加上--go_out,看看会发生什么。

❯ mkdir out
❯ protoc --proto_path=src --go_out=out --go_opt=paths=source_relative protos/foo.proto protos/bar/baz.proto
❯ ls -l        
total 8
-rw-rw-r--  1 rbhanot  staff  45 Jul  5 16:12 go.mod
drwxrwxr-x  3 rbhanot  staff  96 Jul  5 21:05 out
drwxr-xr-x  3 rbhanot  staff  96 Jul  5 18:12 src

~/dummy_go  
❯ ls -lR out
drwxrwxr-x  4 rbhanot  staff  128 Jul  5 21:05 protos

out/protos:
drwxrwxr-x  3 rbhanot  staff    96 Jul  5 21:05 bar
-rw-rw-r--  1 rbhanot  staff  4158 Jul  5 21:05 foo.pb.go

out/protos/bar:
-rw-rw-r--  1 rbhanot  staff  4298 Jul  5 21:05 baz.pb.go

这与上一个示例完全相同,只是我们提供了一个自定义目录来保存生成的代码。

module=$PREFIX

> 如果指定了module=$PREFIX标志,输出文件将放置在以Go包的导入路径命名的目录中,但输出文件名中将删除指定的目录前缀。例如,具有Go导入路径为example.com/project/protos/fizzexample.com/project作为模块前缀的输入文件protos/buzz.proto将生成一个输出文件protos/fizz/buzz.pb.go。在模块路径之外生成任何Go包将导致错误。此模式对于直接输出生成的文件到Go模块非常有用。

让我们看看这个标志的实际效果,考虑以下proto文件:

syntax = "proto3";

package foo;

option go_package = "github.com/rbhanot/dummy-app/greet";

message Foo {
    string name = 1;
}

请注意,我故意从go_package中删除了src,以展示此标志的行为。

❯ protoc --proto_path=src --go_out=. --go_opt=module=github.com/rbhanot/dummy-app protos/foo.proto protos/bar/baz.proto

❯ ls -l
-rw-rw-r--  1 rbhanot  staff   45 Jul  5 16:12 go.mod
drwxrwxr-x  3 rbhanot  staff   96 Jul  5 21:14 greet
drwxr-xr-x  4 rbhanot  staff  128 Jul  5 21:14 src

❯ ls -l greet
-rw-rw-r--  1 rbhanot  staff  4274 Jul  5 21:17 baz.pb.go
-rw-rw-r--  1 rbhanot  staff  4133 Jul  5 21:17 foo.pb.go

我们看到了greet目录和其中的pb文件。因此,它基本上从go_package中删除了前缀(github.com/rbhanot/dummy-app),然后在当前目录(--go_out=.)中创建了go包。

现在让我们将go_package更改为github.com/rbhanot/dummy-app/src/greet,运行上述命令会得到以下结果:

❯ protoc --proto_path=src --go_out=. --go_opt=module=github.com/rbhanot/dummy-app protos/foo.proto protos/bar/baz.proto

❯ ls -l src
drwxrwxr-x  4 rbhanot  staff  128 Jul  5 21:22 greet
drwxr-xr-x  4 rbhanot  staff  128 Jul  5 17:37 protos

这次我们看到在src目录下生成了greet包。

最后,让我们在这里也加上--go_out=out

❯ mkdir out

❯ protoc --proto_path=src --go_out=out --go_opt=module=github.com/rbhanot/dummy-app protos/foo.proto protos/bar/baz.proto

❯ ls -l out     
total 0
drwxrwxr-x  3 rbhanot  staff  96 Jul  5 21:24 src

❯ ls -lR out
total 0
drwxrwxr-x  3 rbhanot  staff  96 Jul  5 21:24 src

out/src:
total 0
drwxrwxr-x  4 rbhanot  staff  128 Jul  5 21:24 greet

out/src/greet:
total 32
-rw-rw-r--  1 rbhanot  staff  4298 Jul  5 21:24 baz.pb.go
-rw-rw-r--  1 rbhanot  staff  4158 Jul  5 21:24 foo.pb.go

这次生成的代码不是放在src中,而是放在out中,注意目录结构src/greet/foo.pb.go

希望这样对于这些标志的行为更加清晰(至少对我来说是这样的)。

英文:

The protoc compiler supports different flags or options and the flags you use on the command line decides where the generated go code will be placed.

The official docs for these flags(atleast for paths=source_relative and module=$PREFIX) are not very clear and can be hard to understand.

paths=source_relative

Here is what official docs says

> 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.

The above statement might be confusing because it is not giving complete context and folder layout of how the files are placed on the disk.

To me what this flag means is that when used generates the go code in the directory specified by --go_out and ensures that directory tree structure of the generated go code files matches the directory tree structure of proto files.

Let's say we have the following directory structure

❯ tree
.
├── go.mod
└── src
    └── protos
        ├── bar
        │   └── baz.proto
        └── foo.proto

5 directories, 3 files

Consider the following examples

protoc --proto_path=src/protos --go_out=. --go_opt=paths=source_relative foo.proto bar/baz.proto

❯ ls -l
drwxrwxr-x  3 rbhanot  staff    96 Jul  5 20:52 bar
-rw-rw-r--  1 rbhanot  staff  3912 Jul  5 20:52 foo.pb.go
-rw-rw-r--  1 rbhanot  staff    45 Jul  5 16:12 go.mod
drwxr-xr-x  3 rbhanot  staff    96 Jul  5 18:12 src

❯ ls -l bar
-rw-rw-r--  1 rbhanot  staff  4053 Jul  5 20:52 baz.pb.go

In the above example we set the --proto_path=src/protos which means the directory path for the actual proto files would be foo.proto and bar/baz.proto, and the pb files were created in the current directory (--go_out=.) as foo.pb.go and bar/baz.pb.go.

Now let's change the --proto_path in the above command to src and see what happnes.

❯ protoc --proto_path=src --go_out=. --go_opt=paths=source_relative protos/foo.proto protos/bar/baz.proto

❯ ls -l
-rw-rw-r--  1 rbhanot  staff   45 Jul  5 16:12 go.mod
drwxrwxr-x  4 rbhanot  staff  128 Jul  5 21:00 protos
drwxr-xr-x  3 rbhanot  staff   96 Jul  5 18:12 src

~/dummy_go  
❯ ls -l protos
drwxrwxr-x  3 rbhanot  staff    96 Jul  5 21:00 bar
-rw-rw-r--  1 rbhanot  staff  4158 Jul  5 21:00 foo.pb.go

This time a new protos directory was created under which we have the generated go files, why ? Because when we changed the --proto-path=src the directory structure of proto files changed to protos/foo.proto and protos/bar/baz.proto.

Now let's finally throw the --go_out as well in here and see what happens

❯ mkdir out
❯ protoc --proto_path=src --go_out=out --go_opt=paths=source_relative protos/foo.proto protos/bar/baz.proto
❯ ls -l        
total 8
-rw-rw-r--  1 rbhanot  staff  45 Jul  5 16:12 go.mod
drwxrwxr-x  3 rbhanot  staff  96 Jul  5 21:05 out
drwxr-xr-x  3 rbhanot  staff  96 Jul  5 18:12 src

~/dummy_go  
❯ ls -lR out
drwxrwxr-x  4 rbhanot  staff  128 Jul  5 21:05 protos

out/protos:
drwxrwxr-x  3 rbhanot  staff    96 Jul  5 21:05 bar
-rw-rw-r--  1 rbhanot  staff  4158 Jul  5 21:05 foo.pb.go

out/protos/bar:
-rw-rw-r--  1 rbhanot  staff  4298 Jul  5 21:05 baz.pb.go

This is exactly similar to last example except that we provided a custom directory to hold the generated code.

module=$PREFIX

> If the module=$PREFIX flag is specified, the output file is placed in
> a directory named after the Go package’s import path, but with the
> specified directory prefix removed from the output filename. For
> example, an input file protos/buzz.proto with a Go import path of
> example.com/project/protos/fizz and example.com/project specified as
> the module prefix results in an output file at protos/fizz/buzz.pb.go.
> Generating any Go packages outside the module path results in an
> error. This mode is useful for outputting generated files directly
> into a Go module.

Lets see this as well as in action, consider the following proto file

syntax = &quot;proto3&quot;;

package foo;

option go_package = &quot;github.com/rbhanot/dummy-app/greet&quot;;

message Foo {
    string name = 1;
}

Note carefully I have intentionally remove the src from the go_package to show the behaviour of this flag

❯ protoc --proto_path=src --go_out=. --go_opt=module=github.com/rbhanot/dummy-app protos/foo.proto protos/bar/baz.proto

❯ ls -l
-rw-rw-r--  1 rbhanot  staff   45 Jul  5 16:12 go.mod
drwxrwxr-x  3 rbhanot  staff   96 Jul  5 21:14 greet
drwxr-xr-x  4 rbhanot  staff  128 Jul  5 21:14 src

❯ ls -l greet
-rw-rw-r--  1 rbhanot  staff  4274 Jul  5 21:17 baz.pb.go
-rw-rw-r--  1 rbhanot  staff  4133 Jul  5 21:17 foo.pb.go

We see the greet directory with the pb files in it. So it basically removed the prefix(github.com/rbhanot/dummy-app) from the go_package and then actually created the go package in the current directory(--go_out=.).

Now lets change go_package = &quot;github.com/rbhanot/dummy-app/src/greet&quot;; and running the above command yields following

❯ protoc --proto_path=src --go_out=. --go_opt=module=github.com/rbhanot/dummy-app protos/foo.proto protos/bar/baz.proto

❯ ls -l src
drwxrwxr-x  4 rbhanot  staff  128 Jul  5 21:22 greet
drwxr-xr-x  4 rbhanot  staff  128 Jul  5 17:37 protos

This time we see the greet package generated inside our src directory.

Finally lets throw in the same --go_out=out here as well

❯ mkdir out

❯ protoc --proto_path=src --go_out=out --go_opt=module=github.com/rbhanot/dummy-app protos/foo.proto protos/bar/baz.proto

❯ ls -l out     
total 0
drwxrwxr-x  3 rbhanot  staff  96 Jul  5 21:24 src

❯ ls -lR out
total 0
drwxrwxr-x  3 rbhanot  staff  96 Jul  5 21:24 src

out/src:
total 0
drwxrwxr-x  4 rbhanot  staff  128 Jul  5 21:24 greet

out/src/greet:
total 32
-rw-rw-r--  1 rbhanot  staff  4298 Jul  5 21:24 baz.pb.go
-rw-rw-r--  1 rbhanot  staff  4158 Jul  5 21:24 foo.pb.go

This time the generated code was not put in src but inside the out and note the directory structure as well src/greet/foo.pb.go.

I hope this makes things more clear (it atleast did for me) wrt to how these flags behave.
1: https://protobuf.dev/reference/go/go-generated/#invocation

huangapple
  • 本文由 发表于 2022年1月16日 22:35:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/70731053.html
匿名

发表评论

匿名网友

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

确定