cmd/protoc-gen-go: register FileDescriptorProto

Generate the gzipped FileDescriptorProto var, and register it with
proto.RegisterFile at init time.

Change-Id: Ie232f20412ca9cd7bde91aaba7127dc181e9758c
Reviewed-on: https://go-review.googlesource.com/134118
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/cmd/protoc-gen-go/main.go b/cmd/protoc-gen-go/main.go
index 5792ebe..b9223b0 100644
--- a/cmd/protoc-gen-go/main.go
+++ b/cmd/protoc-gen-go/main.go
@@ -7,6 +7,15 @@
 package main
 
 import (
+	"bytes"
+	"compress/gzip"
+	"crypto/sha256"
+	"encoding/hex"
+	"fmt"
+	"strconv"
+
+	"github.com/golang/protobuf/proto"
+	descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
 	"google.golang.org/proto/protogen"
 )
 
@@ -34,7 +43,51 @@
 		genMessage(gen, g, m)
 	}
 
-	// TODO: Everything.
+	genFileDescriptor(gen, g, f)
+}
+
+func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *protogen.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, m *protogen.Message) {
diff --git a/cmd/protoc-gen-go/testdata/proto2/nested_messages.pb.go b/cmd/protoc-gen-go/testdata/proto2/nested_messages.pb.go
index 0ae32d1..ba78d1b 100644
--- a/cmd/protoc-gen-go/testdata/proto2/nested_messages.pb.go
+++ b/cmd/protoc-gen-go/testdata/proto2/nested_messages.pb.go
@@ -11,3 +11,21 @@
 
 type Layer1_Layer2_Layer3 struct {
 }
+
+func init() { proto.RegisterFile("proto2/nested_messages.proto", fileDescriptor_7417ee157699d191) }
+
+var fileDescriptor_7417ee157699d191 = []byte{
+	// 184 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x29, 0x28, 0xca, 0x2f,
+	0xc9, 0x37, 0xd2, 0xcf, 0x4b, 0x2d, 0x2e, 0x49, 0x4d, 0x89, 0xcf, 0x4d, 0x2d, 0x2e, 0x4e, 0x4c,
+	0x4f, 0x2d, 0xd6, 0x03, 0x0b, 0x0b, 0x89, 0xa6, 0xe7, 0x83, 0x19, 0x10, 0x6e, 0x32, 0x84, 0x32,
+	0x52, 0x3a, 0xc3, 0xc8, 0xc5, 0xe6, 0x93, 0x58, 0x99, 0x5a, 0x64, 0x28, 0x64, 0xc2, 0xc5, 0x94,
+	0x63, 0x24, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x6d, 0xa4, 0xa2, 0x87, 0x55, 0xb9, 0x1e, 0x44, 0x29,
+	0x84, 0x32, 0x0a, 0x62, 0xca, 0x31, 0x12, 0xb2, 0xe6, 0x62, 0xca, 0x31, 0x96, 0x60, 0x02, 0xeb,
+	0xd2, 0x26, 0x46, 0x17, 0x84, 0x32, 0x0e, 0x62, 0xca, 0x31, 0x96, 0xf2, 0x87, 0x5a, 0x0e, 0x33,
+	0x86, 0x91, 0x3c, 0x63, 0x38, 0xa0, 0xc6, 0x18, 0x3b, 0x59, 0x47, 0x59, 0xa6, 0xe7, 0xe7, 0xa7,
+	0xe7, 0xa4, 0xea, 0xa5, 0xe7, 0xe7, 0x24, 0xe6, 0xa5, 0xeb, 0xe5, 0x17, 0xa5, 0xeb, 0x83, 0xb5,
+	0xeb, 0x27, 0xe7, 0xa6, 0x40, 0x58, 0xc9, 0xba, 0xe9, 0xa9, 0x79, 0xba, 0xe9, 0xf9, 0xfa, 0x25,
+	0xa9, 0xc5, 0x25, 0x29, 0x89, 0x25, 0x89, 0x10, 0x61, 0x23, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff,
+	0x47, 0x0d, 0xa3, 0x19, 0x41, 0x01, 0x00, 0x00,
+}
diff --git a/cmd/protoc-gen-go/testdata/proto2/proto2.pb.go b/cmd/protoc-gen-go/testdata/proto2/proto2.pb.go
index 90ffa17..72dcb71 100644
--- a/cmd/protoc-gen-go/testdata/proto2/proto2.pb.go
+++ b/cmd/protoc-gen-go/testdata/proto2/proto2.pb.go
@@ -5,3 +5,16 @@
 
 type Message struct {
 }
+
+func init() { proto.RegisterFile("proto2/proto2.proto", fileDescriptor_d756bbe8817c03c1) }
+
+var fileDescriptor_d756bbe8817c03c1 = []byte{
+	// 107 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2e, 0x28, 0xca, 0x2f,
+	0xc9, 0x37, 0xd2, 0x87, 0x50, 0x7a, 0x60, 0x4a, 0x48, 0x34, 0x3d, 0x1f, 0xcc, 0x80, 0x70, 0x93,
+	0x21, 0x94, 0x91, 0x12, 0x27, 0x17, 0xbb, 0x6f, 0x6a, 0x71, 0x71, 0x62, 0x7a, 0xaa, 0x93, 0x75,
+	0x94, 0x65, 0x7a, 0x7e, 0x7e, 0x7a, 0x4e, 0xaa, 0x5e, 0x7a, 0x7e, 0x4e, 0x62, 0x5e, 0xba, 0x5e,
+	0x7e, 0x51, 0x3a, 0xc4, 0x0c, 0xfd, 0xe4, 0xdc, 0x14, 0x08, 0x2b, 0x59, 0x37, 0x3d, 0x35, 0x4f,
+	0x37, 0x3d, 0x5f, 0xbf, 0x24, 0xb5, 0xb8, 0x24, 0x25, 0xb1, 0x24, 0x11, 0x6a, 0x09, 0x20, 0x00,
+	0x00, 0xff, 0xff, 0xd7, 0x76, 0x0d, 0x22, 0x74, 0x00, 0x00, 0x00,
+}
diff --git a/protogen/protogen.go b/protogen/protogen.go
index ffc1d7a..be5e503 100644
--- a/protogen/protogen.go
+++ b/protogen/protogen.go
@@ -299,7 +299,8 @@
 
 // A File describes a .proto source file.
 type File struct {
-	Desc protoreflect.FileDescriptor
+	Desc  protoreflect.FileDescriptor
+	Proto *descpb.FileDescriptorProto
 
 	GoPackageName GoPackageName // name of this file's Go package
 	GoImportPath  GoImportPath  // import path of this file's Go package
@@ -324,6 +325,7 @@
 	}
 	f := &File{
 		Desc:          desc,
+		Proto:         p,
 		GoPackageName: packageName,
 		GoImportPath:  importPath,
 	}