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)
93 if !file.isTypesFile() {
95 g.P(apiName, " = ", strconv.Quote(g.file.Desc.Name))
96 g.P(apiVersion, " = ", strconv.Quote(g.file.Version))
97 g.P(apiCrc, " = ", g.file.Desc.CRC)
102 for _, enum := range g.file.Enums {
105 for _, alias := range g.file.Aliases {
108 for _, typ := range g.file.Structs {
111 for _, union := range g.file.Unions {
119 func genPackageComment(g *GenFile) {
120 apifile := g.file.Desc.Name + ".api"
121 g.P("// Package ", g.file.PackageName, " contains generated bindings for API file ", apifile, ".")
124 printObjNum := func(obj string, num int) {
127 if strings.HasSuffix(obj, "s") {
133 g.P("// ", fmt.Sprintf("%3d", num), " ", obj)
136 printObjNum("alias", len(g.file.Aliases))
137 printObjNum("enum", len(g.file.Enums))
138 printObjNum("struct", len(g.file.Structs))
139 printObjNum("union", len(g.file.Unions))
140 printObjNum("message", len(g.file.Messages))
144 func genImport(g *GenFile, imp string) {
145 impFile, ok := g.gen.FilesByName[imp]
149 if impFile.GoImportPath == g.file.GoImportPath {
150 // Skip generating imports for types in the same package
153 // Generate imports for all dependencies, even if not used
154 g.Import(impFile.GoImportPath)
157 func genTypeComment(g *GenFile, goName string, vppName string, objKind string) {
158 g.P("// ", goName, " defines ", objKind, " '", vppName, "'.")
161 func genTypeOptionComment(g *GenFile, options map[string]string) {
162 // all messages for API versions < 1.0.0 are in_progress by default
163 if msg, ok := options[msgInProgress]; ok || options[msgStatus] == msgInProgress ||
164 len(g.file.Version) > 1 && g.file.Version[0:2] == "0." {
168 g.P("// InProgress: ", msg)
170 if msg, ok := options[msgDeprecated]; ok || options[msgStatus] == msgDeprecated {
174 g.P("// Deprecated: ", msg)
178 func genEnum(g *GenFile, enum *Enum) {
179 logf("gen ENUM %s (%s) - %d entries", enum.GoName, enum.Name, len(enum.Entries))
181 genTypeComment(g, enum.GoName, enum.Name, "enum")
183 gotype := BaseTypesGo[enum.Type]
185 g.P("type ", enum.GoName, " ", gotype)
188 // generate enum entries
190 for _, entry := range enum.Entries {
191 g.P(entry.Name, " ", enum.GoName, " = ", entry.Value)
196 // generate enum conversion maps
198 g.P(enum.GoName, "_name = map[", gotype, "]string{")
199 for _, entry := range enum.Entries {
200 g.P(entry.Value, ": ", strconv.Quote(entry.Name), ",")
203 g.P(enum.GoName, "_value = map[string]", gotype, "{")
204 for _, entry := range enum.Entries {
205 g.P(strconv.Quote(entry.Name), ": ", entry.Value, ",")
211 if enum.IsFlag || isEnumFlag(enum) {
212 size := BaseTypeSizes[enum.Type] * 8
213 g.P("func (x ", enum.GoName, ") String() string {")
214 g.P(" s, ok := ", enum.GoName, "_name[", gotype, "(x)]")
215 g.P(" if ok { return s }")
216 g.P(" str := func(n ", gotype, ") string {")
217 g.P(" s, ok := ", enum.GoName, "_name[", gotype, "(n)]")
221 g.P(" return \"", enum.GoName, "(\" + ", strconvPkg.Ident("Itoa"), "(int(n)) + \")\"")
223 g.P(" for i := ", gotype, "(0); i <= ", size, "; i++ {")
224 g.P(" val := ", gotype, "(x)")
225 g.P(" if val&(1<<i) != 0 {")
226 g.P(" if s != \"\" {")
229 g.P(" s += str(1<<i)")
232 g.P(" if s == \"\" {")
233 g.P(" return str(", gotype, "(x))")
239 g.P("func (x ", enum.GoName, ") String() string {")
240 g.P(" s, ok := ", enum.GoName, "_name[", gotype, "(x)]")
241 g.P(" if ok { return s }")
242 g.P(" return \"", enum.GoName, "(\" + ", strconvPkg.Ident("Itoa"), "(int(x)) + \")\"")
248 func genAlias(g *GenFile, alias *Alias) {
249 logf("gen ALIAS %s (%s) - type: %s length: %d", alias.GoName, alias.Name, alias.Type, alias.Length)
251 genTypeComment(g, alias.GoName, alias.Name, "alias")
255 case alias.TypeStruct != nil:
256 gotype = g.GoIdent(alias.TypeStruct.GoIdent)
257 case alias.TypeUnion != nil:
258 gotype = g.GoIdent(alias.TypeUnion.GoIdent)
260 gotype = BaseTypesGo[alias.Type]
262 if alias.Length > 0 {
263 gotype = fmt.Sprintf("[%d]%s", alias.Length, gotype)
266 g.P("type ", alias.GoName, " ", gotype)
269 // generate alias-specific methods
272 genIPConversion(g, alias.GoName, 4)
274 genIPConversion(g, alias.GoName, 16)
275 case "address_with_prefix":
276 genAddressWithPrefixConversion(g, alias.GoName)
278 genMacAddressConversion(g, alias.GoName)
280 genTimestampConversion(g, alias.GoName)
284 func genStruct(g *GenFile, typ *Struct) {
285 logf("gen STRUCT %s (%s) - %d fields", typ.GoName, typ.Name, len(typ.Fields))
287 genTypeComment(g, typ.GoName, typ.Name, "type")
289 if len(typ.Fields) == 0 {
290 g.P("type ", typ.GoName, " struct {}")
292 g.P("type ", typ.GoName, " struct {")
293 for i := range typ.Fields {
294 genField(g, typ.Fields, i)
300 // generate type-specific methods
303 genAddressConversion(g, typ.GoName)
305 genPrefixConversion(g, typ.GoName)
307 genIPPrefixConversion(g, typ.GoName, 4)
309 genIPPrefixConversion(g, typ.GoName, 6)
313 func genUnion(g *GenFile, union *Union) {
314 logf("gen UNION %s (%s) - %d fields", union.GoName, union.Name, len(union.Fields))
316 genTypeComment(g, union.GoName, union.Name, "union")
318 g.P("type ", union.GoName, " struct {")
320 // generate field comments
321 g.P("// ", union.GoName, " can be one of:")
322 for _, field := range union.Fields {
323 g.P("// - ", field.GoName, " *", getFieldType(g, field))
326 // generate data field
327 maxSize := getUnionSize(union)
328 g.P(fieldUnionData, " [", maxSize, "]byte")
330 // generate end of the struct
334 // generate methods for fields
335 for _, field := range union.Fields {
336 genUnionField(g, union, field)
341 func genUnionField(g *GenFile, union *Union, field *Field) {
342 fieldType := fieldGoType(g, field)
343 constructorName := union.GoName + field.GoName
346 g.P("func ", constructorName, "(a ", fieldType, ") (u ", union.GoName, ") {")
347 g.P(" u.Set", field.GoName, "(a)")
352 g.P("func (u *", union.GoName, ") Set", field.GoName, "(a ", fieldType, ") {")
353 g.P(" buf := ", govppCodecPkg.Ident("NewBuffer"), "(u.", fieldUnionData, "[:])")
354 encodeField(g, field, "a", func(name string) string {
360 g.P("func (u *", union.GoName, ") Get", field.GoName, "() (a ", fieldType, ") {")
361 g.P(" buf := ", govppCodecPkg.Ident("NewBuffer"), "(u.", fieldUnionData, "[:])")
362 decodeField(g, field, "a", func(name string) string {
371 func withSuffix(s string, suffix string) string {
372 if strings.HasSuffix(s, suffix) {
378 func genField(g *GenFile, fields []*Field, i int) {
381 logf(" gen FIELD[%d] %s (%s) - type: %q (array: %v/%v)", i, field.GoName, field.Name, field.Type, field.Array, field.Length)
383 gotype := getFieldType(g, field)
385 "binapi": fieldTagBinapi(field),
386 "json": fieldTagJson(field),
389 g.P(field.GoName, " ", gotype, tags)
392 func fieldTagJson(field *Field) string {
393 if field.FieldSizeOf != nil {
396 return fmt.Sprintf("%s,omitempty", field.Name)
399 func fieldTagBinapi(field *Field) string {
400 typ := fromApiType(field.Type)
402 if field.Length > 0 {
403 typ = fmt.Sprintf("%s[%d]", typ, field.Length)
404 } else if field.SizeFrom != "" {
405 typ = fmt.Sprintf("%s[%s]", typ, field.SizeFrom)
407 typ = fmt.Sprintf("%s[]", typ)
412 fmt.Sprintf("name=%s", field.Name),
414 if limit, ok := field.Meta["limit"]; ok && limit.(int) > 0 {
415 tag = append(tag, fmt.Sprintf("limit=%s", limit))
417 if def, ok := field.Meta["default"]; ok && def != nil {
418 switch fieldActualType(field) {
419 case I8, I16, I32, I64:
420 def = int(def.(float64))
421 case U8, U16, U32, U64:
422 def = uint(def.(float64))
426 tag = append(tag, fmt.Sprintf("default=%v", def))
428 return strings.Join(tag, ",")
431 type structTags map[string]string
433 func (tags structTags) String() string {
438 for k := range tags {
439 keys = append(keys, k)
443 for _, key := range keys {
445 ss = append(ss, fmt.Sprintf(`%s:%s`, key, strconv.Quote(tag)))
447 return "`" + strings.Join(ss, " ") + "`"
450 func genMessages(g *GenFile) {
451 if len(g.file.Messages) == 0 {
455 for _, msg := range g.file.Messages {
459 // generate registrations
460 initFnName := fmt.Sprintf("file_%s_binapi_init", g.file.PackageName)
462 g.P("func init() { ", initFnName, "() }")
463 g.P("func ", initFnName, "() {")
464 for _, msg := range g.file.Messages {
465 id := fmt.Sprintf("%s_%s", msg.Name, msg.CRC)
466 g.P(govppApiPkg.Ident("RegisterMessage"), "((*", msg.GoIdent, ")(nil), ", strconv.Quote(id), ")")
471 // generate list of messages
472 g.P("// Messages returns list of all messages in this module.")
473 g.P("func AllMessages() []", govppApiPkg.Ident("Message"), " {")
474 g.P("return []", govppApiPkg.Ident("Message"), "{")
475 for _, msg := range g.file.Messages {
476 g.P("(*", msg.GoIdent, ")(nil),")
482 func genMessage(g *GenFile, msg *Message) {
483 logf("gen MESSAGE %s (%s) - %d fields", msg.GoName, msg.Name, len(msg.Fields))
485 genTypeComment(g, msg.GoIdent.GoName, msg.Name, "message")
486 genTypeOptionComment(g, msg.Options)
488 // generate message definition
489 if len(msg.Fields) == 0 {
490 g.P("type ", msg.GoIdent, " struct {}")
492 g.P("type ", msg.GoIdent, " struct {")
493 for i := range msg.Fields {
494 genField(g, msg.Fields, i)
500 genMessageMethods(g, msg)
503 genMessageSize(g, msg.GoIdent.GoName, msg.Fields)
504 genMessageMarshal(g, msg.GoIdent.GoName, msg.Fields)
505 genMessageUnmarshal(g, msg.GoIdent.GoName, msg.Fields)
510 func genMessageMethods(g *GenFile, msg *Message) {
512 g.P("func (m *", msg.GoIdent.GoName, ") Reset() { *m = ", msg.GoIdent.GoName, "{} }")
514 // GetMessageName method
515 g.P("func (*", msg.GoIdent.GoName, ") GetMessageName() string { return ", strconv.Quote(msg.Name), " }")
517 // GetCrcString method
518 g.P("func (*", msg.GoIdent.GoName, ") GetCrcString() string { return ", strconv.Quote(msg.CRC), " }")
520 // GetMessageType method
521 g.P("func (*", msg.GoIdent.GoName, ") GetMessageType() api.MessageType {")
522 g.P(" return ", apiMsgType(msg.msgType))
528 func apiMsgType(t msgType) GoIdent {
531 return govppApiPkg.Ident("RequestMessage")
533 return govppApiPkg.Ident("ReplyMessage")
535 return govppApiPkg.Ident("EventMessage")
537 return govppApiPkg.Ident("OtherMessage")