mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-01-01 11:58:21 +00:00
cab8dfee90
Add initial handling of location paths (arrays of integers identifying an entity in a .proto source file). Expose path info in protogen; each descriptor has a Path field containing its location path. Format comments in protoc-gen-go. This contains one change from github.com/golang/protobuf: Package comments are now included before the package statement (but not attached to it) and use // comment syntax instead of /* */. e.g., Before: package test /* This package contains interesting messages. */ After: // This package contains interesting messages. package test Change-Id: Ieee13ae77b3584f7562183100554d3df732348aa Reviewed-on: https://go-review.googlesource.com/133915 Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
145 lines
3.4 KiB
Go
145 lines
3.4 KiB
Go
// Copyright 2018 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// The protoc-gen-go binary is a protoc plugin to generate a Go protocol
|
|
// buffer package.
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
|
|
"google.golang.org/proto/protogen"
|
|
)
|
|
|
|
func main() {
|
|
protogen.Run(func(gen *protogen.Plugin) error {
|
|
for _, f := range gen.Files {
|
|
if !f.Generate {
|
|
continue
|
|
}
|
|
genFile(gen, f)
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
type File struct {
|
|
*protogen.File
|
|
locationMap map[string][]*descpb.SourceCodeInfo_Location
|
|
}
|
|
|
|
func genFile(gen *protogen.Plugin, file *protogen.File) {
|
|
f := &File{
|
|
File: file,
|
|
locationMap: make(map[string][]*descpb.SourceCodeInfo_Location),
|
|
}
|
|
for _, loc := range file.Proto.GetSourceCodeInfo().GetLocation() {
|
|
key := pathKey(loc.Path)
|
|
f.locationMap[key] = append(f.locationMap[key], loc)
|
|
}
|
|
|
|
g := gen.NewGeneratedFile(f.GeneratedFilenamePrefix+".pb.go", f.GoImportPath)
|
|
g.P("// Code generated by protoc-gen-go. DO NOT EDIT.")
|
|
g.P("// source: ", f.Desc.Path())
|
|
g.P()
|
|
const filePackageField = 2 // FileDescriptorProto.package
|
|
genComment(g, f, []int32{filePackageField})
|
|
g.P()
|
|
g.P("package ", f.GoPackageName)
|
|
g.P()
|
|
|
|
for _, message := range f.Messages {
|
|
genMessage(gen, g, f, message)
|
|
}
|
|
|
|
genFileDescriptor(gen, g, f)
|
|
}
|
|
|
|
func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File) {
|
|
// Determine the name of the var holding the file descriptor:
|
|
//
|
|
// fileDescriptor_<hash of filename>
|
|
filenameHash := sha256.Sum256([]byte(f.Desc.Path()))
|
|
varName := fmt.Sprintf("fileDescriptor_%s", hex.EncodeToString(filenameHash[:8]))
|
|
|
|
// Trim the source_code_info from the descriptor.
|
|
// Marshal and gzip it.
|
|
descProto := proto.Clone(f.Proto).(*descpb.FileDescriptorProto)
|
|
descProto.SourceCodeInfo = nil
|
|
b, err := proto.Marshal(descProto)
|
|
if err != nil {
|
|
gen.Error(err)
|
|
return
|
|
}
|
|
var buf bytes.Buffer
|
|
w, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression)
|
|
w.Write(b)
|
|
w.Close()
|
|
b = buf.Bytes()
|
|
|
|
g.P("func init() { proto.RegisterFile(", strconv.Quote(f.Desc.Path()), ", ", varName, ") }")
|
|
g.P()
|
|
g.P("var ", varName, " = []byte{")
|
|
g.P("// ", len(b), " bytes of a gzipped FileDescriptorProto")
|
|
for len(b) > 0 {
|
|
n := 16
|
|
if n > len(b) {
|
|
n = len(b)
|
|
}
|
|
|
|
s := ""
|
|
for _, c := range b[:n] {
|
|
s += fmt.Sprintf("0x%02x,", c)
|
|
}
|
|
g.P(s)
|
|
|
|
b = b[n:]
|
|
}
|
|
g.P("}")
|
|
g.P()
|
|
}
|
|
|
|
func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File, message *protogen.Message) {
|
|
genComment(g, f, message.Path)
|
|
g.P("type ", message.GoIdent, " struct {")
|
|
g.P("}")
|
|
g.P()
|
|
|
|
for _, nested := range message.Messages {
|
|
genMessage(gen, g, f, nested)
|
|
}
|
|
}
|
|
|
|
func genComment(g *protogen.GeneratedFile, f *File, path []int32) {
|
|
for _, loc := range f.locationMap[pathKey(path)] {
|
|
if loc.LeadingComments == nil {
|
|
continue
|
|
}
|
|
for _, line := range strings.Split(strings.TrimSuffix(loc.GetLeadingComments(), "\n"), "\n") {
|
|
g.P("//", line)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
// pathKey converts a location path to a string suitable for use as a map key.
|
|
func pathKey(path []int32) string {
|
|
var buf []byte
|
|
for i, x := range path {
|
|
if i != 0 {
|
|
buf = append(buf, ',')
|
|
}
|
|
buf = strconv.AppendInt(buf, int64(x), 10)
|
|
}
|
|
return string(buf)
|
|
}
|