Sync from internal version.
This is the support for the "public import" feature.
R=r
CC=golang-dev
http://codereview.appspot.com/3975043
diff --git a/compiler/generator/generator.go b/compiler/generator/generator.go
index fa67d86..079fe2a 100644
--- a/compiler/generator/generator.go
+++ b/compiler/generator/generator.go
@@ -200,6 +200,10 @@
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 full list of symbols that are exported.
+ // This is used for supporting public imports.
+ exported []Symbol
}
// PackageName is the package name we'll use in the generated code to refer to this file.
@@ -217,6 +221,60 @@
return BaseName(proto.GetString(d.Name))
}
+func (d *FileDescriptor) addExport(symbol Symbol) {
+ d.exported = append(d.exported, symbol)
+}
+
+// Symbol is an interface representing an exported Go symbol.
+type Symbol interface {
+ // GenerateAlias should generate an appropriate alias
+ // for the symbol from the named package.
+ GenerateAlias(g *Generator, pkg string)
+}
+
+type messageSymbol struct {
+ sym string
+ hasExtensions, isMessageSet bool
+}
+
+func (ms messageSymbol) GenerateAlias(g *Generator, pkg string) {
+ remoteSym := pkg + "." + ms.sym
+
+ g.P("type ", ms.sym, " ", remoteSym)
+ g.P("func (this *", ms.sym, ") Reset() { (*", remoteSym, ")(this).Reset() }")
+ if ms.hasExtensions {
+ g.P("func (*", ms.sym, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange ",
+ "{ return (*", remoteSym, ")(nil).ExtensionRangeArray() }")
+ g.P("func (this *", ms.sym, ") ExtensionMap() map[int32][]byte ",
+ "{ return (*", remoteSym, ")(this).ExtensionMap() }")
+ if ms.isMessageSet {
+ g.P("func (this *", ms.sym, ") Marshal() ([]byte, os.Error) ",
+ "{ return (*", remoteSym, ")(this).Marshal() }")
+ g.P("func (this *", ms.sym, ") Unmarshal(buf []byte) os.Error ",
+ "{ return (*", remoteSym, ")(this).Unmarshal(buf) }")
+ }
+ }
+}
+
+type enumSymbol string
+
+func (es enumSymbol) GenerateAlias(g *Generator, pkg string) {
+ s := string(es)
+ g.P("type ", s, " ", pkg, ".", s)
+ g.P("var ", s, "_name = ", pkg, ".", s, "_name")
+ g.P("var ", s, "_value = ", pkg, ".", s, "_value")
+ g.P("func New", s, "(x int32) *", s, " { e := ", s, "(x); return &e }")
+}
+
+type constOrVarSymbol struct {
+ sym string
+ typ string // either "const" or "var"
+}
+
+func (cs constOrVarSymbol) GenerateAlias(g *Generator, pkg string) {
+ g.P(cs.typ, " ", cs.sym, " = ", pkg, ".", cs.sym)
+}
+
// 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.
@@ -560,13 +618,24 @@
for _, p := range plugins {
p.Init(g)
}
- // Generate the output.
- for i, file := range g.genFiles {
+ // Generate the output. The generator runs for every file, even the files
+ // that we don't generate output for, so that we can collate the full list
+ // of exported symbols to support public imports.
+ genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles))
+ for _, file := range g.genFiles {
+ genFileMap[file] = true
+ }
+ i := 0
+ for _, file := range g.allFiles {
g.Reset()
g.generate(file)
+ if _, ok := genFileMap[file]; !ok {
+ continue
+ }
g.Response.File[i] = new(plugin.CodeGeneratorResponse_File)
g.Response.File[i].Name = proto.String(goFileName(*file.Name))
g.Response.File[i].Content = proto.String(g.String())
+ i++
}
}
@@ -626,6 +695,15 @@
g.P()
}
+func (g *Generator) fileByName(filename string) *FileDescriptor {
+ for _, fd := range g.allFiles {
+ if proto.GetString(fd.Name) == filename {
+ return fd
+ }
+ }
+ return nil
+}
+
// Generate the header, including package definition and imports
func (g *Generator) generateImports() {
// We almost always need a proto import. Rather than computing when we
@@ -635,28 +713,23 @@
g.P(`import "math"`)
g.P(`import "os"`)
for _, s := range g.file.Dependency {
- // Need to find the descriptor for this file
- for _, fd := range g.allFiles {
- // Do not import our own package.
- if fd.PackageName() == g.packageName {
- continue
- }
- if proto.GetString(fd.Name) == s {
- filename := goFileName(s)
- if substitution, ok := g.ImportMap[s]; ok {
- filename = substitution
- }
- filename = g.ImportPrefix + filename
- if strings.HasSuffix(filename, ".go") {
- filename = filename[0 : len(filename)-3]
- }
- if _, ok := g.usedPackages[fd.PackageName()]; ok {
- g.P("import ", fd.PackageName(), " ", Quote(filename))
- } else {
- log.Println("protoc-gen-go: discarding unused import:", filename)
- }
- break
- }
+ fd := g.fileByName(s)
+ // Do not import our own package.
+ if fd.PackageName() == g.packageName {
+ continue
+ }
+ filename := goFileName(s)
+ if substitution, ok := g.ImportMap[s]; ok {
+ filename = substitution
+ }
+ filename = g.ImportPrefix + filename
+ if strings.HasSuffix(filename, ".go") {
+ filename = filename[0 : len(filename)-3]
+ }
+ if _, ok := g.usedPackages[fd.PackageName()]; ok {
+ g.P("import ", fd.PackageName(), " ", Quote(filename))
+ } else {
+ log.Println("protoc-gen-go: discarding unused import:", filename)
}
}
g.P()
@@ -670,6 +743,16 @@
g.P("var _ = math.Inf")
g.P("var _ os.Error")
g.P()
+
+ // Symbols from public imports.
+ for _, index := range g.file.PublicDependency {
+ fd := g.fileByName(g.file.Dependency[index])
+ g.P("// Types from public import ", *fd.Name)
+ for _, sym := range fd.exported {
+ sym.GenerateAlias(g, fd.PackageName())
+ }
+ }
+ g.P()
}
// Generate the enum definitions for this EnumDescriptor.
@@ -680,10 +763,13 @@
ccTypeName := CamelCaseSlice(typeName)
ccPrefix := enum.prefix()
g.P("type ", ccTypeName, " int32")
+ g.file.addExport(enumSymbol(ccTypeName))
g.P("const (")
g.In()
for _, e := range enum.Value {
- g.P(ccPrefix+*e.Name, " = ", e.Number)
+ name := ccPrefix + *e.Name
+ g.P(name, " = ", e.Number)
+ g.file.addExport(constOrVarSymbol{name, "const"})
}
g.Out()
g.P(")")
@@ -907,9 +993,12 @@
g.P("}")
// Extension support methods
+ var hasExtensions, isMessageSet bool
if len(message.ExtensionRange) > 0 {
+ hasExtensions = true
// message_set_wire_format only makes sense when extensions are defined.
if opts := message.Options; opts != nil && proto.GetBool(opts.MessageSetWireFormat) {
+ isMessageSet = true
g.P()
g.P("func (this *", ccTypeName, ") Marshal() ([]byte, os.Error) {")
g.In()
@@ -952,6 +1041,8 @@
g.P("}")
}
+ g.file.addExport(messageSymbol{ccTypeName, hasExtensions, isMessageSet})
+
// Default constants
for _, field := range message.Field {
def := proto.GetString(field.DefaultValue)
@@ -996,6 +1087,7 @@
def = g.DefaultPackageName(enum) + enum.prefix() + def
}
g.P(kind, fieldname, " ", typename, " = ", def)
+ g.file.addExport(constOrVarSymbol{fieldname, kind})
}
g.P()
@@ -1029,6 +1121,8 @@
g.Out()
g.P("}")
g.P()
+
+ g.file.addExport(constOrVarSymbol{ccTypeName, "var"})
}
func (g *Generator) generateInitFunction() {
diff --git a/compiler/testdata/imp.proto b/compiler/testdata/imp.proto
index beb6805..6bf2a13 100644
--- a/compiler/testdata/imp.proto
+++ b/compiler/testdata/imp.proto
@@ -38,4 +38,11 @@
DAVE = 1;
MIKE = 2;
}
+
+ extensions 90 to 100;
+}
+
+message ImportedExtendable {
+ option message_set_wire_format = true;
+ extensions 100 to max;
}
diff --git a/compiler/testdata/test.pb.go.golden b/compiler/testdata/test.pb.go.golden
index 6a54d22..4d364e7 100644
--- a/compiler/testdata/test.pb.go.golden
+++ b/compiler/testdata/test.pb.go.golden
@@ -13,6 +13,7 @@
var _ = math.Inf
var _ os.Error
+
type HatType int32
const (
HatType_FEDORA = 1
diff --git a/proto/testdata/test.pb.go b/proto/testdata/test.pb.go
index eaed281..fa7a666 100644
--- a/proto/testdata/test.pb.go
+++ b/proto/testdata/test.pb.go
@@ -12,6 +12,7 @@
var _ = math.Inf
var _ os.Error
+
type FOO int32
const (
FOO_FOO1 = 1