1 // Copyright (c) 2020 Cisco and/or its affiliates.
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at:
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
22 "git.fd.io/govpp.git/binapigen/vppapi"
25 // generatedCodeVersion indicates a version of the generated code.
26 // It is incremented whenever an incompatibility between the generated code and
27 // GoVPP api package is introduced; the generated code references
28 // a constant, api.GoVppAPIPackageIsVersionN (where N is generatedCodeVersion).
29 const generatedCodeVersion = 2
33 optFileVersion = "version"
41 PackageName GoPackageName
42 GoImportPath GoImportPath
56 func newFile(gen *Generator, apifile *vppapi.File, packageName GoPackageName, importPath GoImportPath) (*File, error) {
59 PackageName: packageName,
60 GoImportPath: importPath,
62 if apifile.Options != nil {
63 file.Version = apifile.Options[optFileVersion]
66 file.FilenamePrefix = path.Join(gen.opts.OutputDir, file.Desc.Name)
68 for _, imp := range apifile.Imports {
69 file.Imports = append(file.Imports, normalizeImport(imp))
72 for _, enumType := range apifile.EnumTypes {
73 file.Enums = append(file.Enums, newEnum(gen, file, enumType))
75 for _, aliasType := range apifile.AliasTypes {
76 file.Aliases = append(file.Aliases, newAlias(gen, file, aliasType))
78 for _, structType := range apifile.StructTypes {
79 file.Structs = append(file.Structs, newStruct(gen, file, structType))
81 for _, unionType := range apifile.UnionTypes {
82 file.Unions = append(file.Unions, newUnion(gen, file, unionType))
85 for _, msg := range apifile.Messages {
86 file.Messages = append(file.Messages, newMessage(gen, file, msg))
88 if apifile.Service != nil {
89 file.Service = newService(gen, file, *apifile.Service)
92 for _, t := range file.Aliases {
93 if err := t.resolveDependencies(gen); err != nil {
97 for _, t := range file.Structs {
98 if err := t.resolveDependencies(gen); err != nil {
102 for _, t := range file.Unions {
103 if err := t.resolveDependencies(gen); err != nil {
107 for _, m := range file.Messages {
108 if err := m.resolveDependencies(gen); err != nil {
112 if file.Service != nil {
113 for _, rpc := range file.Service.RPCs {
114 if err := rpc.resolveMessages(gen); err != nil {
123 func (file *File) isTypesFile() bool {
124 return strings.HasSuffix(file.Desc.Name, "_types")
127 func (file *File) hasService() bool {
128 return file.Service != nil && len(file.Service.RPCs) > 0
131 func (file *File) importedFiles(gen *Generator) []*File {
133 for _, imp := range file.Imports {
134 impFile, ok := gen.FilesByName[imp]
136 logf("file %s import %s not found API files", file.Desc.Name, imp)
139 files = append(files, impFile)
144 func (file *File) dependsOnFile(gen *Generator, dep string) bool {
145 for _, imp := range file.Imports {
149 impFile, ok := gen.FilesByName[imp]
150 if ok && impFile.dependsOnFile(gen, dep) {
157 func normalizeImport(imp string) string {
159 if idx := strings.Index(imp, "."); idx >= 0 {
166 enumFlagSuffix = "_flags"
169 func isEnumFlag(enum *Enum) bool {
170 return strings.HasSuffix(enum.Name, enumFlagSuffix)
179 func newEnum(gen *Generator, file *File, apitype vppapi.EnumType) *Enum {
183 GoName: camelCaseName(apitype.Name),
184 GoImportPath: file.GoImportPath,
187 gen.enumsByName[typ.Name] = typ
201 func newAlias(gen *Generator, file *File, apitype vppapi.AliasType) *Alias {
205 GoName: camelCaseName(apitype.Name),
206 GoImportPath: file.GoImportPath,
209 gen.aliasesByName[typ.Name] = typ
213 func (a *Alias) resolveDependencies(gen *Generator) error {
214 if err := a.resolveType(gen); err != nil {
215 return fmt.Errorf("unable to resolve field: %w", err)
220 func (a *Alias) resolveType(gen *Generator) error {
221 if _, ok := BaseTypesGo[a.Type]; ok {
224 typ := fromApiType(a.Type)
225 if t, ok := gen.structsByName[typ]; ok {
229 if t, ok := gen.unionsByName[typ]; ok {
233 return fmt.Errorf("unknown type: %q", a.Type)
244 func newStruct(gen *Generator, file *File, apitype vppapi.StructType) *Struct {
248 GoName: camelCaseName(apitype.Name),
249 GoImportPath: file.GoImportPath,
252 gen.structsByName[typ.Name] = typ
253 for _, fieldType := range apitype.Fields {
254 field := newField(gen, file, fieldType)
255 field.ParentStruct = typ
256 typ.Fields = append(typ.Fields, field)
261 func (m *Struct) resolveDependencies(gen *Generator) (err error) {
262 for _, field := range m.Fields {
263 if err := field.resolveDependencies(gen); err != nil {
264 return fmt.Errorf("unable to resolve for struct %s: %w", m.Name, err)
278 func newUnion(gen *Generator, file *File, apitype vppapi.UnionType) *Union {
282 GoName: camelCaseName(apitype.Name),
283 GoImportPath: file.GoImportPath,
286 gen.unionsByName[typ.Name] = typ
287 for _, fieldType := range apitype.Fields {
288 field := newField(gen, file, fieldType)
289 field.ParentUnion = typ
290 typ.Fields = append(typ.Fields, field)
295 func (m *Union) resolveDependencies(gen *Generator) (err error) {
296 for _, field := range m.Fields {
297 if err := field.resolveDependencies(gen); err != nil {
304 // msgType determines message header fields
308 msgTypeBase msgType = iota // msg_id
309 msgTypeRequest // msg_id, client_index, context
310 msgTypeReply // msg_id, context
311 msgTypeEvent // msg_id, client_index
314 func apiMsgType(t msgType) GoIdent {
317 return govppApiPkg.Ident("RequestMessage")
319 return govppApiPkg.Ident("ReplyMessage")
321 return govppApiPkg.Ident("EventMessage")
323 return govppApiPkg.Ident("OtherMessage")
329 fieldMsgID = "_vl_msg_id"
330 fieldClientIndex = "client_index"
331 fieldContext = "context"
332 fieldRetval = "retval"
337 optFieldDefault = "default"
340 type Message struct {
352 func newMessage(gen *Generator, file *File, apitype vppapi.Message) *Message {
355 CRC: strings.TrimPrefix(apitype.CRC, "0x"),
356 GoIdent: newGoIdent(file, apitype.Name),
358 gen.messagesByName[apitype.Name] = msg
360 for _, fieldType := range apitype.Fields {
361 // skip internal fields
362 switch strings.ToLower(fieldType.Name) {
365 case fieldClientIndex, fieldContext:
371 field := newField(gen, file, fieldType)
372 field.ParentMessage = msg
373 msg.Fields = append(msg.Fields, field)
378 func (m *Message) resolveDependencies(gen *Generator) (err error) {
379 if m.msgType, err = getMsgType(m.Message); err != nil {
382 for _, field := range m.Fields {
383 if err := field.resolveDependencies(gen); err != nil {
390 func getMsgType(m vppapi.Message) (msgType, error) {
391 if len(m.Fields) == 0 {
392 return msgType(0), fmt.Errorf("message %s has no fields", m.Name)
395 wasClientIndex := false
396 for i, field := range m.Fields {
398 if field.Name != fieldMsgID {
399 return msgType(0), fmt.Errorf("message %s is missing ID field", m.Name)
402 if field.Name == fieldClientIndex {
403 // "client_index" as the second member,
404 // this might be an event message or a request
406 wasClientIndex = true
407 } else if field.Name == fieldContext {
408 // reply needs "context" as the second member
412 if wasClientIndex && field.Name == fieldContext {
413 // request needs "client_index" as the second member
414 // and "context" as the third member
427 DefaultValue interface{}
429 // Reference to actual type of this field
435 // Parent in which this field is declared
436 ParentMessage *Message
440 // Field reference for fields determining size
445 func newField(gen *Generator, file *File, apitype vppapi.Field) *Field {
448 GoName: camelCaseName(apitype.Name),
450 if apitype.Meta != nil {
451 if val, ok := apitype.Meta[optFieldDefault]; ok {
452 typ.DefaultValue = val
458 func (f *Field) resolveDependencies(gen *Generator) error {
459 if err := f.resolveType(gen); err != nil {
460 return fmt.Errorf("unable to resolve field type: %w", err)
462 if err := f.resolveFields(gen); err != nil {
463 return fmt.Errorf("unable to resolve fields: %w", err)
468 func (f *Field) resolveType(gen *Generator) error {
469 if _, ok := BaseTypesGo[f.Type]; ok {
472 typ := fromApiType(f.Type)
473 if t, ok := gen.structsByName[typ]; ok {
477 if t, ok := gen.enumsByName[typ]; ok {
481 if t, ok := gen.aliasesByName[typ]; ok {
485 if t, ok := gen.unionsByName[typ]; ok {
489 return fmt.Errorf("unknown type: %q", f.Type)
492 func (f *Field) resolveFields(gen *Generator) error {
494 if f.ParentMessage != nil {
495 fields = f.ParentMessage.Fields
496 } else if f.ParentStruct != nil {
497 fields = f.ParentStruct.Fields
499 if f.SizeFrom != "" {
500 for _, field := range fields {
501 if field.Name == f.SizeFrom {
502 f.FieldSizeFrom = field
507 for _, field := range fields {
508 if field.SizeFrom == f.Name {
509 f.FieldSizeOf = field
517 type Service struct {
523 func newService(gen *Generator, file *File, apitype vppapi.Service) *Service {
527 for _, rpc := range apitype.RPCs {
528 svc.RPCs = append(svc.RPCs, newRpc(file, svc, rpc))
534 serviceNoReply = "null"
549 func newRpc(file *File, service *Service, apitype vppapi.RPC) *RPC {
552 GoName: camelCaseName(apitype.Request),
558 func (rpc *RPC) resolveMessages(gen *Generator) error {
559 msg, ok := gen.messagesByName[rpc.VPP.Request]
561 return fmt.Errorf("rpc %v: no message for request type %v", rpc.GoName, rpc.VPP.Request)
565 if rpc.VPP.Reply != "" && rpc.VPP.Reply != serviceNoReply {
566 msg, ok := gen.messagesByName[rpc.VPP.Reply]
568 return fmt.Errorf("rpc %v: no message for reply type %v", rpc.GoName, rpc.VPP.Reply)
572 if rpc.VPP.StreamMsg != "" {
573 msg, ok := gen.messagesByName[rpc.VPP.StreamMsg]
575 return fmt.Errorf("rpc %v: no message for stream type %v", rpc.GoName, rpc.VPP.StreamMsg)