protoc example.proto --plugin=protoc-gen-custom-plugin=./plugin.py --custom-plugin_out=.
注意上面标红的可以是任意名字。注意如果是你自己定义的plugin都是以protoc-gen-打头,注意这里的custom-plugin既是protoc-gen-的最后,也是_out的最前,因此这个名字非常重要。同时需要注意的是这里的./plugin.py我们是直接显示定义了名字,但是如果你把你的plugin设置成了protoc-gen-custom-plugin的这个名字,同时在PATH中暴露,你就不需要去指定这个名字,他就会按照这个隐式规则自己去查找了
之所以能够做到这一点,是因为Protobuf的生态决定,他们严格让protoc跟语言无关,所有跟具体语言生成相关的都通过plugins的方式来进行完成,如下描述:
运行示意图如下:
可以看到plugin会从stdin来读取CodeGeneratorRequest, 当处理完成之后,会写一个对应的CodeGeneratorResponse给stdout. 同时这个plugin要能够被调用需要存放在对应的PATH里,同时在名字命名上需要按照约束比如protoc-gen-$NAME来进行命名,他的调用时间点是通过-$NAME_out来进行触发,因此有下面几个重要的点需要重新强调:
- 无论如何,调用都是通过$NAME_out来进行触发,即只有你的命令行参数里有$NAME_out,这个插件才会被真正触发,之前的--plugin=protoc-gen-custom-plugin=./plugin.py顶多只是在指定你插件的名字,而且这一步甚至可以省略只要你把你的插件放在PATH里,同时带上可执行权限,并且名字方式就是按照protoc-gen-XXX进行命名
- 你需要按照protoc-gen-$NAME的方式来定义你的插件同时把他放到PATH里并且带有执行权限,如果你不按照protoc-gen-$NAME的方式来定义你的插件,你需要多一步命令行参数 - protoc-gen-$NAME=./xxx,此时的xxx需要有执行权限
- 在调用protoc的时候如果出现对应的proto文件找不到,可以使用-I选项来进行传递路径,类似于gcc的-I
- protoc查看自己的命令行参数,如果出现了<plugin>_out的字段,就被认定为在调用对应的plugin,你可以通过--plugin=protoc-gen-custom-plugin=./xxx进行事先指定,也可以按照约束把protoc-gen-plugin放到PATH里让protoc自己去调用
- protoc序列化对应的message descriptions并且放入CodeGenerationRequest消息结构体同时传入给stdin
- 插件从stdin拿到这个CodeGeneratorRequest Message之后进行解析,生成对应的source code同时写回给CodeGenerationResponse Message以二进制的方式再回传进stdout
- 除了传统的$NAME_out=xxx之外,还能来传递对应的opt额外给到对应的plugin,比如我们可以看到这样的调用--go-ascii_opt=paths=source_relative,这里的xxx_opt又跟之前的介绍的xxx_out一样,代表你这个插件的opt选项,同时可以看到他传入的是类似键值对的概念,比如这里的paths=source_relative, 同时如果你一次性想传递多个opt给plugin,你可以通过,进行分割,比如xxx_opt=a=b,c=d
- 因此对应语言的generate,你可以看到有比如--cpp_out, --go_out, 实际上他都会在背后调用对应的插件比如protoc-gen-cpp, protoc-gen-go, 再次强调,插件的调用是通过xxx_out来进行的