The protogen package provides high-level support for writing protoc plugins that generate Go code from .proto files. It handles the protocol buffer compiler plugin protocol and provides structured access to file descriptors, messages, fields, and other protobuf elements.
import (
"google.golang.org/protobuf/compiler/protogen"
"google.golang.org/protobuf/types/pluginpb"
"google.golang.org/protobuf/types/descriptorpb"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/gofeaturespb"
)Configure and run a protoc plugin.
// Options configures a code generation request
type Options struct {
// ParamFunc is called with each unknown generator parameter
// Plugins can accept parameters from the command line via --<lang>_out
// Parameters are passed as key=value pairs
//
// The (flag.FlagSet).Set method matches this function signature
ParamFunc func(name, value string) error
// ImportRewriteFunc is called with the import path of each package
// imported by a generated file. It returns the import path to use
ImportRewriteFunc func(GoImportPath) GoImportPath
// DefaultAPILevel overrides which API to generate by default
// One of OPEN, HYBRID or OPAQUE
DefaultAPILevel gofeaturespb.GoFeatures_APILevel
// InternalStripForEditionsDiff is for internal use by Go Protobuf only
InternalStripForEditionsDiff *bool
}
// New returns a new Plugin from a CodeGeneratorRequest
func (opts Options) New(req *pluginpb.CodeGeneratorRequest) (*Plugin, error)
// Run executes a function as a protoc plugin
// It reads a pluginpb.CodeGeneratorRequest message from os.Stdin,
// invokes the plugin function, and writes a pluginpb.CodeGeneratorResponse
// message to os.Stdout
func (opts Options) Run(f func(*Plugin) error)Usage example:
import (
"flag"
"google.golang.org/protobuf/compiler/protogen"
)
func main() {
var flags flag.FlagSet
myFlag := flags.Bool("my_option", false, "Enable my option")
protogen.Options{
ParamFunc: flags.Set,
}.Run(func(gen *protogen.Plugin) error {
if *myFlag {
// Custom behavior based on flag
}
for _, f := range gen.Files {
if !f.Generate {
continue
}
generateFile(gen, f)
}
return nil
})
}The main context for a protoc plugin invocation.
// Plugin is a protoc plugin invocation
type Plugin struct {
// Request is the CodeGeneratorRequest provided by protoc
Request *pluginpb.CodeGeneratorRequest
// Files is the set of files to generate and everything they import
// Files appear in topological order, so each file appears before any
// file that imports it
Files []*File
FilesByPath map[string]*File
// SupportedFeatures is the set of protobuf language features supported by
// this generator plugin
SupportedFeatures uint64
SupportedEditionsMinimum descriptorpb.Edition
SupportedEditionsMaximum descriptorpb.Edition
// Has unexported fields.
}
// NewGeneratedFile creates a new generated file with the given filename and
// import path
func (gen *Plugin) NewGeneratedFile(filename string, goImportPath GoImportPath) *GeneratedFile
// Error records an error in code generation
// The generator will report the error back to protoc and will not produce output
func (gen *Plugin) Error(err error)
// Response returns the generator output
func (gen *Plugin) Response() *pluginpb.CodeGeneratorResponse
// InternalStripForEditionsDiff is for internal use by Go Protobuf only
func (gen *Plugin) InternalStripForEditionsDiff() boolRepresents a single generated Go source file.
// GeneratedFile is a generated file
type GeneratedFile struct {
// Has unexported fields.
}
// P prints a line to the generated output
// It converts each parameter to a string following the same rules as fmt.Print
// It never inserts spaces between parameters
func (g *GeneratedFile) P(v ...any)
// Write implements io.Writer
func (g *GeneratedFile) Write(p []byte) (n int, err error)
// Import ensures a package is imported by the generated file
// Packages referenced by GeneratedFile.QualifiedGoIdent are automatically imported
// Explicitly importing with Import is generally only necessary for blank imports
func (g *GeneratedFile) Import(importPath GoImportPath)
// QualifiedGoIdent returns the string to use for a Go identifier
// If the identifier is from a different Go package than the generated file,
// the returned name will be qualified (package.name) and an import statement
// will be included in the file
func (g *GeneratedFile) QualifiedGoIdent(ident GoIdent) string
// AnnotateSymbol associates a symbol in a generated Go file with a location in
// a source .proto file and a semantic type
// The symbol may refer to a type, constant, variable, function, method, or struct field
// The "T.sel" syntax is used to identify the method or field 'sel' on type 'T'
func (g *GeneratedFile) AnnotateSymbol(symbol string, info Annotation)
// Annotate associates a symbol in a generated Go file with a location in
// a source .proto file
// Deprecated: Use AnnotateSymbol instead
func (g *GeneratedFile) Annotate(symbol string, loc Location)
// Content returns the contents of the generated file
func (g *GeneratedFile) Content() ([]byte, error)
// Skip removes the generated file from the plugin output
func (g *GeneratedFile) Skip()
// Unskip reverts a previous call to Skip, re-including the generated file
func (g *GeneratedFile) Unskip()
// InternalStripForEditionsDiff is for internal use by Go Protobuf only
func (g *GeneratedFile) InternalStripForEditionsDiff() boolUsage example:
func generateFile(gen *protogen.Plugin, file *protogen.File) {
filename := file.GeneratedFilenamePrefix + ".myext.go"
g := gen.NewGeneratedFile(filename, file.GoImportPath)
// Generate package declaration
g.P("package ", file.GoPackageName)
g.P()
// Generate imports and code
g.P("import (")
g.P(" ", g.QualifiedGoIdent(protogen.GoIdent{
GoImportPath: "context",
GoName: "",
}))
g.P(")")
g.P()
// Generate type declarations
for _, message := range file.Messages {
generateMessage(g, message)
}
}Represents a .proto source file.
// File describes a .proto source file
type File struct {
Desc protoreflect.FileDescriptor
Proto *descriptorpb.FileDescriptorProto
GoDescriptorIdent GoIdent // name of Go variable for the file descriptor
GoPackageName GoPackageName // name of this file's Go package
GoImportPath GoImportPath // import path of this file's Go package
Enums []*Enum // top-level enum declarations
Messages []*Message // top-level message declarations
Extensions []*Extension // top-level extension declarations
Services []*Service // top-level service declarations
Generate bool // true if we should generate code for this file
// GeneratedFilenamePrefix is used to construct filenames for generated files
// For example, "dir/foo.proto" might have a prefix of "dir/foo"
GeneratedFilenamePrefix string
// APILevel specifies which API to generate. One of OPEN, HYBRID or OPAQUE
APILevel gofeaturespb.GoFeatures_APILevel
// Has unexported fields.
}Represents a protocol buffer message type.
// Message describes a message
type Message struct {
Desc protoreflect.MessageDescriptor
GoIdent GoIdent // name of the generated Go type
Fields []*Field // message field declarations
Oneofs []*Oneof // message oneof declarations
Enums []*Enum // nested enum declarations
Messages []*Message // nested message declarations
Extensions []*Extension // nested extension declarations
Location Location // location of this message
Comments CommentSet // comments associated with this message
// APILevel specifies which API to generate. One of OPEN, HYBRID or OPAQUE
APILevel gofeaturespb.GoFeatures_APILevel
}Represents a message field.
// Field describes a message field
type Field struct {
Desc protoreflect.FieldDescriptor
// GoName is the base name of this field's Go field and methods
// For code generated by protoc-gen-go, this means a field named
// '{{GoName}}' and a getter method named 'Get{{GoName}}'
GoName string // e.g., "FieldName"
// GoIdent is the base name of a top-level declaration for this field
// For code generated by protoc-gen-go, this means a wrapper type named
// '{{GoIdent}}' for members fields of a oneof, and a variable named
// 'E_{{GoIdent}}' for extension fields
GoIdent GoIdent // e.g., "MessageName_FieldName"
Parent *Message // message in which this field is declared; nil if top-level extension
Oneof *Oneof // containing oneof; nil if not part of a oneof
Extendee *Message // extended message for extension fields; nil otherwise
Enum *Enum // type for enum fields; nil otherwise
Message *Message // type for message or group fields; nil otherwise
Location Location // location of this field
Comments CommentSet // comments associated with this field
// Has unexported fields.
}
// Extension is an alias of Field for documentation
type Extension = Field
// MethodName returns the (possibly mangled) name of the generated accessor method,
// along with the backwards-compatible name (if needed)
// method must be one of Get, Set, Has, Clear. MethodName panics otherwise
func (field *Field) MethodName(method string) (name, compat string)
// BuilderFieldName returns the name of this field in the corresponding _builder struct
func (field *Field) BuilderFieldName() stringRepresents an enum type.
// Enum describes an enum
type Enum struct {
Desc protoreflect.EnumDescriptor
GoIdent GoIdent // name of the generated Go type
Values []*EnumValue // enum value declarations
Location Location // location of this enum
Comments CommentSet // comments associated with this enum
}
// EnumValue describes an enum value
type EnumValue struct {
Desc protoreflect.EnumValueDescriptor
GoIdent GoIdent // name of the generated Go declaration
// PrefixedAlias is usually empty, except when the strip_enum_prefix feature
// was set to GENERATE_BOTH, in which case PrefixedAlias holds the old name
// which should be generated as an alias for compatibility
PrefixedAlias GoIdent
Parent *Enum // enum in which this value is declared
Location Location // location of this enum value
Comments CommentSet // comments associated with this enum value
}Represents a oneof field group.
// Oneof describes a message oneof
type Oneof struct {
Desc protoreflect.OneofDescriptor
// GoName is the base name of this oneof's Go field and methods
// For code generated by protoc-gen-go, this means a field named
// '{{GoName}}' and a getter method named 'Get{{GoName}}'
GoName string // e.g., "OneofName"
// GoIdent is the base name of a top-level declaration for this oneof
GoIdent GoIdent // e.g., "MessageName_OneofName"
Parent *Message // message in which this oneof is declared
Fields []*Field // fields that are part of this oneof
Location Location // location of this oneof
Comments CommentSet // comments associated with this oneof
// Has unexported fields.
}
// MethodName returns the (possibly mangled) name of the generated accessor method
// method must be one of Has, Clear, Which. MethodName panics otherwise
func (oneof *Oneof) MethodName(method string) stringRepresents a gRPC service.
// Service describes a service
type Service struct {
Desc protoreflect.ServiceDescriptor
GoName string
Methods []*Method // service method declarations
Location Location // location of this service
Comments CommentSet // comments associated with this service
}
// Method describes a method in a service
type Method struct {
Desc protoreflect.MethodDescriptor
GoName string
Parent *Service // service in which this method is declared
Input *Message
Output *Message
Location Location // location of this method
Comments CommentSet // comments associated with this method
}Types for representing Go identifiers and import paths.
// GoIdent is a Go identifier, consisting of a name and import path
// The name is a single identifier and may not be a dot-qualified selector
type GoIdent struct {
GoName string
GoImportPath GoImportPath
}
func (id GoIdent) String() string
// GoImportPath is the import path of a Go package
// For example: "google.golang.org/protobuf/compiler/protogen"
type GoImportPath string
// Ident returns a GoIdent with s as the GoName and p as the GoImportPath
func (p GoImportPath) Ident(s string) GoIdent
func (p GoImportPath) String() string
// GoPackageName is the name of a Go package, e.g., "protobuf"
type GoPackageName stringRepresents a location in a .proto source file.
// Location is a location in a .proto source file
type Location struct {
SourceFile string
Path protoreflect.SourcePath
}Provides semantic detail for generated elements.
// Annotation provides semantic detail for a generated proto element
type Annotation struct {
// Location is the source .proto file for the element
Location Location
// Semantic is the symbol's effect on the element in the original .proto file
Semantic *descriptorpb.GeneratedCodeInfo_Annotation_Semantic
}Documentation comments from .proto files.
// Comments is a comments string as provided by protoc
type Comments string
// String formats the comments by inserting // to the start of each line,
// ensuring that there is a trailing newline
// An empty comment is formatted as an empty string
func (c Comments) String() string
// CommentSet is a set of leading and trailing comments associated with a
// .proto descriptor declaration
type CommentSet struct {
LeadingDetached []Comments
Leading Comments
Trailing Comments
}package main
import (
"fmt"
"google.golang.org/protobuf/compiler/protogen"
)
func main() {
protogen.Options{}.Run(func(gen *protogen.Plugin) error {
// Set supported features
gen.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)
// Process each file marked for generation
for _, f := range gen.Files {
if !f.Generate {
continue
}
generateFile(gen, f)
}
return nil
})
}
func generateFile(gen *protogen.Plugin, file *protogen.File) {
filename := file.GeneratedFilenamePrefix + ".myext.go"
g := gen.NewGeneratedFile(filename, file.GoImportPath)
// Generate header
g.P("// Code generated by protoc-gen-myext. DO NOT EDIT.")
g.P()
g.P("package ", file.GoPackageName)
g.P()
// Generate for each message
for _, message := range file.Messages {
generateMessage(g, message)
}
}
func generateMessage(g *protogen.GeneratedFile, message *protogen.Message) {
// Generate comments
g.P(message.Comments.Leading.String())
g.P("type ", message.GoIdent, "Wrapper struct {")
g.P(" Inner *", g.QualifiedGoIdent(message.GoIdent))
g.P("}")
g.P()
// Generate method
g.P("func (w *", message.GoIdent, "Wrapper) Validate() error {")
g.P(" // Add validation logic here")
g.P(" return nil")
g.P("}")
g.P()
// Annotate symbol for IDE integration
g.AnnotateSymbol(message.GoIdent.GoName+"Wrapper", protogen.Annotation{
Location: message.Location,
})
// Recursively process nested messages
for _, nested := range message.Messages {
generateMessage(g, nested)
}
}This comprehensive API enables building sophisticated protoc plugins that generate Go code from protocol buffer definitions with full access to type information, comments, and source locations.