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.
24 "git.fd.io/govpp.git/internal/version"
27 // library dependencies
29 strconvPkg = GoImportPath("strconv")
31 govppApiPkg = GoImportPath("git.fd.io/govpp.git/api")
32 govppCodecPkg = GoImportPath("git.fd.io/govpp.git/codec")
37 apiName = "APIFile" // API file name
38 apiVersion = "APIVersion" // API version number
39 apiCrc = "VersionCrc" // version checksum
41 fieldUnionData = "XXX_UnionData" // name for the union data field
47 msgDeprecated = "deprecated"
48 msgInProgress = "in_progress"
51 // generated option messages
53 deprecatedMsg = "the message will be removed in the future versions"
54 inProgressMsg = "the message form may change in the future versions"
57 func GenerateAPI(gen *Generator, file *File) *GenFile {
58 logf("----------------------------")
59 logf(" Generate API - %s", file.Desc.Name)
60 logf("----------------------------")
62 filename := path.Join(file.FilenamePrefix, file.Desc.Name+".ba.go")
63 g := gen.NewGenFile(filename, file.GoImportPath)
66 g.P("// Code generated by GoVPP's binapi-generator. DO NOT EDIT.")
67 if !gen.opts.NoVersionInfo {
69 g.P("// binapi-generator: ", version.Version())
70 g.P("// VPP: ", g.gen.vppVersion)
71 if !gen.opts.NoSourcePathInfo {
72 g.P("// source: ", g.file.Desc.Path)
78 g.P("package ", file.PackageName)
81 for _, imp := range g.file.Imports {
85 // generate version assertion
86 g.P("// This is a compile-time assertion to ensure that this generated file")
87 g.P("// is compatible with the GoVPP api package it is being compiled against.")
88 g.P("// A compilation error at this line likely means your copy of the")
89 g.P("// GoVPP api package needs to be updated.")
90 g.P("const _ = ", govppApiPkg.Ident("GoVppAPIPackageIsVersion"), generatedCodeVersion)
94 g.P(apiName, " = ", strconv.Quote(g.file.Desc.Name))
95 g.P(apiVersion, " = ", strconv.Quote(g.file.Version))
96 g.P(apiCrc, " = ", g.file.Desc.CRC)
100 for _, enum := range g.file.Enums {
103 for _, alias := range g.file.Aliases {
106 for _, typ := range g.file.Structs {
109 for _, union := range g.file.Unions {
117 func genPackageComment(g *GenFile) {
118 apifile := g.file.Desc.Name + ".api"
119 g.P("// Package ", g.file.PackageName, " contains generated bindings for API file ", apifile, ".")
122 printObjNum := func(obj string, num int) {
125 if strings.HasSuffix(obj, "s") {
131 g.P("// ", fmt.Sprintf("%3d", num), " ", obj)
134 printObjNum("alias", len(g.file.Aliases))
135 printObjNum("enum", len(g.file.Enums))
136 printObjNum("struct", len(g.file.Structs))
137 printObjNum("union", len(g.file.Unions))
138 printObjNum("message", len(g.file.Messages))
142 func genImport(g *GenFile, imp string) {
143 impFile, ok := g.gen.FilesByName[imp]
147 if impFile.GoImportPath == g.file.GoImportPath {
148 // Skip generating imports for types in the same package
151 // Generate imports for all dependencies, even if not used
152 g.Import(impFile.GoImportPath)
155 func genTypeComment(g *GenFile, goName string, vppName string, objKind string) {
156 g.P("// ", goName, " defines ", objKind, " '", vppName, "'.")
159 func genTypeOptionComment(g *GenFile, options map[string]string) {
160 // all messages for API versions < 1.0.0 are in_progress by default
161 if msg, ok := options[msgInProgress]; ok || options[msgStatus] == msgInProgress ||
162 len(g.file.Version) > 1 && g.file.Version[0:2] == "0." {
166 g.P("// InProgress: ", msg)
168 if msg, ok := options[msgDeprecated]; ok || options[msgStatus] == msgDeprecated {
172 g.P("// Deprecated: ", msg)
176 func genEnum(g *GenFile, enum *Enum) {
177 logf("gen ENUM %s (%s) - %d entries", enum.GoName, enum.Name, len(enum.Entries))
179 genTypeComment(g, enum.GoName, enum.Name, "enum")
181 gotype := BaseTypesGo[enum.Type]
183 g.P("type ", enum.GoName, " ", gotype)
186 // generate enum entries
188 for _, entry := range enum.Entries {
189 g.P(entry.Name, " ", enum.GoName, " = ", entry.Value)
194 // generate enum conversion maps
196 g.P(enum.GoName, "_name = map[", gotype, "]string{")
197 for _, entry := range enum.Entries {
198 g.P(entry.Value, ": ", strconv.Quote(entry.Name), ",")
201 g.P(enum.GoName, "_value = map[string]", gotype, "{")
202 for _, entry := range enum.Entries {
203 g.P(strconv.Quote(entry.Name), ": ", entry.Value, ",")
209 if enum.IsFlag || isEnumFlag(enum) {
210 size := BaseTypeSizes[enum.Type] * 8
211 g.P("func (x ", enum.GoName, ") String() string {")
212 g.P(" s, ok := ", enum.GoName, "_name[", gotype, "(x)]")
213 g.P(" if ok { return s }")
214 g.P(" str := func(n ", gotype, ") string {")
215 g.P(" s, ok := ", enum.GoName, "_name[", gotype, "(n)]")
219 g.P(" return \"", enum.GoName, "(\" + ", strconvPkg.Ident("Itoa"), "(int(n)) + \")\"")
221 g.P(" for i := ", gotype, "(0); i <= ", size, "; i++ {")
222 g.P(" val := ", gotype, "(x)")
223 g.P(" if val&(1<<i) != 0 {")
224 g.P(" if s != \"\" {")
227 g.P(" s += str(1<<i)")
230 g.P(" if s == \"\" {")
231 g.P(" return str(", gotype, "(x))")
237 g.P("func (x ", enum.GoName, ") String() string {")
238 g.P(" s, ok := ", enum.GoName, "_name[", gotype, "(x)]")
239 g.P(" if ok { return s }")
240 g.P(" return \"", enum.GoName, "(\" + ", strconvPkg.Ident("Itoa"), "(int(x)) + \")\"")
246 func genAlias(g *GenFile, alias *Alias) {
247 logf("gen ALIAS %s (%s) - type: %s length: %d", alias.GoName, alias.Name, alias.Type, alias.Length)
249 genTypeComment(g, alias.GoName, alias.Name, "alias")
253 case alias.TypeStruct != nil:
254 gotype = g.GoIdent(alias.TypeStruct.GoIdent)
255 case alias.TypeUnion != nil:
256 gotype = g.GoIdent(alias.TypeUnion.GoIdent)
258 gotype = BaseTypesGo[alias.Type]
260 if alias.Length > 0 {
261 gotype = fmt.Sprintf("[%d]%s", alias.Length, gotype)
264 g.P("type ", alias.GoName, " ", gotype)
267 // generate alias-specific methods
270 genIPConversion(g, alias.GoName, 4)
272 genIPConversion(g, alias.GoName, 16)
273 case "address_with_prefix":
274 genAddressWithPrefixConversion(g, alias.GoName)
276 genMacAddressConversion(g, alias.GoName)
278 genTimestampConversion(g, alias.GoName)
282 func genStruct(g *GenFile, typ *Struct) {
283 logf("gen STRUCT %s (%s) - %d fields", typ.GoName, typ.Name, len(typ.Fields))
285 genTypeComment(g, typ.GoName, typ.Name, "type")
287 if len(typ.Fields) == 0 {
288 g.P("type ", typ.GoName, " struct {}")
290 g.P("type ", typ.GoName, " struct {")
291 for i := range typ.Fields {
292 genField(g, typ.Fields, i)
298 // generate type-specific methods
301 genAddressConversion(g, typ.GoName)
303 genPrefixConversion(g, typ.GoName)
305 genIPPrefixConversion(g, typ.GoName, 4)
307 genIPPrefixConversion(g, typ.GoName, 6)
311 func genUnion(g *GenFile, union *Union) {
312 logf("gen UNION %s (%s) - %d fields", union.GoName, union.Name, len(union.Fields))
314 genTypeComment(g, union.GoName, union.Name, "union")
316 g.P("type ", union.GoName, " struct {")
318 // generate field comments
319 g.P("// ", union.GoName, " can be one of:")
320 for _, field := range union.Fields {
321 g.P("// - ", field.GoName, " *", getFieldType(g, field))
324 // generate data field
325 maxSize := getUnionSize(union)
326 g.P(fieldUnionData, " [", maxSize, "]byte")
328 // generate end of the struct
332 // generate methods for fields
333 for _, field := range union.Fields {
334 genUnionField(g, union, field)
339 func genUnionField(g *GenFile, union *Union, field *Field) {
340 fieldType := fieldGoType(g, field)
341 constructorName := union.GoName + field.GoName
344 g.P("func ", constructorName, "(a ", fieldType, ") (u ", union.GoName, ") {")
345 g.P(" u.Set", field.GoName, "(a)")
350 g.P("func (u *", union.GoName, ") Set", field.GoName, "(a ", fieldType, ") {")
351 g.P(" buf := ", govppCodecPkg.Ident("NewBuffer"), "(u.", fieldUnionData, "[:])")
352 encodeField(g, field, "a", func(name string) string {
358 g.P("func (u *", union.GoName, ") Get", field.GoName, "() (a ", fieldType, ") {")
359 g.P(" buf := ", govppCodecPkg.Ident("NewBuffer"), "(u.", fieldUnionData, "[:])")
360 decodeField(g, field, "a", func(name string) string {
369 func withSuffix(s string, suffix string) string {
370 if strings.HasSuffix(s, suffix) {
376 func genField(g *GenFile, fields []*Field, i int) {
379 logf(" gen FIELD[%d] %s (%s) - type: %q (array: %v/%v)", i, field.GoName, field.Name, field.Type, field.Array, field.Length)
381 gotype := getFieldType(g, field)
383 "binapi": fieldTagBinapi(field),
384 "json": fieldTagJson(field),
387 g.P(field.GoName, " ", gotype, tags)
390 func fieldTagJson(field *Field) string {
391 if field.FieldSizeOf != nil {
394 return fmt.Sprintf("%s,omitempty", field.Name)
397 func fieldTagBinapi(field *Field) string {
398 typ := fromApiType(field.Type)
400 if field.Length > 0 {
401 typ = fmt.Sprintf("%s[%d]", typ, field.Length)
402 } else if field.SizeFrom != "" {
403 typ = fmt.Sprintf("%s[%s]", typ, field.SizeFrom)
405 typ = fmt.Sprintf("%s[]", typ)
410 fmt.Sprintf("name=%s", field.Name),
412 if limit, ok := field.Meta["limit"]; ok && limit.(int) > 0 {
413 tag = append(tag, fmt.Sprintf("limit=%s", limit))
415 if def, ok := field.Meta["default"]; ok && def != nil {
416 switch fieldActualType(field) {
417 case I8, I16, I32, I64:
418 def = int(def.(float64))
419 case U8, U16, U32, U64:
420 def = uint(def.(float64))
424 tag = append(tag, fmt.Sprintf("default=%v", def))
426 return strings.Join(tag, ",")
429 type structTags map[string]string
431 func (tags structTags) String() string {
436 for k := range tags {
437 keys = append(keys, k)
441 for _, key := range keys {
443 ss = append(ss, fmt.Sprintf(`%s:%s`, key, strconv.Quote(tag)))
445 return "`" + strings.Join(ss, " ") + "`"
448 func genMessages(g *GenFile) {
449 if len(g.file.Messages) == 0 {
453 for _, msg := range g.file.Messages {
457 // generate registrations
458 initFnName := fmt.Sprintf("file_%s_binapi_init", g.file.PackageName)
460 g.P("func init() { ", initFnName, "() }")
461 g.P("func ", initFnName, "() {")
462 for _, msg := range g.file.Messages {
463 id := fmt.Sprintf("%s_%s", msg.Name, msg.CRC)
464 g.P(govppApiPkg.Ident("RegisterMessage"), "((*", msg.GoIdent, ")(nil), ", strconv.Quote(id), ")")
469 // generate list of messages
470 g.P("// Messages returns list of all messages in this module.")
471 g.P("func AllMessages() []", govppApiPkg.Ident("Message"), " {")
472 g.P("return []", govppApiPkg.Ident("Message"), "{")
473 for _, msg := range g.file.Messages {
474 g.P("(*", msg.GoIdent, ")(nil),")
480 func genMessage(g *GenFile, msg *Message) {
481 logf("gen MESSAGE %s (%s) - %d fields", msg.GoName, msg.Name, len(msg.Fields))
483 genTypeComment(g, msg.GoIdent.GoName, msg.Name, "message")
484 genTypeOptionComment(g, msg.Options)
486 // generate message definition
487 if len(msg.Fields) == 0 {
488 g.P("type ", msg.GoIdent, " struct {}")
490 g.P("type ", msg.GoIdent, " struct {")
491 for i := range msg.Fields {
492 genField(g, msg.Fields, i)
498 genMessageMethods(g, msg)
501 genMessageSize(g, msg.GoIdent.GoName, msg.Fields)
502 genMessageMarshal(g, msg.GoIdent.GoName, msg.Fields)
503 genMessageUnmarshal(g, msg.GoIdent.GoName, msg.Fields)
508 func genMessageMethods(g *GenFile, msg *Message) {
510 g.P("func (m *", msg.GoIdent.GoName, ") Reset() { *m = ", msg.GoIdent.GoName, "{} }")
512 // GetMessageName method
513 g.P("func (*", msg.GoIdent.GoName, ") GetMessageName() string { return ", strconv.Quote(msg.Name), " }")
515 // GetCrcString method
516 g.P("func (*", msg.GoIdent.GoName, ") GetCrcString() string { return ", strconv.Quote(msg.CRC), " }")
518 // GetMessageType method
519 g.P("func (*", msg.GoIdent.GoName, ") GetMessageType() api.MessageType {")
520 g.P(" return ", apiMsgType(msg.msgType))
526 func apiMsgType(t msgType) GoIdent {
529 return govppApiPkg.Ident("RequestMessage")
531 return govppApiPkg.Ident("ReplyMessage")
533 return govppApiPkg.Ident("EventMessage")
535 return govppApiPkg.Ident("OtherMessage")