blob: d3309416dca4c406c67532e59e143e3e571ee861 [file] [log] [blame]
Damien Neil220c2022018-08-15 11:24:18 -07001// 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
5// The protoc-gen-go binary is a protoc plugin to generate a Go protocol
6// buffer package.
7package main
8
9import (
Damien Neil7779e052018-09-07 14:14:06 -070010 "bytes"
11 "compress/gzip"
12 "crypto/sha256"
13 "encoding/hex"
14 "fmt"
15 "strconv"
Damien Neilcab8dfe2018-09-06 14:51:28 -070016 "strings"
Damien Neil7779e052018-09-07 14:14:06 -070017
18 "github.com/golang/protobuf/proto"
19 descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
Damien Neil220c2022018-08-15 11:24:18 -070020 "google.golang.org/proto/protogen"
21)
22
23func main() {
24 protogen.Run(func(gen *protogen.Plugin) error {
25 for _, f := range gen.Files {
26 if !f.Generate {
27 continue
28 }
29 genFile(gen, f)
30 }
31 return nil
32 })
33}
34
Damien Neilcab8dfe2018-09-06 14:51:28 -070035type File struct {
36 *protogen.File
37 locationMap map[string][]*descpb.SourceCodeInfo_Location
38}
39
40func genFile(gen *protogen.Plugin, file *protogen.File) {
41 f := &File{
42 File: file,
43 locationMap: make(map[string][]*descpb.SourceCodeInfo_Location),
44 }
45 for _, loc := range file.Proto.GetSourceCodeInfo().GetLocation() {
46 key := pathKey(loc.Path)
47 f.locationMap[key] = append(f.locationMap[key], loc)
48 }
49
Damien Neil082ce922018-09-06 10:23:53 -070050 g := gen.NewGeneratedFile(f.GeneratedFilenamePrefix+".pb.go", f.GoImportPath)
Damien Neil220c2022018-08-15 11:24:18 -070051 g.P("// Code generated by protoc-gen-go. DO NOT EDIT.")
Damien Neilabc6fc12018-08-23 14:39:30 -070052 g.P("// source: ", f.Desc.Path())
Damien Neil220c2022018-08-15 11:24:18 -070053 g.P()
Damien Neilcab8dfe2018-09-06 14:51:28 -070054 const filePackageField = 2 // FileDescriptorProto.package
55 genComment(g, f, []int32{filePackageField})
56 g.P()
Damien Neil082ce922018-09-06 10:23:53 -070057 g.P("package ", f.GoPackageName)
Damien Neilc7d07d92018-08-22 13:46:02 -070058 g.P()
59
Damien Neilcab8dfe2018-09-06 14:51:28 -070060 for _, message := range f.Messages {
61 genMessage(gen, g, f, message)
Damien Neilc7d07d92018-08-22 13:46:02 -070062 }
Damien Neil220c2022018-08-15 11:24:18 -070063
Damien Neil7779e052018-09-07 14:14:06 -070064 genFileDescriptor(gen, g, f)
65}
66
Damien Neilcab8dfe2018-09-06 14:51:28 -070067func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File) {
Damien Neil7779e052018-09-07 14:14:06 -070068 // Determine the name of the var holding the file descriptor:
69 //
70 // fileDescriptor_<hash of filename>
71 filenameHash := sha256.Sum256([]byte(f.Desc.Path()))
72 varName := fmt.Sprintf("fileDescriptor_%s", hex.EncodeToString(filenameHash[:8]))
73
74 // Trim the source_code_info from the descriptor.
75 // Marshal and gzip it.
76 descProto := proto.Clone(f.Proto).(*descpb.FileDescriptorProto)
77 descProto.SourceCodeInfo = nil
78 b, err := proto.Marshal(descProto)
79 if err != nil {
80 gen.Error(err)
81 return
82 }
83 var buf bytes.Buffer
84 w, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression)
85 w.Write(b)
86 w.Close()
87 b = buf.Bytes()
88
89 g.P("func init() { proto.RegisterFile(", strconv.Quote(f.Desc.Path()), ", ", varName, ") }")
90 g.P()
91 g.P("var ", varName, " = []byte{")
92 g.P("// ", len(b), " bytes of a gzipped FileDescriptorProto")
93 for len(b) > 0 {
94 n := 16
95 if n > len(b) {
96 n = len(b)
97 }
98
99 s := ""
100 for _, c := range b[:n] {
101 s += fmt.Sprintf("0x%02x,", c)
102 }
103 g.P(s)
104
105 b = b[n:]
106 }
107 g.P("}")
108 g.P()
Damien Neil220c2022018-08-15 11:24:18 -0700109}
Damien Neilc7d07d92018-08-22 13:46:02 -0700110
Damien Neilcab8dfe2018-09-06 14:51:28 -0700111func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File, message *protogen.Message) {
112 genComment(g, f, message.Path)
113 g.P("type ", message.GoIdent, " struct {")
Damien Neilc7d07d92018-08-22 13:46:02 -0700114 g.P("}")
115 g.P()
116
Damien Neilcab8dfe2018-09-06 14:51:28 -0700117 for _, nested := range message.Messages {
118 genMessage(gen, g, f, nested)
Damien Neilc7d07d92018-08-22 13:46:02 -0700119 }
120}
Damien Neilcab8dfe2018-09-06 14:51:28 -0700121
122func genComment(g *protogen.GeneratedFile, f *File, path []int32) {
123 for _, loc := range f.locationMap[pathKey(path)] {
124 if loc.LeadingComments == nil {
125 continue
126 }
127 for _, line := range strings.Split(strings.TrimSuffix(loc.GetLeadingComments(), "\n"), "\n") {
128 g.P("//", line)
129 }
130 return
131 }
132}
133
134// pathKey converts a location path to a string suitable for use as a map key.
135func pathKey(path []int32) string {
136 var buf []byte
137 for i, x := range path {
138 if i != 0 {
139 buf = append(buf, ',')
140 }
141 buf = strconv.AppendInt(buf, int64(x), 10)
142 }
143 return string(buf)
144}