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
44 func GenerateAPI(gen *Generator, file *File) *GenFile {
45 logf("----------------------------")
46 logf(" Generate API - %s", file.Desc.Name)
47 logf("----------------------------")
49 filename := path.Join(file.FilenamePrefix, file.Desc.Name+".ba.go")
50 g := gen.NewGenFile(filename, file.GoImportPath)
53 g.P("// Code generated by GoVPP's binapi-generator. DO NOT EDIT.")
54 if !gen.opts.NoVersionInfo {
56 g.P("// binapi-generator: ", version.Version())
57 g.P("// VPP: ", g.gen.vppVersion)
58 g.P("// source: ", g.file.Desc.Path)
63 g.P("package ", file.PackageName)
66 for _, imp := range g.file.Imports {
70 // generate version assertion
71 g.P("// This is a compile-time assertion to ensure that this generated file")
72 g.P("// is compatible with the GoVPP api package it is being compiled against.")
73 g.P("// A compilation error at this line likely means your copy of the")
74 g.P("// GoVPP api package needs to be updated.")
75 g.P("const _ = ", govppApiPkg.Ident("GoVppAPIPackageIsVersion"), generatedCodeVersion)
78 if !file.isTypesFile() {
80 g.P(apiName, " = ", strconv.Quote(g.file.Desc.Name))
81 g.P(apiVersion, " = ", strconv.Quote(g.file.Version))
82 g.P(apiCrc, " = ", g.file.Desc.CRC)
87 for _, enum := range g.file.Enums {
90 for _, alias := range g.file.Aliases {
93 for _, typ := range g.file.Structs {
96 for _, union := range g.file.Unions {
104 func genPackageComment(g *GenFile) {
105 apifile := g.file.Desc.Name + ".api"
106 g.P("// Package ", g.file.PackageName, " contains generated bindings for API file ", apifile, ".")
109 printObjNum := func(obj string, num int) {
112 if strings.HasSuffix(obj, "s") {
118 g.P("// ", fmt.Sprintf("%3d", num), " ", obj)
121 printObjNum("alias", len(g.file.Aliases))
122 printObjNum("enum", len(g.file.Enums))
123 printObjNum("struct", len(g.file.Structs))
124 printObjNum("union", len(g.file.Unions))
125 printObjNum("message", len(g.file.Messages))
129 func genImport(g *GenFile, imp string) {
130 impFile, ok := g.gen.FilesByName[imp]
134 if impFile.GoImportPath == g.file.GoImportPath {
135 // Skip generating imports for types in the same package
138 // Generate imports for all dependencies, even if not used
139 g.Import(impFile.GoImportPath)
142 func genTypeComment(g *GenFile, goName string, vppName string, objKind string) {
143 g.P("// ", goName, " defines ", objKind, " '", vppName, "'.")
146 func genEnum(g *GenFile, enum *Enum) {
147 logf("gen ENUM %s (%s) - %d entries", enum.GoName, enum.Name, len(enum.Entries))
149 genTypeComment(g, enum.GoName, enum.Name, "enum")
151 gotype := BaseTypesGo[enum.Type]
153 g.P("type ", enum.GoName, " ", gotype)
156 // generate enum entries
158 for _, entry := range enum.Entries {
159 g.P(entry.Name, " ", enum.GoName, " = ", entry.Value)
164 // generate enum conversion maps
166 g.P(enum.GoName, "_name = map[", gotype, "]string{")
167 for _, entry := range enum.Entries {
168 g.P(entry.Value, ": ", strconv.Quote(entry.Name), ",")
171 g.P(enum.GoName, "_value = map[string]", gotype, "{")
172 for _, entry := range enum.Entries {
173 g.P(strconv.Quote(entry.Name), ": ", entry.Value, ",")
179 if isEnumFlag(enum) {
180 size := BaseTypeSizes[enum.Type] * 8
181 g.P("func (x ", enum.GoName, ") String() string {")
182 g.P(" s, ok := ", enum.GoName, "_name[", gotype, "(x)]")
183 g.P(" if ok { return s }")
184 g.P(" str := func(n ", gotype, ") string {")
185 g.P(" s, ok := ", enum.GoName, "_name[", gotype, "(n)]")
189 g.P(" return \"", enum.GoName, "(\" + ", strconvPkg.Ident("Itoa"), "(int(n)) + \")\"")
191 g.P(" for i := ", gotype, "(0); i <= ", size, "; i++ {")
192 g.P(" val := ", gotype, "(x)")
193 g.P(" if val&(1<<i) != 0 {")
194 g.P(" if s != \"\" {")
197 g.P(" s += str(1<<i)")
200 g.P(" if s == \"\" {")
201 g.P(" return str(", gotype, "(x))")
207 g.P("func (x ", enum.GoName, ") String() string {")
208 g.P(" s, ok := ", enum.GoName, "_name[", gotype, "(x)]")
209 g.P(" if ok { return s }")
210 g.P(" return \"", enum.GoName, "(\" + ", strconvPkg.Ident("Itoa"), "(int(x)) + \")\"")
216 func genAlias(g *GenFile, alias *Alias) {
217 logf("gen ALIAS %s (%s) - type: %s length: %d", alias.GoName, alias.Name, alias.Type, alias.Length)
219 genTypeComment(g, alias.GoName, alias.Name, "alias")
223 case alias.TypeStruct != nil:
224 gotype = g.GoIdent(alias.TypeStruct.GoIdent)
225 case alias.TypeUnion != nil:
226 gotype = g.GoIdent(alias.TypeUnion.GoIdent)
228 gotype = BaseTypesGo[alias.Type]
230 if alias.Length > 0 {
231 gotype = fmt.Sprintf("[%d]%s", alias.Length, gotype)
234 g.P("type ", alias.GoName, " ", gotype)
237 // generate alias-specific methods
240 generateIPConversion(g, alias.GoName, 4)
242 generateIPConversion(g, alias.GoName, 16)
243 case "address_with_prefix":
244 generateAddressWithPrefixConversion(g, alias.GoName)
246 generateMacAddressConversion(g, alias.GoName)
250 func genStruct(g *GenFile, typ *Struct) {
251 logf("gen STRUCT %s (%s) - %d fields", typ.GoName, typ.Name, len(typ.Fields))
253 genTypeComment(g, typ.GoName, typ.Name, "type")
255 if len(typ.Fields) == 0 {
256 g.P("type ", typ.GoName, " struct {}")
258 g.P("type ", typ.GoName, " struct {")
259 for i := range typ.Fields {
260 generateField(g, typ.Fields, i)
266 // generate type-specific methods
269 generateAddressConversion(g, typ.GoName)
271 generatePrefixConversion(g, typ.GoName)
273 generateIPPrefixConversion(g, typ.GoName, 4)
275 generateIPPrefixConversion(g, typ.GoName, 6)
279 func genUnion(g *GenFile, union *Union) {
280 logf("gen UNION %s (%s) - %d fields", union.GoName, union.Name, len(union.Fields))
282 genTypeComment(g, union.GoName, union.Name, "union")
284 g.P("type ", union.GoName, " struct {")
286 for _, field := range union.Fields {
287 g.P("// ", field.GoName, " *", getFieldType(g, field))
290 // generate data field
291 maxSize := getUnionSize(union)
292 g.P(fieldUnionData, " [", maxSize, "]byte")
294 // generate end of the struct
298 // generate methods for fields
299 for _, field := range union.Fields {
300 genUnionFieldMethods(g, union.GoName, field)
305 func genUnionFieldMethods(g *GenFile, structName string, field *Field) {
306 getterStruct := fieldGoType(g, field)
309 g.P("func ", structName, field.GoName, "(a ", getterStruct, ") (u ", structName, ") {")
310 g.P(" u.Set", field.GoName, "(a)")
315 g.P("func (u *", structName, ") Set", field.GoName, "(a ", getterStruct, ") {")
316 g.P(" var buf = ", govppCodecPkg.Ident("NewBuffer"), "(u.", fieldUnionData, "[:])")
317 encodeField(g, field, "a", func(name string) string {
323 g.P("func (u *", structName, ") Get", field.GoName, "() (a ", getterStruct, ") {")
324 g.P(" var buf = ", govppCodecPkg.Ident("NewBuffer"), "(u.", fieldUnionData, "[:])")
325 decodeField(g, field, "a", func(name string) string {
333 func generateField(g *GenFile, fields []*Field, i int) {
336 logf(" gen FIELD[%d] %s (%s) - type: %q (array: %v/%v)", i, field.GoName, field.Name, field.Type, field.Array, field.Length)
338 gotype := getFieldType(g, field)
340 "binapi": fieldTagJSON(field),
341 "json": fieldTagBinapi(field),
344 g.P(field.GoName, " ", gotype, tags)
347 func fieldTagBinapi(field *Field) string {
348 if field.FieldSizeOf != nil {
351 return fmt.Sprintf("%s,omitempty", field.Name)
354 func fieldTagJSON(field *Field) string {
355 typ := fromApiType(field.Type)
357 if field.Length > 0 {
358 typ = fmt.Sprintf("%s[%d]", typ, field.Length)
359 } else if field.SizeFrom != "" {
360 typ = fmt.Sprintf("%s[%s]", typ, field.SizeFrom)
362 typ = fmt.Sprintf("%s[]", typ)
367 fmt.Sprintf("name=%s", field.Name),
369 if limit, ok := field.Meta["limit"]; ok && limit.(int) > 0 {
370 tag = append(tag, fmt.Sprintf("limit=%s", limit))
372 if def, ok := field.Meta["default"]; ok && def != nil {
373 actual := fieldActualType(field)
374 if t, ok := BaseTypesGo[actual]; ok {
376 case I8, I16, I32, I64:
377 def = int(def.(float64))
378 case U8, U16, U32, U64:
379 def = uint(def.(float64))
384 tag = append(tag, fmt.Sprintf("default=%s", def))
386 return strings.Join(tag, ",")
389 type structTags map[string]string
391 func (tags structTags) String() string {
396 for k := range tags {
397 keys = append(keys, k)
401 for _, key := range keys {
403 ss = append(ss, fmt.Sprintf(`%s:%s`, key, strconv.Quote(tag)))
405 return "`" + strings.Join(ss, " ") + "`"
408 func genMessages(g *GenFile) {
409 if len(g.file.Messages) == 0 {
413 for _, msg := range g.file.Messages {
417 // generate registrations
418 initFnName := fmt.Sprintf("file_%s_binapi_init", g.file.PackageName)
420 g.P("func init() { ", initFnName, "() }")
421 g.P("func ", initFnName, "() {")
422 for _, msg := range g.file.Messages {
423 id := fmt.Sprintf("%s_%s", msg.Name, msg.CRC)
424 g.P(govppApiPkg.Ident("RegisterMessage"), "((*", msg.GoIdent, ")(nil), ", strconv.Quote(id), ")")
429 // generate list of messages
430 g.P("// Messages returns list of all messages in this module.")
431 g.P("func AllMessages() []", govppApiPkg.Ident("Message"), " {")
432 g.P("return []", govppApiPkg.Ident("Message"), "{")
433 for _, msg := range g.file.Messages {
434 g.P("(*", msg.GoIdent, ")(nil),")
440 func genMessage(g *GenFile, msg *Message) {
441 logf("gen MESSAGE %s (%s) - %d fields", msg.GoName, msg.Name, len(msg.Fields))
443 genTypeComment(g, msg.GoIdent.GoName, msg.Name, "message")
445 // generate message definition
446 if len(msg.Fields) == 0 {
447 g.P("type ", msg.GoIdent, " struct {}")
449 g.P("type ", msg.GoIdent, " struct {")
450 for i := range msg.Fields {
451 generateField(g, msg.Fields, i)
457 generateMessageMethods(g, msg)
460 generateMessageSize(g, msg.GoIdent.GoName, msg.Fields)
461 generateMessageMarshal(g, msg.GoIdent.GoName, msg.Fields)
462 generateMessageUnmarshal(g, msg.GoIdent.GoName, msg.Fields)
467 func generateMessageMethods(g *GenFile, msg *Message) {
469 g.P("func (m *", msg.GoIdent.GoName, ") Reset() { *m = ", msg.GoIdent.GoName, "{} }")
471 // GetMessageName method
472 g.P("func (*", msg.GoIdent.GoName, ") GetMessageName() string { return ", strconv.Quote(msg.Name), " }")
474 // GetCrcString method
475 g.P("func (*", msg.GoIdent.GoName, ") GetCrcString() string { return ", strconv.Quote(msg.CRC), " }")
477 // GetMessageType method
478 g.P("func (*", msg.GoIdent.GoName, ") GetMessageType() api.MessageType {")
479 g.P(" return ", apiMsgType(msg.msgType))