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 if !gen.opts.NoSourcePathInfo {
59 g.P("// source: ", g.file.Desc.Path)
65 g.P("package ", file.PackageName)
68 for _, imp := range g.file.Imports {
72 // generate version assertion
73 g.P("// This is a compile-time assertion to ensure that this generated file")
74 g.P("// is compatible with the GoVPP api package it is being compiled against.")
75 g.P("// A compilation error at this line likely means your copy of the")
76 g.P("// GoVPP api package needs to be updated.")
77 g.P("const _ = ", govppApiPkg.Ident("GoVppAPIPackageIsVersion"), generatedCodeVersion)
80 if !file.isTypesFile() {
82 g.P(apiName, " = ", strconv.Quote(g.file.Desc.Name))
83 g.P(apiVersion, " = ", strconv.Quote(g.file.Version))
84 g.P(apiCrc, " = ", g.file.Desc.CRC)
89 for _, enum := range g.file.Enums {
92 for _, alias := range g.file.Aliases {
95 for _, typ := range g.file.Structs {
98 for _, union := range g.file.Unions {
106 func genPackageComment(g *GenFile) {
107 apifile := g.file.Desc.Name + ".api"
108 g.P("// Package ", g.file.PackageName, " contains generated bindings for API file ", apifile, ".")
111 printObjNum := func(obj string, num int) {
114 if strings.HasSuffix(obj, "s") {
120 g.P("// ", fmt.Sprintf("%3d", num), " ", obj)
123 printObjNum("alias", len(g.file.Aliases))
124 printObjNum("enum", len(g.file.Enums))
125 printObjNum("struct", len(g.file.Structs))
126 printObjNum("union", len(g.file.Unions))
127 printObjNum("message", len(g.file.Messages))
131 func genImport(g *GenFile, imp string) {
132 impFile, ok := g.gen.FilesByName[imp]
136 if impFile.GoImportPath == g.file.GoImportPath {
137 // Skip generating imports for types in the same package
140 // Generate imports for all dependencies, even if not used
141 g.Import(impFile.GoImportPath)
144 func genTypeComment(g *GenFile, goName string, vppName string, objKind string) {
145 g.P("// ", goName, " defines ", objKind, " '", vppName, "'.")
148 func genEnum(g *GenFile, enum *Enum) {
149 logf("gen ENUM %s (%s) - %d entries", enum.GoName, enum.Name, len(enum.Entries))
151 genTypeComment(g, enum.GoName, enum.Name, "enum")
153 gotype := BaseTypesGo[enum.Type]
155 g.P("type ", enum.GoName, " ", gotype)
158 // generate enum entries
160 for _, entry := range enum.Entries {
161 g.P(entry.Name, " ", enum.GoName, " = ", entry.Value)
166 // generate enum conversion maps
168 g.P(enum.GoName, "_name = map[", gotype, "]string{")
169 for _, entry := range enum.Entries {
170 g.P(entry.Value, ": ", strconv.Quote(entry.Name), ",")
173 g.P(enum.GoName, "_value = map[string]", gotype, "{")
174 for _, entry := range enum.Entries {
175 g.P(strconv.Quote(entry.Name), ": ", entry.Value, ",")
181 if isEnumFlag(enum) {
182 size := BaseTypeSizes[enum.Type] * 8
183 g.P("func (x ", enum.GoName, ") String() string {")
184 g.P(" s, ok := ", enum.GoName, "_name[", gotype, "(x)]")
185 g.P(" if ok { return s }")
186 g.P(" str := func(n ", gotype, ") string {")
187 g.P(" s, ok := ", enum.GoName, "_name[", gotype, "(n)]")
191 g.P(" return \"", enum.GoName, "(\" + ", strconvPkg.Ident("Itoa"), "(int(n)) + \")\"")
193 g.P(" for i := ", gotype, "(0); i <= ", size, "; i++ {")
194 g.P(" val := ", gotype, "(x)")
195 g.P(" if val&(1<<i) != 0 {")
196 g.P(" if s != \"\" {")
199 g.P(" s += str(1<<i)")
202 g.P(" if s == \"\" {")
203 g.P(" return str(", gotype, "(x))")
209 g.P("func (x ", enum.GoName, ") String() string {")
210 g.P(" s, ok := ", enum.GoName, "_name[", gotype, "(x)]")
211 g.P(" if ok { return s }")
212 g.P(" return \"", enum.GoName, "(\" + ", strconvPkg.Ident("Itoa"), "(int(x)) + \")\"")
218 func genAlias(g *GenFile, alias *Alias) {
219 logf("gen ALIAS %s (%s) - type: %s length: %d", alias.GoName, alias.Name, alias.Type, alias.Length)
221 genTypeComment(g, alias.GoName, alias.Name, "alias")
225 case alias.TypeStruct != nil:
226 gotype = g.GoIdent(alias.TypeStruct.GoIdent)
227 case alias.TypeUnion != nil:
228 gotype = g.GoIdent(alias.TypeUnion.GoIdent)
230 gotype = BaseTypesGo[alias.Type]
232 if alias.Length > 0 {
233 gotype = fmt.Sprintf("[%d]%s", alias.Length, gotype)
236 g.P("type ", alias.GoName, " ", gotype)
239 // generate alias-specific methods
242 genIPConversion(g, alias.GoName, 4)
244 genIPConversion(g, alias.GoName, 16)
245 case "address_with_prefix":
246 genAddressWithPrefixConversion(g, alias.GoName)
248 genMacAddressConversion(g, alias.GoName)
252 func genStruct(g *GenFile, typ *Struct) {
253 logf("gen STRUCT %s (%s) - %d fields", typ.GoName, typ.Name, len(typ.Fields))
255 genTypeComment(g, typ.GoName, typ.Name, "type")
257 if len(typ.Fields) == 0 {
258 g.P("type ", typ.GoName, " struct {}")
260 g.P("type ", typ.GoName, " struct {")
261 for i := range typ.Fields {
262 genField(g, typ.Fields, i)
268 // generate type-specific methods
271 genAddressConversion(g, typ.GoName)
273 genPrefixConversion(g, typ.GoName)
275 genIPPrefixConversion(g, typ.GoName, 4)
277 genIPPrefixConversion(g, typ.GoName, 6)
281 func genUnion(g *GenFile, union *Union) {
282 logf("gen UNION %s (%s) - %d fields", union.GoName, union.Name, len(union.Fields))
284 genTypeComment(g, union.GoName, union.Name, "union")
286 g.P("type ", union.GoName, " struct {")
288 // generate field comments
289 g.P("// ", union.GoName, " can be one of:")
290 for _, field := range union.Fields {
291 g.P("// - ", field.GoName, " *", getFieldType(g, field))
294 // generate data field
295 maxSize := getUnionSize(union)
296 g.P(fieldUnionData, " [", maxSize, "]byte")
298 // generate end of the struct
302 // generate methods for fields
303 for _, field := range union.Fields {
304 genUnionField(g, union, field)
309 func genUnionField(g *GenFile, union *Union, field *Field) {
310 fieldType := fieldGoType(g, field)
311 constructorName := union.GoName + field.GoName
314 g.P("func ", constructorName, "(a ", fieldType, ") (u ", union.GoName, ") {")
315 g.P(" u.Set", field.GoName, "(a)")
320 g.P("func (u *", union.GoName, ") Set", field.GoName, "(a ", fieldType, ") {")
321 g.P(" buf := ", govppCodecPkg.Ident("NewBuffer"), "(u.", fieldUnionData, "[:])")
322 encodeField(g, field, "a", func(name string) string {
328 g.P("func (u *", union.GoName, ") Get", field.GoName, "() (a ", fieldType, ") {")
329 g.P(" buf := ", govppCodecPkg.Ident("NewBuffer"), "(u.", fieldUnionData, "[:])")
330 decodeField(g, field, "a", func(name string) string {
339 func withSuffix(s string, suffix string) string {
340 if strings.HasSuffix(s, suffix) {
346 func genField(g *GenFile, fields []*Field, i int) {
349 logf(" gen FIELD[%d] %s (%s) - type: %q (array: %v/%v)", i, field.GoName, field.Name, field.Type, field.Array, field.Length)
351 gotype := getFieldType(g, field)
353 "binapi": fieldTagBinapi(field),
354 "json": fieldTagJson(field),
357 g.P(field.GoName, " ", gotype, tags)
360 func fieldTagJson(field *Field) string {
361 if field.FieldSizeOf != nil {
364 return fmt.Sprintf("%s,omitempty", field.Name)
367 func fieldTagBinapi(field *Field) string {
368 typ := fromApiType(field.Type)
370 if field.Length > 0 {
371 typ = fmt.Sprintf("%s[%d]", typ, field.Length)
372 } else if field.SizeFrom != "" {
373 typ = fmt.Sprintf("%s[%s]", typ, field.SizeFrom)
375 typ = fmt.Sprintf("%s[]", typ)
380 fmt.Sprintf("name=%s", field.Name),
382 if limit, ok := field.Meta["limit"]; ok && limit.(int) > 0 {
383 tag = append(tag, fmt.Sprintf("limit=%s", limit))
385 if def, ok := field.Meta["default"]; ok && def != nil {
386 switch fieldActualType(field) {
387 case I8, I16, I32, I64:
388 def = int(def.(float64))
389 case U8, U16, U32, U64:
390 def = uint(def.(float64))
394 tag = append(tag, fmt.Sprintf("default=%v", def))
396 return strings.Join(tag, ",")
399 type structTags map[string]string
401 func (tags structTags) String() string {
406 for k := range tags {
407 keys = append(keys, k)
411 for _, key := range keys {
413 ss = append(ss, fmt.Sprintf(`%s:%s`, key, strconv.Quote(tag)))
415 return "`" + strings.Join(ss, " ") + "`"
418 func genMessages(g *GenFile) {
419 if len(g.file.Messages) == 0 {
423 for _, msg := range g.file.Messages {
427 // generate registrations
428 initFnName := fmt.Sprintf("file_%s_binapi_init", g.file.PackageName)
430 g.P("func init() { ", initFnName, "() }")
431 g.P("func ", initFnName, "() {")
432 for _, msg := range g.file.Messages {
433 id := fmt.Sprintf("%s_%s", msg.Name, msg.CRC)
434 g.P(govppApiPkg.Ident("RegisterMessage"), "((*", msg.GoIdent, ")(nil), ", strconv.Quote(id), ")")
439 // generate list of messages
440 g.P("// Messages returns list of all messages in this module.")
441 g.P("func AllMessages() []", govppApiPkg.Ident("Message"), " {")
442 g.P("return []", govppApiPkg.Ident("Message"), "{")
443 for _, msg := range g.file.Messages {
444 g.P("(*", msg.GoIdent, ")(nil),")
450 func genMessage(g *GenFile, msg *Message) {
451 logf("gen MESSAGE %s (%s) - %d fields", msg.GoName, msg.Name, len(msg.Fields))
453 genTypeComment(g, msg.GoIdent.GoName, msg.Name, "message")
455 // generate message definition
456 if len(msg.Fields) == 0 {
457 g.P("type ", msg.GoIdent, " struct {}")
459 g.P("type ", msg.GoIdent, " struct {")
460 for i := range msg.Fields {
461 genField(g, msg.Fields, i)
467 genMessageMethods(g, msg)
470 genMessageSize(g, msg.GoIdent.GoName, msg.Fields)
471 genMessageMarshal(g, msg.GoIdent.GoName, msg.Fields)
472 genMessageUnmarshal(g, msg.GoIdent.GoName, msg.Fields)
477 func genMessageMethods(g *GenFile, msg *Message) {
479 g.P("func (m *", msg.GoIdent.GoName, ") Reset() { *m = ", msg.GoIdent.GoName, "{} }")
481 // GetMessageName method
482 g.P("func (*", msg.GoIdent.GoName, ") GetMessageName() string { return ", strconv.Quote(msg.Name), " }")
484 // GetCrcString method
485 g.P("func (*", msg.GoIdent.GoName, ") GetCrcString() string { return ", strconv.Quote(msg.CRC), " }")
487 // GetMessageType method
488 g.P("func (*", msg.GoIdent.GoName, ") GetMessageType() api.MessageType {")
489 g.P(" return ", apiMsgType(msg.msgType))
495 func apiMsgType(t msgType) GoIdent {
498 return govppApiPkg.Ident("RequestMessage")
500 return govppApiPkg.Ident("ReplyMessage")
502 return govppApiPkg.Ident("EventMessage")
504 return govppApiPkg.Ident("OtherMessage")