protogen.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. // Copyright 2018 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Package protogen provides support for writing protoc plugins.
  5. //
  6. // Plugins for protoc, the Protocol Buffers Compiler, are programs which read
  7. // a CodeGeneratorRequest protocol buffer from standard input and write a
  8. // CodeGeneratorResponse protocol buffer to standard output. This package
  9. // provides support for writing plugins which generate Go code.
  10. package protogen
  11. import (
  12. "bufio"
  13. "bytes"
  14. "fmt"
  15. "go/parser"
  16. "go/printer"
  17. "go/token"
  18. "io/ioutil"
  19. "os"
  20. "path/filepath"
  21. "strings"
  22. "github.com/golang/protobuf/proto"
  23. descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
  24. pluginpb "github.com/golang/protobuf/protoc-gen-go/plugin"
  25. )
  26. // Run executes a function as a protoc plugin.
  27. //
  28. // It reads a CodeGeneratorRequest message from os.Stdin, invokes the plugin
  29. // function, and writes a CodeGeneratorResponse message to os.Stdout.
  30. //
  31. // If a failure occurs while reading or writing, Run prints an error to
  32. // os.Stderr and calls os.Exit(1).
  33. func Run(f func(*Plugin) error) {
  34. if err := run(f); err != nil {
  35. fmt.Fprintf(os.Stderr, "%s: %v\n", filepath.Base(os.Args[0]), err)
  36. os.Exit(1)
  37. }
  38. }
  39. func run(f func(*Plugin) error) error {
  40. in, err := ioutil.ReadAll(os.Stdin)
  41. if err != nil {
  42. return err
  43. }
  44. req := &pluginpb.CodeGeneratorRequest{}
  45. if err := proto.Unmarshal(in, req); err != nil {
  46. return err
  47. }
  48. gen, err := New(req)
  49. if err != nil {
  50. return err
  51. }
  52. if err := f(gen); err != nil {
  53. // Errors from the plugin function are reported by setting the
  54. // error field in the CodeGeneratorResponse.
  55. //
  56. // In contrast, errors that indicate a problem in protoc
  57. // itself (unparsable input, I/O errors, etc.) are reported
  58. // to stderr.
  59. gen.Error(err)
  60. }
  61. resp := gen.Response()
  62. out, err := proto.Marshal(resp)
  63. if err != nil {
  64. return err
  65. }
  66. if _, err := os.Stdout.Write(out); err != nil {
  67. return err
  68. }
  69. return nil
  70. }
  71. // A Plugin is a protoc plugin invocation.
  72. type Plugin struct {
  73. // Request is the CodeGeneratorRequest provided by protoc.
  74. Request *pluginpb.CodeGeneratorRequest
  75. // Files is the set of files to generate and everything they import.
  76. // Files appear in topological order, so each file appears before any
  77. // file that imports it.
  78. Files []*File
  79. filesByName map[string]*File
  80. packageImportPath string // Go import path of the package we're generating code for.
  81. genFiles []*GeneratedFile
  82. err error
  83. }
  84. // New returns a new Plugin.
  85. func New(req *pluginpb.CodeGeneratorRequest) (*Plugin, error) {
  86. gen := &Plugin{
  87. Request: req,
  88. filesByName: make(map[string]*File),
  89. }
  90. // TODO: Figure out how to pass parameters to the generator.
  91. for _, param := range strings.Split(req.GetParameter(), ",") {
  92. var value string
  93. if i := strings.Index(param, "="); i >= 0 {
  94. value = param[i+1:]
  95. param = param[0:i]
  96. }
  97. switch param {
  98. case "":
  99. // Ignore.
  100. case "import_prefix":
  101. // TODO
  102. case "import_path":
  103. gen.packageImportPath = value
  104. case "paths":
  105. // TODO
  106. case "plugins":
  107. // TODO
  108. case "annotate_code":
  109. // TODO
  110. default:
  111. if param[0] != 'M' {
  112. return nil, fmt.Errorf("unknown parameter %q", param)
  113. }
  114. // TODO
  115. }
  116. }
  117. for _, fdesc := range gen.Request.ProtoFile {
  118. f := newFile(gen, fdesc)
  119. name := f.Desc.GetName()
  120. if gen.filesByName[name] != nil {
  121. return nil, fmt.Errorf("duplicate file name: %q", name)
  122. }
  123. gen.Files = append(gen.Files, f)
  124. gen.filesByName[name] = f
  125. }
  126. for _, name := range gen.Request.FileToGenerate {
  127. f, ok := gen.FileByName(name)
  128. if !ok {
  129. return nil, fmt.Errorf("no descriptor for generated file: %v", name)
  130. }
  131. f.Generate = true
  132. }
  133. return gen, nil
  134. }
  135. // Error records an error in code generation. The generator will report the
  136. // error back to protoc and will not produce output.
  137. func (gen *Plugin) Error(err error) {
  138. if gen.err == nil {
  139. gen.err = err
  140. }
  141. }
  142. // Response returns the generator output.
  143. func (gen *Plugin) Response() *pluginpb.CodeGeneratorResponse {
  144. resp := &pluginpb.CodeGeneratorResponse{}
  145. if gen.err != nil {
  146. resp.Error = proto.String(gen.err.Error())
  147. return resp
  148. }
  149. for _, gf := range gen.genFiles {
  150. content, err := gf.Content()
  151. if err != nil {
  152. return &pluginpb.CodeGeneratorResponse{
  153. Error: proto.String(err.Error()),
  154. }
  155. }
  156. resp.File = append(resp.File, &pluginpb.CodeGeneratorResponse_File{
  157. Name: proto.String(gf.path),
  158. Content: proto.String(string(content)),
  159. })
  160. }
  161. return resp
  162. }
  163. // FileByName returns the file with the given name.
  164. func (gen *Plugin) FileByName(name string) (f *File, ok bool) {
  165. f, ok = gen.filesByName[name]
  166. return f, ok
  167. }
  168. // A File describes a .proto source file.
  169. type File struct {
  170. Desc *descpb.FileDescriptorProto // TODO: protoreflect.FileDescriptor
  171. Messages []*Message // top-level message declartions
  172. Generate bool // true if we should generate code for this file
  173. }
  174. func newFile(gen *Plugin, p *descpb.FileDescriptorProto) *File {
  175. f := &File{
  176. Desc: p,
  177. }
  178. for _, d := range p.MessageType {
  179. f.Messages = append(f.Messages, newMessage(gen, nil, d))
  180. }
  181. return f
  182. }
  183. // A Message describes a message.
  184. type Message struct {
  185. Desc *descpb.DescriptorProto // TODO: protoreflect.MessageDescriptor
  186. GoIdent GoIdent // name of the generated Go type
  187. Messages []*Message // nested message declarations
  188. }
  189. func newMessage(gen *Plugin, parent *Message, p *descpb.DescriptorProto) *Message {
  190. m := &Message{
  191. Desc: p,
  192. GoIdent: camelCase(p.GetName()),
  193. }
  194. if parent != nil {
  195. m.GoIdent = parent.GoIdent + "_" + m.GoIdent
  196. }
  197. for _, nested := range p.GetNestedType() {
  198. m.Messages = append(m.Messages, newMessage(gen, m, nested))
  199. }
  200. return m
  201. }
  202. // A GeneratedFile is a generated file.
  203. type GeneratedFile struct {
  204. path string
  205. buf bytes.Buffer
  206. }
  207. // NewGeneratedFile creates a new generated file with the given path.
  208. func (gen *Plugin) NewGeneratedFile(path string) *GeneratedFile {
  209. g := &GeneratedFile{
  210. path: path,
  211. }
  212. gen.genFiles = append(gen.genFiles, g)
  213. return g
  214. }
  215. // P prints a line to the generated output. It converts each parameter to a
  216. // string following the same rules as fmt.Print. It never inserts spaces
  217. // between parameters.
  218. //
  219. // TODO: .meta file annotations.
  220. func (g *GeneratedFile) P(v ...interface{}) {
  221. for _, x := range v {
  222. fmt.Fprint(&g.buf, x)
  223. }
  224. fmt.Fprintln(&g.buf)
  225. }
  226. // Write implements io.Writer.
  227. func (g *GeneratedFile) Write(p []byte) (n int, err error) {
  228. return g.buf.Write(p)
  229. }
  230. // Content returns the contents of the generated file.
  231. func (g *GeneratedFile) Content() ([]byte, error) {
  232. if !strings.HasSuffix(g.path, ".go") {
  233. return g.buf.Bytes(), nil
  234. }
  235. // Reformat generated code.
  236. original := g.buf.Bytes()
  237. fset := token.NewFileSet()
  238. ast, err := parser.ParseFile(fset, "", original, parser.ParseComments)
  239. if err != nil {
  240. // Print out the bad code with line numbers.
  241. // This should never happen in practice, but it can while changing generated code
  242. // so consider this a debugging aid.
  243. var src bytes.Buffer
  244. s := bufio.NewScanner(bytes.NewReader(original))
  245. for line := 1; s.Scan(); line++ {
  246. fmt.Fprintf(&src, "%5d\t%s\n", line, s.Bytes())
  247. }
  248. return nil, fmt.Errorf("%v: unparsable Go source: %v\n%v", g.path, err, src.String())
  249. }
  250. var out bytes.Buffer
  251. if err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(&out, fset, ast); err != nil {
  252. return nil, fmt.Errorf("%v: can not reformat Go source: %v", g.path, err)
  253. }
  254. // TODO: Patch annotation locations.
  255. return out.Bytes(), nil
  256. }