protoc-gen-gin.rar

难点:
插件是属于被protoc调用的工具, 所以无法单独启动,很难调试
protoc插件运行需要一些输入,这些输入是由protoc传递过来放入os.Stdin中
插件的输入是到stdout而不是由我们自己确定的,所以很难输出文件查看生成效果

注意:
下面属于魔改源码使得为了方便调试,所以本着尽量少改源码的方式去修改

修改源码 protobuf/compiler/protogen/protogen.go

  1. 新增函数

    func fileDescriptorProto(files …string) (*descriptorpb.FileDescriptorProto, error) {

    1. var p protoparse.Parser
    2. fd, err := p.ParseFiles(files...)
    3. if err != nil {
    4. return nil, err
    5. }
    6. return fd[0].AsFileDescriptorProto(), nil

    }

b. 修改func run(opts Options, f func(*Plugin) error) 函数

  1. import "github.com/jhump/protoreflect/desc/protoparse"
  2. func runV2(opts Options, f func(*Plugin) error) error {
  3. if len(os.Args) > 1 {
  4. return fmt.Errorf("unknown argument %q (this program should be run by protoc, not directly)", os.Args[1])
  5. }
  6. fileName := "E:/xxx/api.proto"
  7. fdpb, err := fileDescriptorProto(fileName)
  8. if err != nil {
  9. panic(err)
  10. }
  11. req1 := []*descriptorpb.FileDescriptorProto{fdpb}
  12. req := &pluginpb.CodeGeneratorRequest{
  13. ProtoFile: req1,
  14. FileToGenerate: []string{fileName},
  15. }
  16. gen, err := opts.New(req)
  17. if err != nil {
  18. return err
  19. }
  1. New函数中增加一行

    func (opts Options) New(req pluginpb.CodeGeneratorRequest) (Plugin, error) {

    1. //新增
    2. gen.fileReg = protoregistry.GlobalFiles

    }

导入相关proto文件

  1. 需要将third_party中依赖的google相关的proto文件拷贝到工具目录下
  2. 在goland的运行配置中设置当前运行目录为 E:/myprojects/gostart/tools 要和main文件在同一个目录下

自定义注册proto源码

  1. package main
  2. import (
  3. "flag"
  4. "google.golang.org/protobuf/reflect/protodesc"
  5. "google.golang.org/protobuf/reflect/protoregistry"
  6. "google.golang.org/protobuf/types/descriptorpb"
  7. "io/ioutil"
  8. "os"
  9. "os/exec"
  10. "path"
  11. "github.com/golang/protobuf/proto"
  12. "google.golang.org/protobuf/compiler/protogen"
  13. "google.golang.org/protobuf/types/pluginpb"
  14. )
  15. func registerProtoFile(srcDir string, filename string) error {
  16. // First, convert the .proto file to a file descriptor set.
  17. tmpFile := path.Join(srcDir, filename+".pb")
  18. cmd := exec.Command("protoc",
  19. "--include_source_info",
  20. "--descriptor_set_out="+tmpFile,
  21. "--proto_path="+srcDir,
  22. path.Join(srcDir, filename))
  23. cmd.Stdout = os.Stdout
  24. cmd.Stderr = os.Stderr
  25. err := cmd.Run()
  26. if err != nil {
  27. panic(err)
  28. }
  29. defer os.Remove(tmpFile)
  30. // Now load that temporary file as a file descriptor set protobuf.
  31. protoFile, err := ioutil.ReadFile(tmpFile)
  32. if err != nil {
  33. panic(err)
  34. }
  35. pbSet := new(descriptorpb.FileDescriptorSet)
  36. if err := proto.Unmarshal(protoFile, pbSet); err != nil {
  37. panic(err)
  38. }
  39. // We know protoc was invoked with a single .proto file.
  40. pb := pbSet.GetFile()[0]
  41. // Initialize the file descriptor object.
  42. fd, err := protodesc.NewFile(pb, protoregistry.GlobalFiles)
  43. if err != nil {
  44. panic(err)
  45. }
  46. // and finally register it.
  47. return protoregistry.GlobalFiles.RegisterFile(fd)
  48. }
  49. func registerProto() {
  50. files := []string{
  51. "google/api/http.proto",
  52. "google/api/annotations.proto",
  53. "google/api/client.proto",
  54. "google/api/field_behavior.proto",
  55. "google/api/resource.proto",
  56. }
  57. for _, f := range files {
  58. err := registerProtoFile("./", f)
  59. if err != nil {
  60. panic(err)
  61. }
  62. }
  63. }
  64. func main() {
  65. registerProto()
  66. flag.Parse()
  67. var flags flag.FlagSet
  68. protogen.Options{
  69. ParamFunc: flags.Set,
  70. }.Run(func(gen *protogen.Plugin) error {
  71. gen.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)
  72. for _, f := range gen.Files {
  73. if !f.Generate {
  74. continue
  75. }
  76. //generateFile(gen, f)
  77. }
  78. return nil
  79. })
  80. }