goprotobuf: make the generator a package, with the main
program a separate file. also introduce the hooks for a plugin
(really a plugin for this plugin) to enable add-on generators for
things like RPC.
R=rsc
CC=golang-dev
http://codereview.appspot.com/970046
diff --git a/compiler/Makefile b/compiler/Makefile
index ad85973..8da2086 100644
--- a/compiler/Makefile
+++ b/compiler/Makefile
@@ -36,7 +36,7 @@
GOFILES=\
main.go\
-DEPS=descriptor plugin
+DEPS=generator descriptor plugin
include $(GOROOT)/src/Make.cmd
diff --git a/compiler/descriptor/Makefile b/compiler/descriptor/Makefile
index cd2add9..5c7d716 100644
--- a/compiler/descriptor/Makefile
+++ b/compiler/descriptor/Makefile
@@ -35,6 +35,8 @@
GOFILES=\
descriptor.pb.go\
+DEPS=../../proto
+
include $(GOROOT)/src/Make.pkg
# Not stored here, but descriptor.proto is in http://code.google.com/p/protobuf
diff --git a/compiler/generator/Makefile b/compiler/generator/Makefile
new file mode 100644
index 0000000..2872c83
--- /dev/null
+++ b/compiler/generator/Makefile
@@ -0,0 +1,40 @@
+# Go support for Protocol Buffers - Google's data interchange format
+#
+# Copyright 2010 Google Inc. All rights reserved.
+# http://code.google.com/p/goprotobuf/
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+include $(GOROOT)/src/Make.$(GOARCH)
+
+TARG=goprotobuf.googlecode.com/hg/compiler/generator
+GOFILES=\
+ generator.go\
+
+DEPS=../descriptor ../plugin ../../proto
+
+include $(GOROOT)/src/Make.pkg
diff --git a/compiler/generator/generator.go b/compiler/generator/generator.go
new file mode 100644
index 0000000..5cef6f5
--- /dev/null
+++ b/compiler/generator/generator.go
@@ -0,0 +1,1030 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 Google Inc. All rights reserved.
+// http://code.google.com/p/goprotobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/*
+ The code generator for the plugin for the Google protocol buffer compiler.
+ It generates Go code from the protocol buffer description files read by the
+ main routine.
+
+ Not supported yet:
+ services
+ options
+*/
+package generator
+
+import (
+ "bytes"
+ "fmt"
+ "log"
+ "os"
+ "strings"
+ "unicode"
+
+ "goprotobuf.googlecode.com/hg/proto"
+ plugin "goprotobuf.googlecode.com/hg/compiler/plugin"
+ descriptor "goprotobuf.googlecode.com/hg/compiler/descriptor"
+)
+
+// A Plugin provides functionality to add to the output during Go code generation,
+// such as to produce RPC stubs.
+type Plugin interface {
+ // Name identifies the plugin.
+ Name() string
+ // Generate produces the code generated by the plugin for this file, except for the imports,
+ // by calling the generator's methods P, In, and Out.
+ Generate(g *Generator, file *FileDescriptor)
+ // GenerateImports produces the import declarations for this file.
+ GenerateImports(g *Generator, file *FileDescriptor)
+}
+
+var plugins []Plugin
+
+// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated.
+// It is typically called during initialization.
+func RegisterPlugin(p Plugin) {
+ n := len(plugins)
+ if cap(plugins) == n {
+ nplugins := make([]Plugin, n, n+10) // very unlikely to need more than this
+ copy(nplugins, plugins)
+ plugins = nplugins
+ }
+ plugins = plugins[0:n+1]
+ plugins[n] = p
+ log.Stderr("installed plugin:", p.Name())
+}
+
+// Each type we import as a protocol buffer (other than FileDescriptorProto) needs
+// a pointer to the FileDescriptorProto that represents it. These types achieve that
+// wrapping by placing each Proto inside a struct with the pointer to its File. The
+// structs have the same names as their contents, with "Proto" removed.
+// FileDescriptor is used to store the things that it points to.
+
+// The file and package name method are common to messages and enums.
+type common struct {
+ File *descriptor.FileDescriptorProto // File this object comes from.
+}
+
+// PackageName is name in the package clause in the generated file.
+func (c *common) PackageName() string { return uniquePackageOf(c.File) }
+
+// Descriptor represents a protocol buffer message.
+type Descriptor struct {
+ common
+ *descriptor.DescriptorProto
+ parent *Descriptor // The containing message, if any.
+ nested []*Descriptor // Inner messages, if any.
+ ext []*ExtensionDescriptor // Extensions, if any.
+ typename []string // Cached typename vector.
+}
+
+// TypeName returns the elements of the dotted type name.
+// The package name is not part of this name.
+func (d *Descriptor) TypeName() []string {
+ if d.typename != nil {
+ return d.typename
+ }
+ n := 0
+ for parent := d; parent != nil; parent = parent.parent {
+ n++
+ }
+ s := make([]string, n, n)
+ for parent := d; parent != nil; parent = parent.parent {
+ n--
+ s[n] = proto.GetString(parent.Name)
+ }
+ d.typename = s
+ return s
+}
+
+// EnumDescriptor describes an enum. If it's at top level, its parent will be nil.
+// Otherwise it will be the descriptor of the message in which it is defined.
+type EnumDescriptor struct {
+ common
+ *descriptor.EnumDescriptorProto
+ parent *Descriptor // The containing message, if any.
+ typename []string // Cached typename vector.
+}
+
+// TypeName returns the elements of the dotted type name.
+// The package name is not part of this name.
+func (e *EnumDescriptor) TypeName() (s []string) {
+ if e.typename != nil {
+ return e.typename
+ }
+ name := proto.GetString(e.Name)
+ if e.parent == nil {
+ s = make([]string, 1)
+ } else {
+ pname := e.parent.TypeName()
+ s = make([]string, len(pname)+1)
+ copy(s, pname)
+ }
+ s[len(s)-1] = name
+ e.typename = s
+ return s
+}
+
+// Everything but the last element of the full type name, CamelCased.
+// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
+func (e *EnumDescriptor) prefix() string {
+ typeName := e.TypeName()
+ ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
+ if e.parent == nil {
+ // If the enum is not part of a message, the prefix is just the type name.
+ ccPrefix = CamelCase(*e.Name) + "_"
+ }
+ return ccPrefix
+}
+
+// The integer value of the named constant in this enumerated type.
+func (e *EnumDescriptor) integerValueAsString(name string) string {
+ for _, c := range e.Value {
+ if proto.GetString(c.Name) == name {
+ return fmt.Sprint(proto.GetInt32(c.Number))
+ }
+ }
+ log.Exit("cannot find value for enum constant")
+ return ""
+}
+
+// ExtensionDescriptor desribes an extension. If it's at top level, its parent will be nil.
+// Otherwise it will be the descriptor of the message in which it is defined.
+type ExtensionDescriptor struct {
+ common
+ *descriptor.FieldDescriptorProto
+ parent *Descriptor // The containing message, if any.
+}
+
+// TypeName returns the elements of the dotted type name.
+// The package name is not part of this name.
+func (e *ExtensionDescriptor) TypeName() (s []string) {
+ name := proto.GetString(e.Name)
+ if e.parent == nil {
+ // top-level extension
+ s = make([]string, 1)
+ } else {
+ pname := e.parent.TypeName()
+ s = make([]string, len(pname)+1)
+ copy(s, pname)
+ }
+ s[len(s)-1] = name
+ return s
+}
+
+// FileDescriptor describes an protocol buffer descriptor file (.proto).
+// It includes slices of all the messages and enums defined within it.
+// Those slices are constructed by WrapTypes.
+type FileDescriptor struct {
+ *descriptor.FileDescriptorProto
+ desc []*Descriptor // All the messages defined in this file.
+ enum []*EnumDescriptor // All the enums defined in this file.
+ ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
+}
+
+// PackageName is the package name we'll use in the generated code to refer to this file.
+func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
+
+// The package named defined in the input for this file, possibly dotted.
+func (d *FileDescriptor) originalPackageName() string {
+ return proto.GetString(d.Package)
+}
+
+// Whether the proto library needs importing.
+// This will be true if there are any enums, extensions, or messages with extension ranges.
+func (d *FileDescriptor) needProtoImport() bool {
+ if len(d.enum) > 0 || len(d.ext) > 0 {
+ return true
+ }
+ for _, desc := range d.desc {
+ if len(desc.ext) > 0 || len(desc.ExtensionRange) > 0 {
+ return true
+ }
+ }
+ return false
+}
+
+// Object is an interface abstracting the abilities shared by enums and messages.
+type Object interface {
+ PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
+ TypeName() []string
+}
+
+// Each package name we generate must be unique. The package we're generating
+// gets its own name but every other package must have a unqiue name that does
+// not conflict in the code we generate. These names are chosen globally (although
+// they don't have to be, it simplifies things to do them globally).
+func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
+ s, ok := uniquePackageName[fd]
+ if !ok {
+ log.Exit("internal error: no package name defined for", proto.GetString(fd.Name))
+ }
+ return s
+}
+
+// Generator is the type whose methods generate the output, stored in the associated response structure.
+type Generator struct {
+ bytes.Buffer
+
+ Request *plugin.CodeGeneratorRequest // The input.
+ Response *plugin.CodeGeneratorResponse // The output.
+
+ packageName string // What we're calling ourselves.
+ allFiles []*FileDescriptor // All files in the tree
+ genFiles []*FileDescriptor // Those files we will generate output for.
+ file *FileDescriptor // The file we are compiling now.
+ typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
+ indent string
+}
+
+// New creates a new generator and allocates the request and response protobufs.
+func New() *Generator {
+ g := new(Generator)
+ g.Request = plugin.NewCodeGeneratorRequest()
+ g.Response = plugin.NewCodeGeneratorResponse()
+ return g
+}
+
+// Error reports a problem, including an os.Error, and exits the program.
+func (g *Generator) Error(err os.Error, msgs ...string) {
+ s := strings.Join(msgs, " ") + ":" + err.String()
+ log.Stderr("protoc-gen-go: error: ", s)
+ g.Response.Error = proto.String(s)
+ os.Exit(1)
+}
+
+// Fail reports a problem and exits the program.
+func (g *Generator) Fail(msgs ...string) {
+ s := strings.Join(msgs, " ")
+ log.Stderr("protoc-gen-go: error: ", s)
+ g.Response.Error = proto.String(s)
+ os.Exit(1)
+}
+
+// DefaultPackageName returns the package name printed for the object.
+// If its file is in a different package, it returns the package name we're using for this file, plus ".".
+// Otherwise it returns the empty string.
+func (g *Generator) DefaultPackageName(obj Object) string {
+ pkg := obj.PackageName()
+ if pkg == g.packageName {
+ return ""
+ }
+ return pkg + "."
+}
+
+// For each input file, the unique package name to use, underscored.
+var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
+
+// SetPackageNames Sets the package name for this run.
+// The package name must agree across all files being generated.
+// It also defines unique package names for all imported files.
+func (g *Generator) SetPackageNames() {
+ inUse := make(map[string]bool)
+ pkg := proto.GetString(g.genFiles[0].Package)
+ g.packageName = strings.Map(DotToUnderscore, pkg)
+ inUse[pkg] = true
+ for _, f := range g.genFiles {
+ thisPkg := proto.GetString(f.Package)
+ if thisPkg != pkg {
+ g.Fail("inconsistent package names:", thisPkg, pkg)
+ }
+ }
+AllFiles:
+ for _, f := range g.allFiles {
+ for _, genf := range g.genFiles {
+ if f == genf {
+ // In this package already.
+ uniquePackageName[f.FileDescriptorProto] = g.packageName
+ continue AllFiles
+ }
+ }
+ truePkg := proto.GetString(f.Package)
+ pkg := truePkg
+ for {
+ _, present := inUse[pkg]
+ if present {
+ // It's a duplicate; must rename.
+ pkg += "X"
+ continue
+ }
+ break
+ }
+ // Install it.
+ inUse[pkg] = true
+ uniquePackageName[f.FileDescriptorProto] = strings.Map(DotToUnderscore, pkg)
+ }
+}
+
+// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
+// and FileDescriptorProtos into file-referenced objects within the Generator.
+// It also creates the list of files to generate and so should be called before GenerateAllFiles.
+func (g *Generator) WrapTypes() {
+ g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
+ for i, f := range g.Request.ProtoFile {
+ pkg := proto.GetString(f.Package)
+ if pkg == "" {
+ g.Fail(proto.GetString(f.Name), "is missing a package declaration")
+ }
+ // We must wrap the descriptors before we wrap the enums
+ descs := wrapDescriptors(f)
+ g.buildNestedDescriptors(descs)
+ enums := wrapEnumDescriptors(f, descs)
+ exts := wrapExtensions(f)
+ g.allFiles[i] = &FileDescriptor{
+ FileDescriptorProto: f,
+ desc: descs,
+ enum: enums,
+ ext: exts,
+ }
+ }
+
+ g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
+FindFiles:
+ for i, fileName := range g.Request.FileToGenerate {
+ // Search the list. This algorithm is n^2 but n is tiny.
+ for _, file := range g.allFiles {
+ if fileName == proto.GetString(file.Name) {
+ g.genFiles[i] = file
+ continue FindFiles
+ }
+ }
+ g.Fail("could not find file named", fileName)
+ }
+ g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
+}
+
+// Scan the descriptors in this file. For each one, build the slice of nested descriptors
+func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
+ for _, desc := range descs {
+ if len(desc.NestedType) != 0 {
+ desc.nested = make([]*Descriptor, len(desc.NestedType))
+ n := 0
+ for _, nest := range descs {
+ if nest.parent == desc {
+ desc.nested[n] = nest
+ n++
+ }
+ }
+ if n != len(desc.NestedType) {
+ g.Fail("internal error: nesting failure for", proto.GetString(desc.Name))
+ }
+ }
+ }
+}
+
+// Construct the Descriptor and add it to the slice
+func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
+ d := &Descriptor{common{File: file}, desc, parent, nil, nil, nil}
+
+ d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
+ for i, field := range desc.Extension {
+ d.ext[i] = &ExtensionDescriptor{common{File: file}, field, d}
+ }
+
+ if len(sl) == cap(sl) {
+ nsl := make([]*Descriptor, len(sl), 2*len(sl))
+ copy(nsl, sl)
+ sl = nsl
+ }
+ sl = sl[0 : len(sl)+1]
+ sl[len(sl)-1] = d
+ return sl
+}
+
+// Return a slice of all the Descriptors defined within this file
+func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
+ sl := make([]*Descriptor, 0, len(file.MessageType)+10)
+ for _, desc := range file.MessageType {
+ sl = wrapThisDescriptor(sl, desc, nil, file)
+ }
+ return sl
+}
+
+// Wrap this Descriptor, recursively
+func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
+ sl = addDescriptor(sl, desc, parent, file)
+ me := sl[len(sl)-1]
+ for _, nested := range desc.NestedType {
+ sl = wrapThisDescriptor(sl, nested, me, file)
+ }
+ return sl
+}
+
+// Construct the EnumDescriptor and add it to the slice
+func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
+ if len(sl) == cap(sl) {
+ nsl := make([]*EnumDescriptor, len(sl), 2*len(sl))
+ copy(nsl, sl)
+ sl = nsl
+ }
+ sl = sl[0 : len(sl)+1]
+ sl[len(sl)-1] = &EnumDescriptor{common{File: file}, desc, parent, nil}
+ return sl
+}
+
+// Return a slice of all the EnumDescriptors defined within this file
+func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
+ sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
+ for _, enum := range file.EnumType {
+ sl = addEnumDescriptor(sl, enum, nil, file)
+ }
+ for _, nested := range descs {
+ sl = wrapEnumDescriptorsInMessage(sl, nested, file)
+ }
+ return sl
+}
+
+// Wrap this EnumDescriptor, recursively
+func wrapEnumDescriptorsInMessage(sl []*EnumDescriptor, desc *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
+ for _, enum := range desc.EnumType {
+ sl = addEnumDescriptor(sl, enum, desc, file)
+ }
+ for _, nested := range desc.nested {
+ sl = wrapEnumDescriptorsInMessage(sl, nested, file)
+ }
+ return sl
+}
+
+// Return a slice of all the top-level ExtensionDescriptors defined within this file.
+func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
+ sl := make([]*ExtensionDescriptor, len(file.Extension))
+ for i, field := range file.Extension {
+ sl[i] = &ExtensionDescriptor{common{File: file}, field, nil}
+ }
+ return sl
+}
+
+// BuildTypeNameMap builds the map from fully qualified type names to objects.
+// The key names for the map come from the input data, which puts a period at the beginning.
+// It should be called after SetPackageNames and before GenerateAllFiles.
+func (g *Generator) BuildTypeNameMap() {
+ g.typeNameToObject = make(map[string]Object)
+ for _, f := range g.allFiles {
+ dottedPkg := "." + f.originalPackageName() + "."
+ for _, enum := range f.enum {
+ name := dottedPkg + dottedSlice(enum.TypeName())
+ g.typeNameToObject[name] = enum
+ }
+ for _, desc := range f.desc {
+ name := dottedPkg + dottedSlice(desc.TypeName())
+ g.typeNameToObject[name] = desc
+ }
+ }
+}
+
+// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
+// returns the descriptor for the message or enum with that name.
+func (g *Generator) ObjectNamed(typeName string) Object {
+ f, ok := g.typeNameToObject[typeName]
+ if !ok {
+ g.Fail("can't find object with type", typeName)
+ }
+ return f
+}
+
+// P prints the arguments to the generated output. It handles strings and int32s, plus
+// handling indirections because they may be *string, etc.
+func (g *Generator) P(str ...interface{}) {
+ g.WriteString(g.indent)
+ for _, v := range str {
+ switch s := v.(type) {
+ case string:
+ g.WriteString(s)
+ case *string:
+ g.WriteString(*s)
+ case *int32:
+ g.WriteString(fmt.Sprintf("%d", *s))
+ default:
+ g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
+ }
+ }
+ g.WriteByte('\n')
+}
+
+// In Indents the output one tab stop.
+func (g *Generator) In() { g.indent += "\t" }
+
+// Out unindents the output one tab stop.
+func (g *Generator) Out() {
+ if len(g.indent) > 0 {
+ g.indent = g.indent[1:]
+ }
+}
+
+// GenerateAllFiles generates the output for all the files we're outputting.
+func (g *Generator) GenerateAllFiles() {
+ for i, file := range g.genFiles {
+ g.Reset()
+ g.generate(file)
+ g.runPlugins(file)
+ g.Response.File[i] = plugin.NewCodeGeneratorResponse_File()
+ g.Response.File[i].Name = proto.String(goFileName(*file.Name))
+ g.Response.File[i].Content = proto.String(g.String())
+ }
+}
+
+// Run all the plugins associated with the file.
+func (g *Generator) runPlugins(file *FileDescriptor) {
+ for _, p := range plugins {
+ p.Generate(g, file)
+ }
+}
+
+
+// FileOf return the FileDescriptor for this FileDescriptorProto.
+func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
+ for _, file := range g.allFiles {
+ if file.FileDescriptorProto == fd {
+ return file
+ }
+ }
+ g.Fail("could not find file in table:", proto.GetString(fd.Name))
+ return nil
+}
+
+// Fill the response protocol buffer with the generated output for all the files we're
+// supposed to generate.
+func (g *Generator) generate(file *FileDescriptor) {
+ g.file = g.FileOf(file.FileDescriptorProto)
+ g.generateHeader()
+ g.generateImports()
+ for _, enum := range g.file.enum {
+ g.generateEnum(enum)
+ }
+ for _, desc := range g.file.desc {
+ g.generateMessage(desc)
+ }
+ for _, ext := range g.file.ext {
+ g.generateExtension(ext)
+ }
+ g.generateInitFunction()
+}
+
+// Generate the header, including package definition and imports
+func (g *Generator) generateHeader() {
+ g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
+ g.P("// DO NOT EDIT!")
+ g.P()
+ g.P("package ", g.file.PackageName())
+ g.P()
+}
+
+// Generate the header, including package definition and imports
+func (g *Generator) generateImports() {
+ if g.file.needProtoImport() {
+ g.P(`import "goprotobuf.googlecode.com/hg/proto"`)
+ }
+ for _, s := range g.file.Dependency {
+ // Need to find the descriptor for this file
+ for _, fd := range g.allFiles {
+ if proto.GetString(fd.Name) == s {
+ filename := goFileName(s)
+ if strings.HasSuffix(filename, ".go") {
+ filename = filename[0:len(filename)-3]
+ }
+ g.P("import ", fd.PackageName(), " ", Quote(filename))
+ break
+ }
+ }
+ }
+ g.P()
+ // TODO: may need to worry about uniqueness across plugins
+ for _, p := range plugins {
+ p.GenerateImports(g, g.file)
+ g.P()
+ }
+}
+
+// Generate the enum definitions for this EnumDescriptor.
+func (g *Generator) generateEnum(enum *EnumDescriptor) {
+ // The full type name
+ typeName := enum.TypeName()
+ // The full type name, CamelCased.
+ ccTypeName := CamelCaseSlice(typeName)
+ ccPrefix := enum.prefix()
+ g.P("type ", ccTypeName, " int32")
+ g.P("const (")
+ g.In()
+ for _, e := range enum.Value {
+ g.P(ccPrefix+*e.Name, " = ", e.Number)
+ }
+ g.Out()
+ g.P(")")
+ g.P("var ", ccTypeName, "_name = map[int32] string {")
+ g.In()
+ generated := make(map[int32] bool) // avoid duplicate values
+ for _, e := range enum.Value {
+ duplicate := ""
+ if _, present := generated[*e.Number]; present {
+ duplicate = "// Duplicate value: "
+ }
+ g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
+ generated[*e.Number] = true
+ }
+ g.Out()
+ g.P("}")
+ g.P("var ", ccTypeName, "_value = map[string] int32 {")
+ g.In()
+ for _, e := range enum.Value {
+ g.P(Quote(*e.Name), ": ", e.Number, ",")
+ }
+ g.Out()
+ g.P("}")
+ g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
+ g.In()
+ g.P("e := ", ccTypeName, "(x)")
+ g.P("return &e")
+ g.Out()
+ g.P("}")
+ g.P()
+}
+
+// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
+// identifies details of the field for the protocol buffer marshaling and unmarshaling
+// code. The fields are:
+// wire encoding
+// protocol tag number
+// opt,req,rep for optional, required, or repeated
+// name= the original declared name
+// enum= the name of the enum type if it is an enum-typed field.
+// def= string representation of the default value, if any.
+// The default value must be in a representation that can be used at run-time
+// to generate the default value. Thus bools become 0 and 1, for instance.
+func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
+ optrepreq := ""
+ switch {
+ case isOptional(field):
+ optrepreq = "opt"
+ case isRequired(field):
+ optrepreq = "req"
+ case isRepeated(field):
+ optrepreq = "rep"
+ }
+ defaultValue := proto.GetString(field.DefaultValue)
+ if defaultValue != "" {
+ switch *field.Type {
+ case descriptor.FieldDescriptorProto_TYPE_BOOL:
+ if defaultValue == "true" {
+ defaultValue = "1"
+ } else {
+ defaultValue = "0"
+ }
+ case descriptor.FieldDescriptorProto_TYPE_STRING,
+ descriptor.FieldDescriptorProto_TYPE_BYTES:
+ // Protect frogs.
+ defaultValue = Quote(defaultValue)
+ // Don't need the quotes
+ defaultValue = defaultValue[1 : len(defaultValue)-1]
+ case descriptor.FieldDescriptorProto_TYPE_ENUM:
+ // For enums we need to provide the integer constant.
+ obj := g.ObjectNamed(proto.GetString(field.TypeName))
+ enum, ok := obj.(*EnumDescriptor)
+ if !ok {
+ g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
+ }
+ defaultValue = enum.integerValueAsString(defaultValue)
+ }
+ defaultValue = ",def=" + defaultValue
+ }
+ enum := ""
+ if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
+ obj := g.ObjectNamed(proto.GetString(field.TypeName))
+ enum = ",enum=" + obj.PackageName() + "." + CamelCaseSlice(obj.TypeName())
+ }
+ name := proto.GetString(field.Name)
+ if name == CamelCase(name) {
+ name = ""
+ } else {
+ name = ",name=" + name
+ }
+ return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s)",
+ wiretype,
+ proto.GetInt32(field.Number),
+ optrepreq,
+ name,
+ enum,
+ defaultValue))
+}
+
+func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
+ switch typ {
+ case descriptor.FieldDescriptorProto_TYPE_GROUP:
+ return false
+ case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
+ return false
+ case descriptor.FieldDescriptorProto_TYPE_BYTES:
+ return false
+ }
+ return true
+}
+
+// TypeName is the printed name appropriate for an item. If the object is in the current file,
+// TypeName drops the package name and underscores the rest.
+// Otherwise the object is from another package; and the result is the underscored
+// package name followed by the item name.
+// The result always has an initial capital.
+func (g *Generator) TypeName(obj Object) string {
+ return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
+}
+
+// TypeNameWithPackage is like TypeName, but always includes the package
+// name even if the object is in our own package.
+func (g *Generator) TypeNameWithPackage(obj Object) string {
+ return obj.PackageName() + CamelCaseSlice(obj.TypeName())
+}
+
+// GoType returns a string representing the type name, and the wire type
+func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
+ // TODO: Options.
+ switch *field.Type {
+ case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
+ typ, wire = "float64", "fixed64"
+ case descriptor.FieldDescriptorProto_TYPE_FLOAT:
+ typ, wire = "float32", "fixed32"
+ case descriptor.FieldDescriptorProto_TYPE_INT64:
+ typ, wire = "int64", "varint"
+ case descriptor.FieldDescriptorProto_TYPE_UINT64:
+ typ, wire = "uint64", "varint"
+ case descriptor.FieldDescriptorProto_TYPE_INT32:
+ typ, wire = "int32", "varint"
+ case descriptor.FieldDescriptorProto_TYPE_UINT32:
+ typ, wire = "uint32", "varint"
+ case descriptor.FieldDescriptorProto_TYPE_FIXED64:
+ typ, wire = "uint64", "fixed64"
+ case descriptor.FieldDescriptorProto_TYPE_FIXED32:
+ typ, wire = "uint32", "fixed32"
+ case descriptor.FieldDescriptorProto_TYPE_BOOL:
+ typ, wire = "bool", "varint"
+ case descriptor.FieldDescriptorProto_TYPE_STRING:
+ typ, wire = "string", "bytes"
+ case descriptor.FieldDescriptorProto_TYPE_GROUP:
+ desc := g.ObjectNamed(proto.GetString(field.TypeName))
+ typ, wire = "*"+g.TypeName(desc), "group"
+ case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
+ desc := g.ObjectNamed(proto.GetString(field.TypeName))
+ typ, wire = "*"+g.TypeName(desc), "bytes"
+ case descriptor.FieldDescriptorProto_TYPE_BYTES:
+ typ, wire = "[]byte", "bytes"
+ case descriptor.FieldDescriptorProto_TYPE_ENUM:
+ desc := g.ObjectNamed(proto.GetString(field.TypeName))
+ typ, wire = g.TypeName(desc), "varint"
+ case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
+ typ, wire = "int32", "fixed32"
+ case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
+ typ, wire = "int64", "fixed64"
+ case descriptor.FieldDescriptorProto_TYPE_SINT32:
+ typ, wire = "int32", "zigzag32"
+ case descriptor.FieldDescriptorProto_TYPE_SINT64:
+ typ, wire = "int64", "zigzag64"
+ default:
+ g.Fail("unknown type for", proto.GetString(field.Name))
+ }
+ if isRepeated(field) {
+ typ = "[]" + typ
+ } else if needsStar(*field.Type) {
+ typ = "*" + typ
+ }
+ return
+}
+
+// Generate the type and default constant definitions for this Descriptor.
+func (g *Generator) generateMessage(message *Descriptor) {
+ // The full type name
+ typeName := message.TypeName()
+ // The full type name, CamelCased.
+ ccTypeName := CamelCaseSlice(typeName)
+
+ g.P("type ", ccTypeName, " struct {")
+ g.In()
+ for _, field := range message.Field {
+ fieldname := CamelCase(*field.Name)
+ typename, wiretype := g.GoType(message, field)
+ tag := g.goTag(field, wiretype)
+ g.P(fieldname, "\t", typename, "\t", tag)
+ }
+ if len(message.ExtensionRange) > 0 {
+ g.P("XXX_extensions\t\tmap[int32][]byte")
+ }
+ g.P("XXX_unrecognized\t[]byte")
+ g.Out()
+ g.P("}")
+
+ // Reset and New functions
+ g.P("func (this *", ccTypeName, ") Reset() {")
+ g.In()
+ g.P("*this = ", ccTypeName, "{}")
+ g.Out()
+ g.P("}")
+ g.P("func New", ccTypeName, "() *", ccTypeName, " {")
+ g.In()
+ g.P("return new(", ccTypeName, ")")
+ g.Out()
+ g.P("}")
+
+ // Extension support methods
+ if len(message.ExtensionRange) > 0 {
+ g.P()
+ g.P("var extRange_", ccTypeName, " = []proto.ExtensionRange{")
+ g.In()
+ for _, r := range message.ExtensionRange {
+ end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
+ g.P("proto.ExtensionRange{", r.Start, ", ", end, "},")
+ }
+ g.Out()
+ g.P("}")
+ g.P("func (*", ccTypeName, ") ExtensionRangeArray() []proto.ExtensionRange {")
+ g.In()
+ g.P("return extRange_", ccTypeName)
+ g.Out()
+ g.P("}")
+ g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
+ g.In()
+ g.P("if this.XXX_extensions == nil {")
+ g.In()
+ g.P("this.XXX_extensions = make(map[int32][]byte)")
+ g.Out()
+ g.P("}")
+ g.P("return this.XXX_extensions")
+ g.Out()
+ g.P("}")
+ }
+
+ // Default constants
+ for _, field := range message.Field {
+ def := proto.GetString(field.DefaultValue)
+ if def == "" {
+ continue
+ }
+ fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
+ typename, _ := g.GoType(message, field)
+ if typename[0] == '*' {
+ typename = typename[1:]
+ }
+ kind := "const "
+ switch {
+ case typename == "bool":
+ case typename == "string":
+ def = Quote(def)
+ case typename == "[]byte":
+ def = "[]byte(" + Quote(def) + ")"
+ kind = "var "
+ case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
+ // Must be an enum. Need to construct the prefixed name.
+ obj := g.ObjectNamed(proto.GetString(field.TypeName))
+ enum, ok := obj.(*EnumDescriptor)
+ if !ok {
+ log.Stderr("don't know how to generate constant for", fieldname)
+ continue
+ }
+ def = enum.prefix() + def
+ }
+ g.P(kind, fieldname, " ", typename, " = ", def)
+ }
+ g.P()
+
+ for _, ext := range message.ext {
+ g.generateExtension(ext)
+ }
+}
+
+func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
+ // The full type name
+ typeName := ext.TypeName()
+ // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
+ for i, s := range typeName {
+ typeName[i] = CamelCase(s)
+ }
+ ccTypeName := "E_" + strings.Join(typeName, "_")
+
+ extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
+ field := ext.FieldDescriptorProto
+ fieldType, wireType := g.GoType(ext.parent, field)
+ tag := g.goTag(field, wireType)
+
+ g.P("var ", ccTypeName, " = &proto.ExtensionDesc{")
+ g.In()
+ g.P("ExtendedType: (", extendedType, ")(nil),")
+ g.P("ExtensionType: (", fieldType, ")(nil),")
+ g.P("Field: ", field.Number, ",")
+ g.P("Tag: ", tag, ",")
+
+ g.Out()
+ g.P("}")
+ g.P()
+}
+
+func (g *Generator) generateInitFunction() {
+ g.P("func init() {")
+ g.In()
+ for _, enum := range g.file.enum {
+ g.generateEnumRegistration(enum)
+ }
+ g.Out()
+ g.P("}")
+}
+
+func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
+ pkg := g.packageName + "." // We always print the full package name here.
+ // The full type name
+ typeName := enum.TypeName()
+ // The full type name, CamelCased.
+ ccTypeName := CamelCaseSlice(typeName)
+ g.P("proto.RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
+}
+
+// And now lots of helper functions.
+
+// CamelCase returns the CamelCased name. Given foo_bar_Baz, the result is FooBar_Baz.
+func CamelCase(name string) string {
+ elems := strings.Split(name, "_", 0)
+ for i, e := range elems {
+ if e == "" {
+ elems[i] = "_"
+ continue
+ }
+ runes := []int(e)
+ if unicode.IsLower(runes[0]) {
+ runes[0] = unicode.ToUpper(runes[0])
+ elems[i] = string(runes)
+ } else {
+ if i > 0 {
+ elems[i] = "_" + e
+ }
+ }
+ }
+ s := strings.Join(elems, "")
+ // Name must not begin with an underscore.
+ if len(s) > 0 && s[0] == '_' {
+ s = "X" + s[1:]
+ }
+ return s
+}
+
+// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
+// be joined with "_".
+func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
+
+// dottedSlice turns a sliced name into a dotted name.
+func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
+
+// Quote returns a Go-source quoted string representation of s.
+func Quote(s string) string { return fmt.Sprintf("%q", s) }
+
+// Given a .proto file name, return the output name for the generated Go program.
+func goFileName(name string) string {
+ if strings.HasSuffix(name, ".proto") {
+ name = name[0 : len(name)-6]
+ }
+ return name + ".pb.go"
+}
+
+// Is this field optional?
+func isOptional(field *descriptor.FieldDescriptorProto) bool {
+ return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
+}
+
+// Is this field required?
+func isRequired(field *descriptor.FieldDescriptorProto) bool {
+ return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
+}
+
+// Is this field repeated?
+func isRepeated(field *descriptor.FieldDescriptorProto) bool {
+ return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
+}
+
+// DotToUnderscore is the mapping function used to generate Go names from package names,
+// which can be dotted in the input .proto file.
+func DotToUnderscore(rune int) int {
+ if rune == '.' {
+ return '_'
+ }
+ return rune
+}
diff --git a/compiler/main.go b/compiler/main.go
index c8aaa45..568a62b 100644
--- a/compiler/main.go
+++ b/compiler/main.go
@@ -35,44 +35,35 @@
This plugin takes no options and the protocol buffer file syntax does
not yet define any options for Go, so program does no option evaluation.
That may change.
-
- Not supported yet:
- Services
*/
package main
import (
- "bytes"
- "fmt"
"io/ioutil"
- "log"
"os"
- "strings"
- "unicode"
"goprotobuf.googlecode.com/hg/proto"
- plugin "goprotobuf.googlecode.com/hg/compiler/plugin"
- descriptor "goprotobuf.googlecode.com/hg/compiler/descriptor"
+ "goprotobuf.googlecode.com/hg/compiler/generator"
)
func main() {
// Begin by allocating a generator. The request and response structures are stored there
// so we can do error handling easily - the response structure contains the field to
// report failure.
- g := NewGenerator()
+ g := generator.New()
data, err := ioutil.ReadAll(os.Stdin)
if err != nil {
- g.error(err, "reading input")
+ g.Error(err, "reading input")
}
- if err := proto.Unmarshal(data, g.request); err != nil {
- g.error(err, "parsing input proto")
+ if err := proto.Unmarshal(data, g.Request); err != nil {
+ g.Error(err, "parsing input proto")
}
- if len(g.request.FileToGenerate) == 0 {
- g.fail("no files to generate")
+ if len(g.Request.FileToGenerate) == 0 {
+ g.Fail("no files to generate")
}
// Create a wrapped version of the Descriptors and EnumDescriptors that
@@ -85,936 +76,12 @@
g.GenerateAllFiles()
// Send back the results.
- data, err = proto.Marshal(g.response)
+ data, err = proto.Marshal(g.Response)
if err != nil {
- g.error(err, "failed to marshal output proto")
+ g.Error(err, "failed to marshal output proto")
}
_, err = os.Stdout.Write(data)
if err != nil {
- g.error(err, "failed to write output proto")
+ g.Error(err, "failed to write output proto")
}
}
-
-// Each type we import as a protocol buffer (other than FileDescriptorProto) needs
-// a pointer to the FileDescriptorProto that represents it. These types achieve that
-// wrapping by placing each Proto inside a struct with the pointer to its File. The
-// structs have the same names as their contents, with "Proto" removed.
-// FileDescriptor is used to store the things that it points to.
-
-// The file and package name method are common to messages and enums.
-type common struct {
- file *descriptor.FileDescriptorProto // File this object comes from.
-}
-
-// The package name we will produce in our output.
-func (c *common) packageName() string { return uniquePackageOf(c.file) }
-
-// A message (struct).
-type Descriptor struct {
- common
- *descriptor.DescriptorProto
- parent *Descriptor // The containing message, if any.
- nested []*Descriptor // Inner messages, if any.
- ext []*ExtensionDescriptor // Extensions, if any.
- typename []string // Cached typename vector.
-}
-
-// Return the elements of the dotted type name. The package name is not part
-// of this name.
-func (d *Descriptor) typeName() []string {
- if d.typename != nil {
- return d.typename
- }
- n := 0
- for parent := d; parent != nil; parent = parent.parent {
- n++
- }
- s := make([]string, n, n)
- for parent := d; parent != nil; parent = parent.parent {
- n--
- s[n] = proto.GetString(parent.Name)
- }
- d.typename = s
- return s
-}
-
-// An enum. If it's at top level, its parent will be nil. Otherwise it will be
-// the descriptor of the message in which it is defined.
-type EnumDescriptor struct {
- common
- *descriptor.EnumDescriptorProto
- parent *Descriptor // The containing message, if any.
- typename []string // Cached typename vector.
-}
-
-// Return the elements of the dotted type name.
-func (e *EnumDescriptor) typeName() (s []string) {
- if e.typename != nil {
- return e.typename
- }
- name := proto.GetString(e.Name)
- if e.parent == nil {
- s = make([]string, 1)
- } else {
- pname := e.parent.typeName()
- s = make([]string, len(pname)+1)
- copy(s, pname)
- }
- s[len(s)-1] = name
- e.typename = s
- return s
-}
-
-// Everything but the last element of the full type name, CamelCased.
-// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
-func (e *EnumDescriptor) prefix() string {
- typeName := e.typeName()
- ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
- if e.parent == nil {
- // If the enum is not part of a message, the prefix is just the type name.
- ccPrefix = CamelCase(*e.Name) + "_"
- }
- return ccPrefix
-}
-
-// The integer value of the named constant in this enumerated type.
-func (e *EnumDescriptor) integerValueAsString(name string) string {
- for _, c := range e.Value {
- if proto.GetString(c.Name) == name {
- return fmt.Sprint(proto.GetInt32(c.Number))
- }
- }
- log.Exit("cannot find value for enum constant")
- return ""
-}
-
-// An extension. If it's at top level, its parent will be nil. Otherwise it will be
-// the descriptor of the message in which it is defined.
-type ExtensionDescriptor struct {
- common
- *descriptor.FieldDescriptorProto
- parent *Descriptor // The containing message, if any.
-}
-
-// Return the elements of the dotted type name.
-func (e *ExtensionDescriptor) typeName() (s []string) {
- name := proto.GetString(e.Name)
- if e.parent == nil {
- // top-level extension
- s = make([]string, 1)
- } else {
- pname := e.parent.typeName()
- s = make([]string, len(pname)+1)
- copy(s, pname)
- }
- s[len(s)-1] = name
- return s
-}
-
-// A file. Includes slices of all the messages and enums defined within it.
-// Those slices are constructed by WrapTypes.
-type FileDescriptor struct {
- *descriptor.FileDescriptorProto
- desc []*Descriptor // All the messages defined in this file.
- enum []*EnumDescriptor // All the enums defined in this file.
- ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
-}
-
-// The package name we'll use in the generated code to refer to this file.
-func (d *FileDescriptor) packageName() string { return uniquePackageOf(d.FileDescriptorProto) }
-
-// The package named defined in the input for this file, possibly dotted.
-func (d *FileDescriptor) originalPackageName() string {
- return proto.GetString(d.Package)
-}
-
-// Whether the proto library needs importing.
-// This will be true if there are any enums or any extensions, or any messages with extension ranges.
-func (d *FileDescriptor) needProtoImport() bool {
- if len(d.enum) > 0 || len(d.ext) > 0 {
- return true
- }
- for _, desc := range d.desc {
- if len(desc.ext) > 0 || len(desc.ExtensionRange) > 0 {
- return true
- }
- }
- return false
-}
-
-// Simplify some things by abstracting the abilities shared by enums and messages.
-type Object interface {
- packageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
- typeName() []string
-}
-
-// Each package name we generate must be unique. The package we're generating
-// gets its own name but every other package must have a unqiue name that does
-// not conflict in the code we generate. These names are chosen globally (although
-// they don't have to be, it simplifies things to do them globally).
-func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
- s, ok := uniquePackageName[fd]
- if !ok {
- log.Exit("internal error: no package name defined for", proto.GetString(fd.Name))
- }
- return s
-}
-
-// The type whose methods generate the output, stored in the associated response structure.
-type Generator struct {
- bytes.Buffer
-
- request *plugin.CodeGeneratorRequest // The input.
- response *plugin.CodeGeneratorResponse // The output.
-
- packageName string // What we're calling ourselves.
- allFiles []*FileDescriptor // All files in the tree
- genFiles []*FileDescriptor // Those files we will generate output for.
- file *FileDescriptor // The file we are compiling now.
- typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
- indent string
-}
-
-// Create a new generator and allocate the request and response protobufs.
-func NewGenerator() *Generator {
- g := new(Generator)
- g.request = plugin.NewCodeGeneratorRequest()
- g.response = plugin.NewCodeGeneratorResponse()
- return g
-}
-
-// Report problem, including an os.Error, and fail.
-func (g *Generator) error(err os.Error, msgs ...string) {
- s := strings.Join(msgs, " ") + ":" + err.String()
- log.Stderr("protoc-gen-go: error: ", s)
- g.response.Error = proto.String(s)
- os.Exit(1)
-}
-
-// Report problem and fail.
-func (g *Generator) fail(msgs ...string) {
- s := strings.Join(msgs, " ")
- log.Stderr("protoc-gen-go: error: ", s)
- g.response.Error = proto.String(s)
- os.Exit(1)
-}
-
-// If this file is in a different package, return the package name we're using for this file, plus ".".
-// Otherwise return the empty string.
-func (g *Generator) DefaultPackageName(obj Object) string {
- pkg := obj.packageName()
- if pkg == g.packageName {
- return ""
- }
- return pkg + "."
-}
-
-// For each input file, the unique package name to use, underscored.
-var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
-
-// Set the package name for this run. It must agree across all files being generated.
-// Also define unique package names for all imported files.
-func (g *Generator) SetPackageNames() {
- inUse := make(map[string]bool)
- pkg := proto.GetString(g.genFiles[0].Package)
- g.packageName = strings.Map(DotToUnderscore, pkg)
- inUse[pkg] = true
- for _, f := range g.genFiles {
- thisPkg := proto.GetString(f.Package)
- if thisPkg != pkg {
- g.fail("inconsistent package names:", thisPkg, pkg)
- }
- }
-AllFiles:
- for _, f := range g.allFiles {
- for _, genf := range g.genFiles {
- if f == genf {
- // In this package already.
- uniquePackageName[f.FileDescriptorProto] = g.packageName
- continue AllFiles
- }
- }
- truePkg := proto.GetString(f.Package)
- pkg := truePkg
- for {
- _, present := inUse[pkg]
- if present {
- // It's a duplicate; must rename.
- pkg += "X"
- continue
- }
- break
- }
- // Install it.
- inUse[pkg] = true
- uniquePackageName[f.FileDescriptorProto] = strings.Map(DotToUnderscore, pkg)
- }
-}
-
-// Walk the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos and FileDescriptorProtos
-// into file-referenced objects within the Generator. Also create the list of files
-// to generate
-func (g *Generator) WrapTypes() {
- g.allFiles = make([]*FileDescriptor, len(g.request.ProtoFile))
- for i, f := range g.request.ProtoFile {
- pkg := proto.GetString(f.Package)
- if pkg == "" {
- g.fail(proto.GetString(f.Name), "is missing a package declaration")
- }
- // We must wrap the descriptors before we wrap the enums
- descs := WrapDescriptors(f)
- g.BuildNestedDescriptors(descs)
- enums := WrapEnumDescriptors(f, descs)
- exts := WrapExtensions(f)
- g.allFiles[i] = &FileDescriptor{
- FileDescriptorProto: f,
- desc: descs,
- enum: enums,
- ext: exts,
- }
- }
-
- g.genFiles = make([]*FileDescriptor, len(g.request.FileToGenerate))
-FindFiles:
- for i, fileName := range g.request.FileToGenerate {
- // Search the list. This algorithm is n^2 but n is tiny.
- for _, file := range g.allFiles {
- if fileName == proto.GetString(file.Name) {
- g.genFiles[i] = file
- continue FindFiles
- }
- }
- g.fail("could not find file named", fileName)
- }
- g.response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
-}
-
-// Scan the descriptors in this file. For each one, build the slice of nested descriptors
-func (g *Generator) BuildNestedDescriptors(descs []*Descriptor) {
- for _, desc := range descs {
- if len(desc.NestedType) != 0 {
- desc.nested = make([]*Descriptor, len(desc.NestedType))
- n := 0
- for _, nest := range descs {
- if nest.parent == desc {
- desc.nested[n] = nest
- n++
- }
- }
- if n != len(desc.NestedType) {
- g.fail("internal error: nesting failure for", proto.GetString(desc.Name))
- }
- }
- }
-}
-
-// Construct the Descriptor and add it to the slice
-func AddDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
- d := &Descriptor{common{file: file}, desc, parent, nil, nil, nil}
-
- d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
- for i, field := range desc.Extension {
- d.ext[i] = &ExtensionDescriptor{common{file: file}, field, d}
- }
-
- if len(sl) == cap(sl) {
- nsl := make([]*Descriptor, len(sl), 2*len(sl))
- copy(nsl, sl)
- sl = nsl
- }
- sl = sl[0 : len(sl)+1]
- sl[len(sl)-1] = d
- return sl
-}
-
-// Return a slice of all the Descriptors defined within this file
-func WrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
- sl := make([]*Descriptor, 0, len(file.MessageType)+10)
- for _, desc := range file.MessageType {
- sl = WrapThisDescriptor(sl, desc, nil, file)
- }
- return sl
-}
-
-// Wrap this Descriptor, recursively
-func WrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
- sl = AddDescriptor(sl, desc, parent, file)
- me := sl[len(sl)-1]
- for _, nested := range desc.NestedType {
- sl = WrapThisDescriptor(sl, nested, me, file)
- }
- return sl
-}
-
-// Construct the EnumDescriptor and add it to the slice
-func AddEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
- if len(sl) == cap(sl) {
- nsl := make([]*EnumDescriptor, len(sl), 2*len(sl))
- copy(nsl, sl)
- sl = nsl
- }
- sl = sl[0 : len(sl)+1]
- sl[len(sl)-1] = &EnumDescriptor{common{file: file}, desc, parent, nil}
- return sl
-}
-
-// Return a slice of all the EnumDescriptors defined within this file
-func WrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
- sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
- for _, enum := range file.EnumType {
- sl = AddEnumDescriptor(sl, enum, nil, file)
- }
- for _, nested := range descs {
- sl = WrapEnumDescriptorsInMessage(sl, nested, file)
- }
- return sl
-}
-
-// Wrap this EnumDescriptor, recursively
-func WrapEnumDescriptorsInMessage(sl []*EnumDescriptor, desc *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
- for _, enum := range desc.EnumType {
- sl = AddEnumDescriptor(sl, enum, desc, file)
- }
- for _, nested := range desc.nested {
- sl = WrapEnumDescriptorsInMessage(sl, nested, file)
- }
- return sl
-}
-
-// Return a slice of all the top-level ExtensionDescriptors defined within this file.
-func WrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
- sl := make([]*ExtensionDescriptor, len(file.Extension))
- for i, field := range file.Extension {
- sl[i] = &ExtensionDescriptor{common{file: file}, field, nil}
- }
- return sl
-}
-
-// Build the map from fully qualified type names to objects. The key for the map
-// comes from the input data, which puts a period at the beginning.
-func (g *Generator) BuildTypeNameMap() {
- g.typeNameToObject = make(map[string]Object)
- for _, f := range g.allFiles {
- dottedPkg := "." + f.originalPackageName() + "."
- for _, enum := range f.enum {
- name := dottedPkg + DottedSlice(enum.typeName())
- g.typeNameToObject[name] = enum
- }
- for _, desc := range f.desc {
- name := dottedPkg + DottedSlice(desc.typeName())
- g.typeNameToObject[name] = desc
- }
- }
-}
-
-// Given a fully-qualified input type name, return the descriptor for the message or enum with that type.
-func (g *Generator) objectNamed(typeName string) Object {
- f, ok := g.typeNameToObject[typeName]
- if !ok {
- g.fail("can't find object with type", typeName)
- }
- return f
-}
-
-// Print the arguments, handling indirections because they may be *string, etc.
-func (g *Generator) p(str ...interface{}) {
- g.WriteString(g.indent)
- for _, v := range str {
- switch s := v.(type) {
- case string:
- g.WriteString(s)
- case *string:
- g.WriteString(*s)
- case *int32:
- g.WriteString(fmt.Sprintf("%d", *s))
- default:
- g.fail(fmt.Sprintf("unknown type in printer: %T", v))
- }
- }
- g.WriteByte('\n')
-}
-
-// Indent the output one tab stop.
-func (g *Generator) in() { g.indent += "\t" }
-
-// Unindent the output one tab stop.
-func (g *Generator) out() {
- if len(g.indent) > 0 {
- g.indent = g.indent[1:]
- }
-}
-
-// Generate the output for all the files we're generating output for.
-func (g *Generator) GenerateAllFiles() {
- for i, file := range g.genFiles {
- g.Reset()
- g.Generate(file)
- g.response.File[i] = plugin.NewCodeGeneratorResponse_File()
- g.response.File[i].Name = proto.String(GoName(*file.Name))
- g.response.File[i].Content = proto.String(g.String())
- }
-}
-
-// Return the FileDescriptor for this FileDescriptorProto
-func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
- for _, file := range g.allFiles {
- if file.FileDescriptorProto == fd {
- return file
- }
- }
- g.fail("could not find file in table:", proto.GetString(fd.Name))
- return nil
-}
-
-// Fill the response protocol buffer with the generated output for all the files we're
-// supposed to generate.
-func (g *Generator) Generate(file *FileDescriptor) {
- g.file = g.FileOf(file.FileDescriptorProto)
- g.GenerateHeader()
- g.GenerateImports()
- for _, enum := range g.file.enum {
- g.GenerateEnum(enum)
- }
- for _, desc := range g.file.desc {
- g.GenerateMessage(desc)
- }
- for _, ext := range g.file.ext {
- g.GenerateExtension(ext)
- }
- g.GenerateInitFunction()
-}
-
-// Generate the header, including package definition and imports
-func (g *Generator) GenerateHeader() {
- g.p("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
- g.p("// DO NOT EDIT!")
- g.p()
- g.p("package ", g.file.packageName())
- g.p()
-}
-
-// Generate the header, including package definition and imports
-func (g *Generator) GenerateImports() {
- if g.file.needProtoImport() {
- g.p(`import "goprotobuf.googlecode.com/hg/proto"`)
- }
- for _, s := range g.file.Dependency {
- // Need to find the descriptor for this file
- for _, fd := range g.allFiles {
- if proto.GetString(fd.Name) == s {
- filename := GoName(s)
- if strings.HasSuffix(filename, ".go") {
- filename = filename[0:len(filename)-3]
- }
- g.p("import ", fd.packageName(), " ", Quote(filename))
- break
- }
- }
- }
- g.p()
-}
-
-// Generate the enum definitions for this EnumDescriptor.
-func (g *Generator) GenerateEnum(enum *EnumDescriptor) {
- // The full type name
- typeName := enum.typeName()
- // The full type name, CamelCased.
- ccTypeName := CamelCaseSlice(typeName)
- ccPrefix := enum.prefix()
- g.p("type ", ccTypeName, " int32")
- g.p("const (")
- g.in()
- for _, e := range enum.Value {
- g.p(ccPrefix+*e.Name, " = ", e.Number)
- }
- g.out()
- g.p(")")
- g.p("var ", ccTypeName, "_name = map[int32] string {")
- g.in()
- generated := make(map[int32] bool) // avoid duplicate values
- for _, e := range enum.Value {
- duplicate := ""
- if _, present := generated[*e.Number]; present {
- duplicate = "// Duplicate value: "
- }
- g.p(duplicate, e.Number, ": ", Quote(*e.Name), ",")
- generated[*e.Number] = true
- }
- g.out()
- g.p("}")
- g.p("var ", ccTypeName, "_value = map[string] int32 {")
- g.in()
- for _, e := range enum.Value {
- g.p(Quote(*e.Name), ": ", e.Number, ",")
- }
- g.out()
- g.p("}")
- g.p("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
- g.in()
- g.p("e := ", ccTypeName, "(x)")
- g.p("return &e")
- g.out()
- g.p("}")
- g.p()
-}
-
-// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
-// identifies details of the field for the protocol buffer marshaling and unmarshaling
-// code. The fields are:
-// wire encoding
-// protocol tag number
-// opt,req,rep for optional, required, or repeated
-// name= the original declared name
-// enum= the name of the enum type if it is an enum-typed field.
-// def= string representation of the default value, if any.
-// The default value must be in a representation that can be used at run-time
-// to generate the default value. Thus bools become 0 and 1, for instance.
-func (g *Generator) GoTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
- optrepreq := ""
- switch {
- case IsOptional(field):
- optrepreq = "opt"
- case IsRequired(field):
- optrepreq = "req"
- case IsRepeated(field):
- optrepreq = "rep"
- }
- defaultValue := proto.GetString(field.DefaultValue)
- if defaultValue != "" {
- switch *field.Type {
- case descriptor.FieldDescriptorProto_TYPE_BOOL:
- if defaultValue == "true" {
- defaultValue = "1"
- } else {
- defaultValue = "0"
- }
- case descriptor.FieldDescriptorProto_TYPE_STRING,
- descriptor.FieldDescriptorProto_TYPE_BYTES:
- // Protect frogs.
- defaultValue = Quote(defaultValue)
- // Don't need the quotes
- defaultValue = defaultValue[1 : len(defaultValue)-1]
- case descriptor.FieldDescriptorProto_TYPE_ENUM:
- // For enums we need to provide the integer constant.
- obj := g.objectNamed(proto.GetString(field.TypeName))
- enum, ok := obj.(*EnumDescriptor)
- if !ok {
- g.fail("enum type inconsistent for", CamelCaseSlice(obj.typeName()))
- }
- defaultValue = enum.integerValueAsString(defaultValue)
- }
- defaultValue = ",def=" + defaultValue
- }
- enum := ""
- if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
- obj := g.objectNamed(proto.GetString(field.TypeName))
- enum = ",enum=" + obj.packageName() + "." + CamelCaseSlice(obj.typeName())
- }
- name := proto.GetString(field.Name)
- if name == CamelCase(name) {
- name = ""
- } else {
- name = ",name=" + name
- }
- return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s)",
- wiretype,
- proto.GetInt32(field.Number),
- optrepreq,
- name,
- enum,
- defaultValue))
-}
-
-func NeedsStar(typ descriptor.FieldDescriptorProto_Type) bool {
- switch typ {
- case descriptor.FieldDescriptorProto_TYPE_GROUP:
- return false
- case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
- return false
- case descriptor.FieldDescriptorProto_TYPE_BYTES:
- return false
- }
- return true
-}
-
-// The type name appropriate for an item. If it's in the current file,
-// drop the package name and underscore the rest.
-// Otherwise it's from another package; use the underscored package name
-// followed by the field name. The result has an initial capital.
-func (g *Generator) TypeName(obj Object) string {
- return g.DefaultPackageName(obj) + CamelCaseSlice(obj.typeName())
-}
-
-// Like TypeName, but always includes the package name even if it's our own package.
-func (g *Generator) TypeNameWithPackage(obj Object) string {
- return obj.packageName() + CamelCaseSlice(obj.typeName())
-}
-
-// Returns a string representing the type name, and the wire type
-func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
- // TODO: Options.
- switch *field.Type {
- case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
- typ, wire = "float64", "fixed64"
- case descriptor.FieldDescriptorProto_TYPE_FLOAT:
- typ, wire = "float32", "fixed32"
- case descriptor.FieldDescriptorProto_TYPE_INT64:
- typ, wire = "int64", "varint"
- case descriptor.FieldDescriptorProto_TYPE_UINT64:
- typ, wire = "uint64", "varint"
- case descriptor.FieldDescriptorProto_TYPE_INT32:
- typ, wire = "int32", "varint"
- case descriptor.FieldDescriptorProto_TYPE_UINT32:
- typ, wire = "uint32", "varint"
- case descriptor.FieldDescriptorProto_TYPE_FIXED64:
- typ, wire = "uint64", "fixed64"
- case descriptor.FieldDescriptorProto_TYPE_FIXED32:
- typ, wire = "uint32", "fixed32"
- case descriptor.FieldDescriptorProto_TYPE_BOOL:
- typ, wire = "bool", "varint"
- case descriptor.FieldDescriptorProto_TYPE_STRING:
- typ, wire = "string", "bytes"
- case descriptor.FieldDescriptorProto_TYPE_GROUP:
- desc := g.objectNamed(proto.GetString(field.TypeName))
- typ, wire = "*"+g.TypeName(desc), "group"
- case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
- desc := g.objectNamed(proto.GetString(field.TypeName))
- typ, wire = "*"+g.TypeName(desc), "bytes"
- case descriptor.FieldDescriptorProto_TYPE_BYTES:
- typ, wire = "[]byte", "bytes"
- case descriptor.FieldDescriptorProto_TYPE_ENUM:
- desc := g.objectNamed(proto.GetString(field.TypeName))
- typ, wire = g.TypeName(desc), "varint"
- case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
- typ, wire = "int32", "fixed32"
- case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
- typ, wire = "int64", "fixed64"
- case descriptor.FieldDescriptorProto_TYPE_SINT32:
- typ, wire = "int32", "zigzag32"
- case descriptor.FieldDescriptorProto_TYPE_SINT64:
- typ, wire = "int64", "zigzag64"
- default:
- g.fail("unknown type for", proto.GetString(field.Name))
- }
- if IsRepeated(field) {
- typ = "[]" + typ
- } else if NeedsStar(*field.Type) {
- typ = "*" + typ
- }
- return
-}
-
-// Generate the type and default constant definitions for this Descriptor.
-func (g *Generator) GenerateMessage(message *Descriptor) {
- // The full type name
- typeName := message.typeName()
- // The full type name, CamelCased.
- ccTypeName := CamelCaseSlice(typeName)
-
- g.p("type ", ccTypeName, " struct {")
- g.in()
- for _, field := range message.Field {
- fieldname := CamelCase(*field.Name)
- typename, wiretype := g.GoType(message, field)
- tag := g.GoTag(field, wiretype)
- g.p(fieldname, "\t", typename, "\t", tag)
- }
- if len(message.ExtensionRange) > 0 {
- g.p("XXX_extensions\t\tmap[int32][]byte")
- }
- g.p("XXX_unrecognized\t[]byte")
- g.out()
- g.p("}")
-
- // Reset and New functions
- g.p("func (this *", ccTypeName, ") Reset() {")
- g.in()
- g.p("*this = ", ccTypeName, "{}")
- g.out()
- g.p("}")
- g.p("func New", ccTypeName, "() *", ccTypeName, " {")
- g.in()
- g.p("return new(", ccTypeName, ")")
- g.out()
- g.p("}")
-
- // Extension support methods
- if len(message.ExtensionRange) > 0 {
- g.p()
- g.p("var extRange_", ccTypeName, " = []proto.ExtensionRange{")
- g.in()
- for _, r := range message.ExtensionRange {
- end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
- g.p("proto.ExtensionRange{", r.Start, ", ", end, "},")
- }
- g.out()
- g.p("}")
- g.p("func (*", ccTypeName, ") ExtensionRangeArray() []proto.ExtensionRange {")
- g.in()
- g.p("return extRange_", ccTypeName)
- g.out()
- g.p("}")
- g.p("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
- g.in()
- g.p("if this.XXX_extensions == nil {")
- g.in()
- g.p("this.XXX_extensions = make(map[int32][]byte)")
- g.out()
- g.p("}")
- g.p("return this.XXX_extensions")
- g.out()
- g.p("}")
- }
-
- // Default constants
- for _, field := range message.Field {
- def := proto.GetString(field.DefaultValue)
- if def == "" {
- continue
- }
- fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
- typename, _ := g.GoType(message, field)
- if typename[0] == '*' {
- typename = typename[1:]
- }
- kind := "const "
- switch {
- case typename == "bool":
- case typename == "string":
- def = Quote(def)
- case typename == "[]byte":
- def = "[]byte(" + Quote(def) + ")"
- kind = "var "
- case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
- // Must be an enum. Need to construct the prefixed name.
- obj := g.objectNamed(proto.GetString(field.TypeName))
- enum, ok := obj.(*EnumDescriptor)
- if !ok {
- log.Stderr("don't know how to generate constant for", fieldname)
- continue
- }
- def = enum.prefix() + def
- }
- g.p(kind, fieldname, " ", typename, " = ", def)
- }
- g.p()
-
- for _, ext := range message.ext {
- g.GenerateExtension(ext)
- }
-}
-
-// Generate the extension descriptor for this ExtensionDescriptor.
-func (g *Generator) GenerateExtension(ext *ExtensionDescriptor) {
- // The full type name
- typeName := ext.typeName()
- // Each scope of the extension is individually CamelCased, and all are joined with "_" with a "E_" prefix.
- for i, s := range typeName {
- typeName[i] = CamelCase(s)
- }
- ccTypeName := "E_" + strings.Join(typeName, "_")
-
- extendedType := "*" + g.TypeName(g.objectNamed(*ext.Extendee))
- field := ext.FieldDescriptorProto
- fieldType, wireType := g.GoType(ext.parent, field)
- tag := g.GoTag(field, wireType)
-
- g.p("var ", ccTypeName, " = &proto.ExtensionDesc{")
- g.in()
- g.p("ExtendedType: (", extendedType, ")(nil),")
- g.p("ExtensionType: (", fieldType, ")(nil),")
- g.p("Field: ", field.Number, ",")
- g.p("Tag: ", tag, ",")
-
- g.out()
- g.p("}")
- g.p()
-}
-
-func (g *Generator) GenerateInitFunction() {
- g.p("func init() {")
- g.in()
- for _, enum := range g.file.enum {
- g.GenerateEnumRegistration(enum)
- }
- g.out()
- g.p("}")
-}
-
-func (g *Generator) GenerateEnumRegistration(enum *EnumDescriptor) {
- pkg := g.packageName + "." // We always print the full package name here.
- // The full type name
- typeName := enum.typeName()
- // The full type name, CamelCased.
- ccTypeName := CamelCaseSlice(typeName)
- g.p("proto.RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
-}
-
-// And now lots of helper functions.
-
-// Return change foo_bar_Baz to FooBar_Baz.
-func CamelCase(name string) string {
- elems := strings.Split(name, "_", 0)
- for i, e := range elems {
- if e == "" {
- elems[i] = "_"
- continue
- }
- runes := []int(e)
- if unicode.IsLower(runes[0]) {
- runes[0] = unicode.ToUpper(runes[0])
- elems[i] = string(runes)
- } else {
- if i > 0 {
- elems[i] = "_" + e
- }
- }
- }
- s := strings.Join(elems, "")
- // Name must not begin with an underscore.
- if len(s) > 0 && s[0] == '_' {
- s = "X" + s[1:]
- }
- return s
-}
-
-// Like CamelCase, but the argument is a slice of strings to
-// be concatenated with "_"
-func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
-
-// Turn a sliced name into a dotted name
-func DottedSlice(elem []string) string { return strings.Join(elem, ".") }
-
-// Return a Go-source quoted string representation of s.
-func Quote(s string) string { return fmt.Sprintf("%q", s) }
-
-// Given a .proto file name, return the output name for the generated Go program.
-func GoName(name string) string {
- if strings.HasSuffix(name, ".proto") {
- name = name[0 : len(name)-6]
- }
- return name + ".pb.go"
-}
-
-// Is this field optional?
-func IsOptional(field *descriptor.FieldDescriptorProto) bool {
- return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
-}
-
-// Is this field required?
-func IsRequired(field *descriptor.FieldDescriptorProto) bool {
- return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
-}
-
-// Is this field repeated?
-func IsRepeated(field *descriptor.FieldDescriptorProto) bool {
- return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
-}
-
-// Mapping function used to generate Go names from package names, which can be dotted.
-func DotToUnderscore(rune int) int {
- if rune == '.' {
- return '_'
- }
- return rune
-}
diff --git a/compiler/plugin/Makefile b/compiler/plugin/Makefile
index 08be77f..d2db4a4 100644
--- a/compiler/plugin/Makefile
+++ b/compiler/plugin/Makefile
@@ -35,6 +35,8 @@
GOFILES=\
plugin.pb.go\
+DEPS=../../proto ../descriptor
+
include $(GOROOT)/src/Make.pkg
# Not stored here, but plugin.proto is in http://code.google.com/p/protobuf