aboutsummaryrefslogtreecommitdiffstats
path: root/cmd/binapi-generator
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/binapi-generator')
-rw-r--r--cmd/binapi-generator/definitions.go152
-rw-r--r--cmd/binapi-generator/definitions_test.go25
-rw-r--r--cmd/binapi-generator/doc.go16
-rw-r--r--cmd/binapi-generator/generate.go848
-rw-r--r--cmd/binapi-generator/generate_test.go450
-rw-r--r--cmd/binapi-generator/main.go350
-rw-r--r--cmd/binapi-generator/objects.go139
-rw-r--r--cmd/binapi-generator/parse.go557
-rw-r--r--cmd/binapi-generator/parse_test.go97
-rw-r--r--cmd/binapi-generator/testdata/acl.api.json929
-rw-r--r--cmd/binapi-generator/testdata/af_packet.api.json163
-rw-r--r--cmd/binapi-generator/testdata/input-generate-error.json3
-rw-r--r--cmd/binapi-generator/testdata/input-read-json-error.json1
-rw-r--r--cmd/binapi-generator/testdata/input.txt0
-rw-r--r--cmd/binapi-generator/testdata/ip.api.json2246
-rw-r--r--cmd/binapi-generator/types.go147
-rw-r--r--cmd/binapi-generator/util.go81
17 files changed, 160 insertions, 6044 deletions
diff --git a/cmd/binapi-generator/definitions.go b/cmd/binapi-generator/definitions.go
deleted file mode 100644
index 9bf9e53..0000000
--- a/cmd/binapi-generator/definitions.go
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright (c) 2018 Cisco and/or its affiliates.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at:
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
- "strings"
- "unicode"
-)
-
-// commonInitialisms is a set of common initialisms that need to stay in upper case.
-var commonInitialisms = map[string]bool{
- "ACL": true,
- "API": true,
- //"ASCII": true, // there are only two use cases for ASCII which already have initialism before and after
- "CPU": true,
- "CSS": true,
- "DNS": true,
- "DHCP": true,
- "EOF": true,
- "GUID": true,
- "HTML": true,
- "HTTP": true,
- "HTTPS": true,
- "ID": true,
- "IP": true,
- "ICMP": true,
- "JSON": true,
- "LHS": true,
- "QPS": true,
- "PID": true,
- "RAM": true,
- "RHS": true,
- "RPC": true,
- "SLA": true,
- "SMTP": true,
- "SQL": true,
- "SSH": true,
- "TCP": true,
- "TLS": true,
- "TTL": true,
- "UDP": true,
- "UI": true,
- "UID": true,
- "UUID": true,
- "URI": true,
- "URL": true,
- "UTF8": true,
- "VM": true,
- "VPN": true,
- "XML": true,
- "XMPP": true,
- "XSRF": true,
- "XSS": true,
-}
-
-// specialInitialisms is a set of special initialisms that need part to stay in upper case.
-var specialInitialisms = map[string]string{
- "IPV": "IPv",
- //"IPV4": "IPv4",
- //"IPV6": "IPv6",
-}
-
-func usesInitialism(s string) string {
- if u := strings.ToUpper(s); commonInitialisms[u] {
- return u
- } else if su, ok := specialInitialisms[u]; ok {
- return su
- }
- return ""
-}
-
-// camelCaseName returns correct name identifier (camelCase).
-func camelCaseName(name string) (should string) {
- name = strings.Title(name)
-
- // Fast path for simple cases: "_" and all lowercase.
- if name == "_" {
- return name
- }
- allLower := true
- for _, r := range name {
- if !unicode.IsLower(r) {
- allLower = false
- break
- }
- }
- if allLower {
- return name
- }
-
- // Split camelCase at any lower->upper transition, and split on underscores.
- // Check each word for common initialisms.
- runes := []rune(name)
- w, i := 0, 0 // index of start of word, scan
- for i+1 <= len(runes) {
- eow := false // whether we hit the end of a word
- if i+1 == len(runes) {
- eow = true
- } else if runes[i+1] == '_' {
- // underscore; shift the remainder forward over any run of underscores
- eow = true
- n := 1
- for i+n+1 < len(runes) && runes[i+n+1] == '_' {
- n++
- }
-
- // Leave at most one underscore if the underscore is between two digits
- if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) {
- n--
- }
-
- copy(runes[i+1:], runes[i+n+1:])
- runes = runes[:len(runes)-n]
- } else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) {
- // lower->non-lower
- eow = true
- }
- i++
- if !eow {
- continue
- }
-
- // [w,i) is a word.
- word := string(runes[w:i])
- if u := usesInitialism(word); u != "" {
- // Keep consistent case, which is lowercase only at the start.
- if w == 0 && unicode.IsLower(runes[w]) {
- u = strings.ToLower(u)
- }
- // All the common initialisms are ASCII,
- // so we can replace the bytes exactly.
- copy(runes[w:], []rune(u))
- } else if w > 0 && strings.ToLower(word) == word {
- // already all lowercase, and not the first word, so uppercase the first character.
- runes[w] = unicode.ToUpper(runes[w])
- }
- w = i
- }
- return string(runes)
-}
diff --git a/cmd/binapi-generator/definitions_test.go b/cmd/binapi-generator/definitions_test.go
deleted file mode 100644
index 30c85ae..0000000
--- a/cmd/binapi-generator/definitions_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package main
-
-import (
- "testing"
-)
-
-func TestInitialism(t *testing.T) {
- tests := []struct {
- name string
- input string
- expOutput string
- }{
- {name: "id", input: "id", expOutput: "ID"},
- {name: "ipv6", input: "is_ipv6", expOutput: "IsIPv6"},
- {name: "ip6", input: "is_ip6", expOutput: "IsIP6"},
- }
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- output := camelCaseName(test.input)
- if output != test.expOutput {
- t.Errorf("expected %q, got %q", test.expOutput, output)
- }
- })
- }
-}
diff --git a/cmd/binapi-generator/doc.go b/cmd/binapi-generator/doc.go
index d74d47b..9be49e2 100644
--- a/cmd/binapi-generator/doc.go
+++ b/cmd/binapi-generator/doc.go
@@ -1,4 +1,18 @@
-// Generator of Go structs out of the VPP binary API definitions in JSON format.
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Context of Go structs out of the VPP binary API definitions in JSON format.
//
// The JSON input can be specified as a single file (using the `input-file`
// CLI flag), or as a directory that will be scanned for all `.json` files
diff --git a/cmd/binapi-generator/generate.go b/cmd/binapi-generator/generate.go
deleted file mode 100644
index 715836d..0000000
--- a/cmd/binapi-generator/generate.go
+++ /dev/null
@@ -1,848 +0,0 @@
-// Copyright (c) 2017 Cisco and/or its affiliates.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at:
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
- "bytes"
- "fmt"
- "io"
- "os/exec"
- "path"
- "path/filepath"
- "sort"
- "strings"
- "unicode"
-)
-
-// generatedCodeVersion indicates a version of the generated code.
-// It is incremented whenever an incompatibility between the generated code and
-// GoVPP api package is introduced; the generated code references
-// a constant, api.GoVppAPIPackageIsVersionN (where N is generatedCodeVersion).
-const generatedCodeVersion = 1
-
-const (
- inputFileExt = ".api.json" // file extension of the VPP API files
- outputFileExt = ".ba.go" // file extension of the Go generated files
-
- constModuleName = "ModuleName" // module name constant
- constAPIVersion = "APIVersion" // API version constant
- constVersionCrc = "VersionCrc" // version CRC constant
-
- unionDataField = "XXX_UnionData" // name for the union data field
-
- serviceApiName = "RPCService" // name for the RPC service interface
- serviceImplName = "serviceClient" // name for the RPC service implementation
- serviceClientName = "ServiceClient" // name for the RPC service client
-)
-
-// context is a structure storing data for code generation
-type context struct {
- inputFile string // input file with VPP API in JSON
- outputFile string // output file with generated Go package
-
- importPrefix string // defines import path prefix for importing types
-
- inputData []byte // contents of the input file
-
- includeAPIVersion bool // include constant with API version string
- includeComments bool // include parts of original source in comments
- includeBinapiNames bool // include binary API names as struct tag
- includeServices bool // include service interface with client implementation
-
- moduleName string // name of the source VPP module
- packageName string // name of the Go package being generated
-
- packageData *Package // parsed package data
-}
-
-// newContext returns context details of the code generation task
-func newContext(inputFile, outputDir string) (*context, error) {
- if !strings.HasSuffix(inputFile, inputFileExt) {
- return nil, fmt.Errorf("invalid input file name: %q", inputFile)
- }
-
- ctx := &context{
- inputFile: inputFile,
- }
-
- // package name
- inputFileName := filepath.Base(inputFile)
- ctx.moduleName = inputFileName[:strings.Index(inputFileName, ".")]
-
- // alter package names for modules that are reserved keywords in Go
- switch ctx.moduleName {
- case "interface":
- ctx.packageName = "interfaces"
- case "map":
- ctx.packageName = "maps"
- default:
- ctx.packageName = ctx.moduleName
- }
-
- // output file
- packageDir := filepath.Join(outputDir, ctx.packageName)
- outputFileName := ctx.packageName + outputFileExt
- ctx.outputFile = filepath.Join(packageDir, outputFileName)
-
- return ctx, nil
-}
-
-func generatePackage(ctx *context, w io.Writer) error {
- logf("----------------------------")
- logf("generating package %q", ctx.packageName)
- logf("----------------------------")
-
- fmt.Fprintln(w, "// Code generated by GoVPP's binapi-generator. DO NOT EDIT.")
- fmt.Fprintf(w, "// source: %s\n", ctx.inputFile)
- fmt.Fprintln(w)
-
- generateHeader(ctx, w)
- generateImports(ctx, w)
-
- // generate module desc
- fmt.Fprintln(w, "const (")
- fmt.Fprintf(w, "\t// %s is the name of this module.\n", constModuleName)
- fmt.Fprintf(w, "\t%s = \"%s\"\n", constModuleName, ctx.moduleName)
-
- if ctx.includeAPIVersion {
- if ctx.packageData.Version != "" {
- fmt.Fprintf(w, "\t// %s is the API version of this module.\n", constAPIVersion)
- fmt.Fprintf(w, "\t%s = \"%s\"\n", constAPIVersion, ctx.packageData.Version)
- }
- fmt.Fprintf(w, "\t// %s is the CRC of this module.\n", constVersionCrc)
- fmt.Fprintf(w, "\t%s = %v\n", constVersionCrc, ctx.packageData.CRC)
- }
- fmt.Fprintln(w, ")")
- fmt.Fprintln(w)
-
- // generate enums
- if len(ctx.packageData.Enums) > 0 {
- for _, enum := range ctx.packageData.Enums {
- if imp, ok := ctx.packageData.Imports[enum.Name]; ok {
- generateImportedAlias(ctx, w, enum.Name, &imp)
- continue
- }
- generateEnum(ctx, w, &enum)
- }
- }
-
- // generate aliases
- if len(ctx.packageData.Aliases) > 0 {
- for _, alias := range ctx.packageData.Aliases {
- if imp, ok := ctx.packageData.Imports[alias.Name]; ok {
- generateImportedAlias(ctx, w, alias.Name, &imp)
- continue
- }
- generateAlias(ctx, w, &alias)
- }
- }
-
- // generate types
- if len(ctx.packageData.Types) > 0 {
- for _, typ := range ctx.packageData.Types {
- if imp, ok := ctx.packageData.Imports[typ.Name]; ok {
- generateImportedAlias(ctx, w, typ.Name, &imp)
- continue
- }
- generateType(ctx, w, &typ)
- }
- }
-
- // generate unions
- if len(ctx.packageData.Unions) > 0 {
- for _, union := range ctx.packageData.Unions {
- if imp, ok := ctx.packageData.Imports[union.Name]; ok {
- generateImportedAlias(ctx, w, union.Name, &imp)
- continue
- }
- generateUnion(ctx, w, &union)
- }
- }
-
- // generate messages
- if len(ctx.packageData.Messages) > 0 {
- for _, msg := range ctx.packageData.Messages {
- generateMessage(ctx, w, &msg)
- }
-
- // generate message registrations
- fmt.Fprintln(w, "func init() {")
- for _, msg := range ctx.packageData.Messages {
- name := camelCaseName(msg.Name)
- fmt.Fprintf(w, "\tapi.RegisterMessage((*%s)(nil), \"%s\")\n", name, ctx.moduleName+"."+name)
- }
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w)
-
- // generate list of messages
- fmt.Fprintf(w, "// Messages returns list of all messages in this module.\n")
- fmt.Fprintln(w, "func AllMessages() []api.Message {")
- fmt.Fprintln(w, "\treturn []api.Message{")
- for _, msg := range ctx.packageData.Messages {
- name := camelCaseName(msg.Name)
- fmt.Fprintf(w, "\t(*%s)(nil),\n", name)
- }
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w, "}")
- }
-
- if ctx.includeServices {
- // generate services
- if len(ctx.packageData.Services) > 0 {
- generateServices(ctx, w, ctx.packageData.Services)
- }
- }
-
- generateFooter(ctx, w)
-
- return nil
-}
-
-func generateHeader(ctx *context, w io.Writer) {
- fmt.Fprintln(w, "/*")
- fmt.Fprintf(w, "Package %s is a generated VPP binary API for '%s' module.\n", ctx.packageName, ctx.moduleName)
- fmt.Fprintln(w)
- fmt.Fprintln(w, "It consists of:")
- printObjNum := func(obj string, num int) {
- if num > 0 {
- if num > 1 {
- if strings.HasSuffix(obj, "s") {
-
- obj += "es"
- } else {
- obj += "s"
- }
- }
- fmt.Fprintf(w, "\t%3d %s\n", num, obj)
- }
- }
- printObjNum("enum", len(ctx.packageData.Enums))
- printObjNum("alias", len(ctx.packageData.Aliases))
- printObjNum("type", len(ctx.packageData.Types))
- printObjNum("union", len(ctx.packageData.Unions))
- printObjNum("message", len(ctx.packageData.Messages))
- printObjNum("service", len(ctx.packageData.Services))
- fmt.Fprintln(w, "*/")
- fmt.Fprintf(w, "package %s\n", ctx.packageName)
- fmt.Fprintln(w)
-
-}
-
-func generateImports(ctx *context, w io.Writer) {
- fmt.Fprintln(w, "import (")
- fmt.Fprintln(w, ` "bytes"`)
- fmt.Fprintln(w, ` "context"`)
- fmt.Fprintln(w, ` "io"`)
- fmt.Fprintln(w, ` "strconv"`)
- fmt.Fprintln(w)
- fmt.Fprintf(w, "\tapi \"%s\"\n", "git.fd.io/govpp.git/api")
- fmt.Fprintf(w, "\tstruc \"%s\"\n", "github.com/lunixbochs/struc")
- if len(ctx.packageData.Imports) > 0 {
- fmt.Fprintln(w)
- for _, imp := range getImports(ctx) {
- importPath := path.Join(ctx.importPrefix, imp)
- if importPath == "" {
- importPath = getImportPath(filepath.Dir(ctx.outputFile), imp)
- }
- fmt.Fprintf(w, "\t%s \"%s\"\n", imp, strings.TrimSpace(importPath))
- }
- }
- fmt.Fprintln(w, ")")
- fmt.Fprintln(w)
-}
-
-func getImportPath(outputDir string, pkg string) string {
- absPath, _ := filepath.Abs(filepath.Join(outputDir, "..", pkg))
- cmd := exec.Command("go", "list", absPath)
- var errbuf, outbuf bytes.Buffer
- cmd.Stdout = &outbuf
- cmd.Stderr = &errbuf
- if err := cmd.Run(); err != nil {
- fmt.Printf("ERR: %v\n", errbuf.String())
- panic(err)
- }
- return outbuf.String()
-}
-
-func getImports(ctx *context) (imports []string) {
- impmap := map[string]struct{}{}
- for _, imp := range ctx.packageData.Imports {
- if _, ok := impmap[imp.Package]; !ok {
- imports = append(imports, imp.Package)
- impmap[imp.Package] = struct{}{}
- }
- }
- sort.Strings(imports)
- return imports
-}
-
-func generateFooter(ctx *context, w io.Writer) {
- fmt.Fprintln(w, "// This is a compile-time assertion to ensure that this generated file")
- fmt.Fprintln(w, "// is compatible with the GoVPP api package it is being compiled against.")
- fmt.Fprintln(w, "// A compilation error at this line likely means your copy of the")
- fmt.Fprintln(w, "// GoVPP api package needs to be updated.")
- fmt.Fprintf(w, "const _ = api.GoVppAPIPackageIsVersion%d // please upgrade the GoVPP api package\n", generatedCodeVersion)
- fmt.Fprintln(w)
-
- fmt.Fprintf(w, "// Reference imports to suppress errors if they are not otherwise used.\n")
- fmt.Fprintf(w, "var _ = api.RegisterMessage\n")
- fmt.Fprintf(w, "var _ = bytes.NewBuffer\n")
- fmt.Fprintf(w, "var _ = context.Background\n")
- fmt.Fprintf(w, "var _ = io.Copy\n")
- fmt.Fprintf(w, "var _ = strconv.Itoa\n")
- fmt.Fprintf(w, "var _ = struc.Pack\n")
-}
-
-func generateComment(ctx *context, w io.Writer, goName string, vppName string, objKind string) {
- if objKind == "service" {
- fmt.Fprintf(w, "// %s represents RPC service API for %s module.\n", goName, ctx.moduleName)
- } else {
- fmt.Fprintf(w, "// %s represents VPP binary API %s '%s'.\n", goName, objKind, vppName)
- }
-
- if !ctx.includeComments {
- return
- }
-
- var isNotSpace = func(r rune) bool {
- return !unicode.IsSpace(r)
- }
-
- // print out the source of the generated object
- mapType := false
- objFound := false
- objTitle := fmt.Sprintf(`"%s",`, vppName)
- switch objKind {
- case "alias", "service":
- objTitle = fmt.Sprintf(`"%s": {`, vppName)
- mapType = true
- }
-
- inputBuff := bytes.NewBuffer(ctx.inputData)
- inputLine := 0
-
- var trimIndent string
- var indent int
- for {
- line, err := inputBuff.ReadString('\n')
- if err != nil {
- break
- }
- inputLine++
-
- noSpaceAt := strings.IndexFunc(line, isNotSpace)
- if !objFound {
- indent = strings.Index(line, objTitle)
- if indent == -1 {
- continue
- }
- trimIndent = line[:indent]
- // If no other non-whitespace character then we are at the message header.
- if trimmed := strings.TrimSpace(line); trimmed == objTitle {
- objFound = true
- fmt.Fprintln(w, "//")
- }
- } else if noSpaceAt < indent {
- break // end of the definition in JSON for array types
- } else if objFound && mapType && noSpaceAt <= indent {
- fmt.Fprintf(w, "//\t%s", strings.TrimPrefix(line, trimIndent))
- break // end of the definition in JSON for map types (aliases, services)
- }
- fmt.Fprintf(w, "//\t%s", strings.TrimPrefix(line, trimIndent))
- }
-
- fmt.Fprintln(w, "//")
-}
-
-func generateEnum(ctx *context, w io.Writer, enum *Enum) {
- name := camelCaseName(enum.Name)
- typ := binapiTypes[enum.Type]
-
- logf(" writing enum %q (%s) with %d entries", enum.Name, name, len(enum.Entries))
-
- // generate enum comment
- generateComment(ctx, w, name, enum.Name, "enum")
-
- // generate enum definition
- fmt.Fprintf(w, "type %s %s\n", name, typ)
- fmt.Fprintln(w)
-
- // generate enum entries
- fmt.Fprintln(w, "const (")
- for _, entry := range enum.Entries {
- fmt.Fprintf(w, "\t%s %s = %v\n", entry.Name, name, entry.Value)
- }
- fmt.Fprintln(w, ")")
- fmt.Fprintln(w)
-
- // generate enum conversion maps
- fmt.Fprintf(w, "var %s_name = map[%s]string{\n", name, typ)
- for _, entry := range enum.Entries {
- fmt.Fprintf(w, "\t%v: \"%s\",\n", entry.Value, entry.Name)
- }
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w)
-
- fmt.Fprintf(w, "var %s_value = map[string]%s{\n", name, typ)
- for _, entry := range enum.Entries {
- fmt.Fprintf(w, "\t\"%s\": %v,\n", entry.Name, entry.Value)
- }
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w)
-
- fmt.Fprintf(w, "func (x %s) String() string {\n", name)
- fmt.Fprintf(w, "\ts, ok := %s_name[%s(x)]\n", name, typ)
- fmt.Fprintf(w, "\tif ok { return s }\n")
- fmt.Fprintf(w, "\treturn strconv.Itoa(int(x))\n")
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w)
-}
-
-func generateImportedAlias(ctx *context, w io.Writer, tName string, imp *Import) {
- name := camelCaseName(tName)
-
- fmt.Fprintf(w, "type %s = %s.%s\n", name, imp.Package, name)
-
- fmt.Fprintln(w)
-}
-
-func generateAlias(ctx *context, w io.Writer, alias *Alias) {
- name := camelCaseName(alias.Name)
-
- logf(" writing type %q (%s), length: %d", alias.Name, name, alias.Length)
-
- // generate struct comment
- generateComment(ctx, w, name, alias.Name, "alias")
-
- // generate struct definition
- fmt.Fprintf(w, "type %s ", name)
-
- if alias.Length > 0 {
- fmt.Fprintf(w, "[%d]", alias.Length)
- }
-
- dataType := convertToGoType(ctx, alias.Type)
- fmt.Fprintf(w, "%s\n", dataType)
-
- fmt.Fprintln(w)
-}
-
-func generateUnion(ctx *context, w io.Writer, union *Union) {
- name := camelCaseName(union.Name)
-
- logf(" writing union %q (%s) with %d fields", union.Name, name, len(union.Fields))
-
- // generate struct comment
- generateComment(ctx, w, name, union.Name, "union")
-
- // generate struct definition
- fmt.Fprintln(w, "type", name, "struct {")
-
- // maximum size for union
- maxSize := getUnionSize(ctx, union)
-
- // generate data field
- fmt.Fprintf(w, "\t%s [%d]byte\n", unionDataField, maxSize)
-
- // generate end of the struct
- fmt.Fprintln(w, "}")
-
- // generate name getter
- generateTypeNameGetter(w, name, union.Name)
-
- // generate CRC getter
- if union.CRC != "" {
- generateCrcGetter(w, name, union.CRC)
- }
-
- // generate getters for fields
- for _, field := range union.Fields {
- fieldName := camelCaseName(field.Name)
- fieldType := convertToGoType(ctx, field.Type)
- generateUnionGetterSetter(w, name, fieldName, fieldType)
- }
-
- // generate union methods
- //generateUnionMethods(w, name)
-
- fmt.Fprintln(w)
-}
-
-// generateUnionMethods generates methods that implement struc.Custom
-// interface to allow having XXX_uniondata field unexported
-// TODO: do more testing when unions are actually used in some messages
-/*func generateUnionMethods(w io.Writer, structName string) {
- // generate struc.Custom implementation for union
- fmt.Fprintf(w, `
-func (u *%[1]s) Pack(p []byte, opt *struc.Options) (int, error) {
- var b = new(bytes.Buffer)
- if err := struc.PackWithOptions(b, u.union_data, opt); err != nil {
- return 0, err
- }
- copy(p, b.Bytes())
- return b.Len(), nil
-}
-func (u *%[1]s) Unpack(r io.Reader, length int, opt *struc.Options) error {
- return struc.UnpackWithOptions(r, u.union_data[:], opt)
-}
-func (u *%[1]s) Size(opt *struc.Options) int {
- return len(u.union_data)
-}
-func (u *%[1]s) String() string {
- return string(u.union_data[:])
-}
-`, structName)
-}*/
-
-func generateUnionGetterSetter(w io.Writer, structName string, getterField, getterStruct string) {
- fmt.Fprintf(w, `
-func %[1]s%[2]s(a %[3]s) (u %[1]s) {
- u.Set%[2]s(a)
- return
-}
-func (u *%[1]s) Set%[2]s(a %[3]s) {
- var b = new(bytes.Buffer)
- if err := struc.Pack(b, &a); err != nil {
- return
- }
- copy(u.%[4]s[:], b.Bytes())
-}
-func (u *%[1]s) Get%[2]s() (a %[3]s) {
- var b = bytes.NewReader(u.%[4]s[:])
- struc.Unpack(b, &a)
- return
-}
-`, structName, getterField, getterStruct, unionDataField)
-}
-
-func generateType(ctx *context, w io.Writer, typ *Type) {
- name := camelCaseName(typ.Name)
-
- logf(" writing type %q (%s) with %d fields", typ.Name, name, len(typ.Fields))
-
- // generate struct comment
- generateComment(ctx, w, name, typ.Name, "type")
-
- // generate struct definition
- fmt.Fprintf(w, "type %s struct {\n", name)
-
- // generate struct fields
- for i, field := range typ.Fields {
- // skip internal fields
- switch strings.ToLower(field.Name) {
- case crcField, msgIdField:
- continue
- }
-
- generateField(ctx, w, typ.Fields, i)
- }
-
- // generate end of the struct
- fmt.Fprintln(w, "}")
-
- // generate name getter
- generateTypeNameGetter(w, name, typ.Name)
-
- // generate CRC getter
- if typ.CRC != "" {
- generateCrcGetter(w, name, typ.CRC)
- }
-
- fmt.Fprintln(w)
-}
-
-func generateMessage(ctx *context, w io.Writer, msg *Message) {
- name := camelCaseName(msg.Name)
-
- logf(" writing message %q (%s) with %d fields", msg.Name, name, len(msg.Fields))
-
- // generate struct comment
- generateComment(ctx, w, name, msg.Name, "message")
-
- // generate struct definition
- fmt.Fprintf(w, "type %s struct {", name)
-
- msgType := otherMessage
- wasClientIndex := false
-
- // generate struct fields
- n := 0
- for i, field := range msg.Fields {
- if i == 1 {
- if field.Name == clientIndexField {
- // "client_index" as the second member,
- // this might be an event message or a request
- msgType = eventMessage
- wasClientIndex = true
- } else if field.Name == contextField {
- // reply needs "context" as the second member
- msgType = replyMessage
- }
- } else if i == 2 {
- if wasClientIndex && field.Name == contextField {
- // request needs "client_index" as the second member
- // and "context" as the third member
- msgType = requestMessage
- }
- }
-
- // skip internal fields
- switch strings.ToLower(field.Name) {
- case crcField, msgIdField:
- continue
- case clientIndexField, contextField:
- if n == 0 {
- continue
- }
- }
- n++
- if n == 1 {
- fmt.Fprintln(w)
- }
-
- generateField(ctx, w, msg.Fields, i)
- }
-
- // generate end of the struct
- fmt.Fprintln(w, "}")
-
- // generate message methods
- generateMessageResetMethod(w, name)
- generateMessageNameGetter(w, name, msg.Name)
- generateCrcGetter(w, name, msg.CRC)
- generateMessageTypeGetter(w, name, msgType)
-
- fmt.Fprintln(w)
-}
-
-func generateField(ctx *context, w io.Writer, fields []Field, i int) {
- field := fields[i]
-
- fieldName := strings.TrimPrefix(field.Name, "_")
- fieldName = camelCaseName(fieldName)
-
- dataType := convertToGoType(ctx, field.Type)
- fieldType := dataType
-
- // generate length field for strings
- if field.Type == "string" && field.Length == 0 {
- fmt.Fprintf(w, "\tXXX_%sLen uint32 `struc:\"sizeof=%s\"`\n", fieldName, fieldName)
- }
-
- // check if it is array
- if field.Length > 0 || field.SizeFrom != "" {
- if dataType == "uint8" {
- dataType = "byte"
- }
- if dataType == "string" && field.SpecifiedLen {
- fieldType = "string"
- dataType = "byte"
- } else {
- fieldType = "[]" + dataType
- }
- }
- fmt.Fprintf(w, "\t%s %s", fieldName, fieldType)
-
- fieldTags := map[string]string{}
-
- if field.Length > 0 && field.SpecifiedLen {
- // fixed size array
- fieldTags["struc"] = fmt.Sprintf("[%d]%s", field.Length, dataType)
- } else {
- for _, f := range fields {
- if f.SizeFrom == field.Name {
- // variable sized array
- sizeOfName := camelCaseName(f.Name)
- fieldTags["struc"] = fmt.Sprintf("sizeof=%s", sizeOfName)
- }
- }
- }
-
- if ctx.includeBinapiNames {
- fieldTags["binapi"] = field.Name
- }
- if field.Meta.Limit > 0 {
- fieldTags["binapi"] = fmt.Sprintf("%s,limit=%d", fieldTags["binapi"], field.Meta.Limit)
- }
-
- if len(fieldTags) > 0 {
- fmt.Fprintf(w, "\t`")
- var keys []string
- for k := range fieldTags {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- var n int
- for _, tt := range keys {
- t, ok := fieldTags[tt]
- if !ok {
- continue
- }
- if n > 0 {
- fmt.Fprintf(w, " ")
- }
- n++
- fmt.Fprintf(w, `%s:"%s"`, tt, t)
- }
- fmt.Fprintf(w, "`")
- }
-
- fmt.Fprintln(w)
-}
-
-func generateMessageResetMethod(w io.Writer, structName string) {
- fmt.Fprintf(w, "func (m *%[1]s) Reset() { *m = %[1]s{} }\n", structName)
-}
-
-func generateMessageNameGetter(w io.Writer, structName, msgName string) {
- fmt.Fprintf(w, "func (*%s) GetMessageName() string { return %q }\n", structName, msgName)
-}
-
-func generateTypeNameGetter(w io.Writer, structName, msgName string) {
- fmt.Fprintf(w, "func (*%s) GetTypeName() string { return %q }\n", structName, msgName)
-}
-
-func generateCrcGetter(w io.Writer, structName, crc string) {
- crc = strings.TrimPrefix(crc, "0x")
- fmt.Fprintf(w, "func (*%s) GetCrcString() string { return %q }\n", structName, crc)
-}
-
-func generateMessageTypeGetter(w io.Writer, structName string, msgType MessageType) {
- fmt.Fprintf(w, "func (*"+structName+") GetMessageType() api.MessageType {")
- if msgType == requestMessage {
- fmt.Fprintf(w, "\treturn api.RequestMessage")
- } else if msgType == replyMessage {
- fmt.Fprintf(w, "\treturn api.ReplyMessage")
- } else if msgType == eventMessage {
- fmt.Fprintf(w, "\treturn api.EventMessage")
- } else {
- fmt.Fprintf(w, "\treturn api.OtherMessage")
- }
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w)
-}
-
-func generateServices(ctx *context, w io.Writer, services []Service) {
-
- // generate services comment
- generateComment(ctx, w, serviceApiName, "services", "service")
-
- // generate service api
- fmt.Fprintf(w, "type %s interface {\n", serviceApiName)
- for _, svc := range services {
- generateServiceMethod(ctx, w, &svc)
- fmt.Fprintln(w)
- }
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w)
-
- // generate client implementation
- fmt.Fprintf(w, "type %s struct {\n", serviceImplName)
- fmt.Fprintf(w, "\tch api.Channel\n")
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w)
-
- // generate client constructor
- fmt.Fprintf(w, "func New%s(ch api.Channel) %s {\n", serviceClientName, serviceApiName)
- fmt.Fprintf(w, "\treturn &%s{ch}\n", serviceImplName)
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w)
-
- for _, svc := range services {
- method := camelCaseName(svc.RequestType)
- if m := strings.TrimSuffix(method, "Dump"); method != m {
- method = "Dump" + m
- }
-
- fmt.Fprintf(w, "func (c *%s) ", serviceImplName)
- generateServiceMethod(ctx, w, &svc)
- fmt.Fprintln(w, " {")
- if svc.Stream {
- streamImpl := fmt.Sprintf("%s_%sClient", serviceImplName, method)
- fmt.Fprintf(w, "\tstream := c.ch.SendMultiRequest(in)\n")
- fmt.Fprintf(w, "\tx := &%s{stream}\n", streamImpl)
- fmt.Fprintf(w, "\treturn x, nil\n")
- } else if replyTyp := camelCaseName(svc.ReplyType); replyTyp != "" {
- fmt.Fprintf(w, "\tout := new(%s)\n", replyTyp)
- fmt.Fprintf(w, "\terr:= c.ch.SendRequest(in).ReceiveReply(out)\n")
- fmt.Fprintf(w, "\tif err != nil { return nil, err }\n")
- fmt.Fprintf(w, "\treturn out, nil\n")
- } else {
- fmt.Fprintf(w, "\tc.ch.SendRequest(in)\n")
- fmt.Fprintf(w, "\treturn nil\n")
- }
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w)
-
- if svc.Stream {
- replyTyp := camelCaseName(svc.ReplyType)
- method := camelCaseName(svc.RequestType)
- if m := strings.TrimSuffix(method, "Dump"); method != m {
- method = "Dump" + m
- }
- streamApi := fmt.Sprintf("%s_%sClient", serviceApiName, method)
-
- fmt.Fprintf(w, "type %s interface {\n", streamApi)
- fmt.Fprintf(w, "\tRecv() (*%s, error)\n", replyTyp)
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w)
-
- streamImpl := fmt.Sprintf("%s_%sClient", serviceImplName, method)
- fmt.Fprintf(w, "type %s struct {\n", streamImpl)
- fmt.Fprintf(w, "\tapi.MultiRequestCtx\n")
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w)
-
- fmt.Fprintf(w, "func (c *%s) Recv() (*%s, error) {\n", streamImpl, replyTyp)
- fmt.Fprintf(w, "\tm := new(%s)\n", replyTyp)
- fmt.Fprintf(w, "\tstop, err := c.MultiRequestCtx.ReceiveReply(m)\n")
- fmt.Fprintf(w, "\tif err != nil { return nil, err }\n")
- fmt.Fprintf(w, "\tif stop { return nil, io.EOF }\n")
- fmt.Fprintf(w, "\treturn m, nil\n")
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w)
- }
- }
-
- fmt.Fprintln(w)
-}
-
-func generateServiceMethod(ctx *context, w io.Writer, svc *Service) {
- reqTyp := camelCaseName(svc.RequestType)
-
- // method name is same as parameter type name by default
- method := reqTyp
- if svc.Stream {
- // use Dump as prefix instead of suffix for stream services
- if m := strings.TrimSuffix(method, "Dump"); method != m {
- method = "Dump" + m
- }
- }
-
- params := fmt.Sprintf("in *%s", reqTyp)
- returns := "error"
-
- if replyType := camelCaseName(svc.ReplyType); replyType != "" {
- var replyTyp string
- if svc.Stream {
- replyTyp = fmt.Sprintf("%s_%sClient", serviceApiName, method)
- } else {
- replyTyp = fmt.Sprintf("*%s", replyType)
- }
- returns = fmt.Sprintf("(%s, error)", replyTyp)
- }
-
- fmt.Fprintf(w, "\t%s(ctx context.Context, %s) %s", method, params, returns)
-}
diff --git a/cmd/binapi-generator/generate_test.go b/cmd/binapi-generator/generate_test.go
deleted file mode 100644
index 4bec874..0000000
--- a/cmd/binapi-generator/generate_test.go
+++ /dev/null
@@ -1,450 +0,0 @@
-// Copyright (c) 2017 Cisco and/or its affiliates.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at:
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
- "bufio"
- "io/ioutil"
- "os"
- "testing"
-
- . "github.com/onsi/gomega"
-)
-
-func TestGetInputFiles(t *testing.T) {
- RegisterTestingT(t)
- result, err := getInputFiles("testdata", 1)
- Expect(err).ShouldNot(HaveOccurred())
- Expect(result).To(HaveLen(3))
- for _, file := range result {
- Expect(file).To(BeAnExistingFile())
- }
-}
-
-func TestGetInputFilesError(t *testing.T) {
- RegisterTestingT(t)
- result, err := getInputFiles("nonexisting_directory", 1)
- Expect(err).Should(HaveOccurred())
- Expect(result).To(BeNil())
-}
-
-func TestGenerateFromFile(t *testing.T) {
- RegisterTestingT(t)
- outDir := "test_output_directory"
- // remove directory created during test
- defer os.RemoveAll(outDir)
- err := generateFromFile("testdata/acl.api.json", outDir, nil)
- Expect(err).ShouldNot(HaveOccurred())
- fileInfo, err := os.Stat(outDir + "/acl/acl.ba.go")
- Expect(err).ShouldNot(HaveOccurred())
- Expect(fileInfo.IsDir()).To(BeFalse())
- Expect(fileInfo.Name()).To(BeEquivalentTo("acl.ba.go"))
-}
-
-func TestGenerateFromFileInputError(t *testing.T) {
- RegisterTestingT(t)
- outDir := "test_output_directory"
- err := generateFromFile("testdata/nonexisting.json", outDir, nil)
- Expect(err).Should(HaveOccurred())
- Expect(err.Error()).To(ContainSubstring("invalid input file name"))
-}
-
-func TestGenerateFromFileReadJsonError(t *testing.T) {
- RegisterTestingT(t)
- outDir := "test_output_directory"
- err := generateFromFile("testdata/input-read-json-error.json", outDir, nil)
- Expect(err).Should(HaveOccurred())
- Expect(err.Error()).To(ContainSubstring("invalid input file name"))
-}
-
-func TestGenerateFromFileGeneratePackageError(t *testing.T) {
- RegisterTestingT(t)
- outDir := "test_output_directory"
- // generate package throws panic, recover after it
- defer func() {
- if recovery := recover(); recovery != nil {
- t.Logf("Recovered from panic: %v", recovery)
- }
- os.RemoveAll(outDir)
- }()
- err := generateFromFile("testdata/input-generate-error.json", outDir, nil)
- Expect(err).Should(HaveOccurred())
-}
-
-func TestGetContext(t *testing.T) {
- RegisterTestingT(t)
- outDir := "test_output_directory"
- result, err := newContext("testdata/af_packet.api.json", outDir)
- Expect(err).ShouldNot(HaveOccurred())
- Expect(result).ToNot(BeNil())
- Expect(result.outputFile).To(BeEquivalentTo(outDir + "/af_packet/af_packet.ba.go"))
-}
-
-func TestGetContextNoJsonFile(t *testing.T) {
- RegisterTestingT(t)
- outDir := "test_output_directory"
- result, err := newContext("testdata/input.txt", outDir)
- Expect(err).Should(HaveOccurred())
- Expect(err.Error()).To(ContainSubstring("invalid input file name"))
- Expect(result).To(BeNil())
-}
-
-func TestGetContextInterfaceJson(t *testing.T) {
- RegisterTestingT(t)
- outDir := "test_output_directory"
- result, err := newContext("testdata/ip.api.json", outDir)
- Expect(err).ShouldNot(HaveOccurred())
- Expect(result).ToNot(BeNil())
- Expect(result.outputFile)
- Expect(result.outputFile).To(BeEquivalentTo(outDir + "/ip/ip.ba.go"))
-}
-
-func TestReadJson(t *testing.T) {
- RegisterTestingT(t)
- inputData, err := ioutil.ReadFile("testdata/af_packet.api.json")
- Expect(err).ShouldNot(HaveOccurred())
- result, err := parseInputJSON(inputData)
- Expect(err).ShouldNot(HaveOccurred())
- Expect(result).ToNot(BeNil())
- Expect(result.Len()).To(BeEquivalentTo(5))
-}
-
-func TestReadJsonError(t *testing.T) {
- RegisterTestingT(t)
- inputData, err := ioutil.ReadFile("testdata/input-read-json-error.json")
- Expect(err).ShouldNot(HaveOccurred())
- result, err := parseInputJSON(inputData)
- Expect(err).Should(HaveOccurred())
- Expect(result).To(BeNil())
-}
-
-func TestGeneratePackage(t *testing.T) {
- RegisterTestingT(t)
- // prepare context
- testCtx := new(context)
- testCtx.packageName = "test-package-name"
-
- // prepare input/output output files
- inputData, err := ioutil.ReadFile("testdata/ip.api.json")
- Expect(err).ShouldNot(HaveOccurred())
- jsonRoot, err := parseInputJSON(inputData)
- Expect(err).ShouldNot(HaveOccurred())
- testCtx.packageData, err = parsePackage(testCtx, jsonRoot)
- Expect(err).ShouldNot(HaveOccurred())
- outDir := "test_output_directory"
- outFile, err := os.Create(outDir)
- Expect(err).ShouldNot(HaveOccurred())
- defer os.RemoveAll(outDir)
-
- // prepare writer
- writer := bufio.NewWriter(outFile)
- Expect(writer.Buffered()).To(BeZero())
- err = generatePackage(testCtx, writer)
- Expect(err).ShouldNot(HaveOccurred())
-}
-
-func TestGenerateMessageType(t *testing.T) {
- RegisterTestingT(t)
- // prepare context
- testCtx := new(context)
- testCtx.packageName = "test-package-name"
-
- // prepare input/output output files
- inputData, err := ioutil.ReadFile("testdata/ip.api.json")
- Expect(err).ShouldNot(HaveOccurred())
- jsonRoot, err := parseInputJSON(inputData)
- Expect(err).ShouldNot(HaveOccurred())
- outDir := "test_output_directory"
- outFile, err := os.Create(outDir)
- Expect(err).ShouldNot(HaveOccurred())
- testCtx.packageData, err = parsePackage(testCtx, jsonRoot)
- Expect(err).ShouldNot(HaveOccurred())
- defer os.RemoveAll(outDir)
-
- // prepare writer
- writer := bufio.NewWriter(outFile)
-
- for _, msg := range testCtx.packageData.Messages {
- generateMessage(testCtx, writer, &msg)
- Expect(writer.Buffered()).ToNot(BeZero())
- }
-}
-
-/*func TestGenerateMessageName(t *testing.T) {
- RegisterTestingT(t)
- // prepare context
- testCtx := new(context)
- testCtx.packageName = "test-package-name"
-
- // prepare input/output output files
- inputData, err := readFile("testdata/ip.api.json")
- Expect(err).ShouldNot(HaveOccurred())
- testCtx.inputBuff = bytes.NewBuffer(inputData)
- inFile, _ := parseJSON(inputData)
- outDir := "test_output_directory"
- outFile, err := os.Create(outDir)
- Expect(err).ShouldNot(HaveOccurred())
- defer os.RemoveAll(outDir)
-
- // prepare writer
- writer := bufio.NewWriter(outFile)
-
- types := inFile.Map("types")
- Expect(types.Len()).To(BeEquivalentTo(1))
- for i := 0; i < types.Len(); i++ {
- typ := types.At(i)
- Expect(writer.Buffered()).To(BeZero())
- err := generateMessage(testCtx, writer, typ, false)
- Expect(err).ShouldNot(HaveOccurred())
- Expect(writer.Buffered()).ToNot(BeZero())
-
- }
-}
-
-func TestGenerateMessageFieldTypes(t *testing.T) {
- // expected results according to acl.api.json in testdata
- expectedTypes := []string{
- "\tIsPermit uint8",
- "\tIsIpv6 uint8",
- "\tSrcIPAddr []byte `struc:\"[16]byte\"`",
- "\tSrcIPPrefixLen uint8",
- "\tDstIPAddr []byte `struc:\"[16]byte\"`",
- "\tDstIPPrefixLen uint8",
- "\tProto uint8",
- "\tSrcportOrIcmptypeFirst uint16",
- "\tSrcportOrIcmptypeLast uint16",
- "\tDstportOrIcmpcodeFirst uint16",
- "\tDstportOrIcmpcodeLast uint16",
- "\tTCPFlagsMask uint8",
- "\tTCPFlagsValue uint8"}
- RegisterTestingT(t)
- // prepare context
- testCtx := new(context)
- testCtx.packageName = "test-package-name"
-
- // prepare input/output output files
- inputData, err := readFile("testdata/acl.api.json")
- Expect(err).ShouldNot(HaveOccurred())
- inFile, err := parseJSON(inputData)
- Expect(err).ShouldNot(HaveOccurred())
- Expect(inFile).ToNot(BeNil())
-
- // test types
- types := inFile.Map("types")
- fields := make([]string, 0)
- for i := 0; i < types.Len(); i++ {
- for j := 0; j < types.At(i).Len(); j++ {
- field := types.At(i).At(j)
- if field.GetType() == jsongo.TypeArray {
- err := processMessageField(testCtx, &fields, field, false)
- Expect(err).ShouldNot(HaveOccurred())
- Expect(fields[j-1]).To(BeEquivalentTo(expectedTypes[j-1]))
- }
- }
- }
-}
-
-func TestGenerateMessageFieldMessages(t *testing.T) {
- // expected results according to acl.api.json in testdata
- expectedFields := []string{"\tMajor uint32", "\tMinor uint32", "\tRetval int32",
- "\tVpePid uint32", "\tACLIndex uint32", "\tTag []byte `struc:\"[64]byte\"`",
- "\tCount uint32"}
- RegisterTestingT(t)
- // prepare context
- testCtx := new(context)
- testCtx.packageName = "test-package-name"
-
- // prepare input/output output files
- inputData, err := readFile("testdata/acl.api.json")
- Expect(err).ShouldNot(HaveOccurred())
- inFile, err := parseJSON(inputData)
- Expect(err).ShouldNot(HaveOccurred())
- Expect(inFile).ToNot(BeNil())
-
- // test message fields
- messages := inFile.Map("messages")
- customIndex := 0
- fields := make([]string, 0)
- for i := 0; i < messages.Len(); i++ {
- for j := 0; j < messages.At(i).Len(); j++ {
- field := messages.At(i).At(j)
- if field.GetType() == jsongo.TypeArray {
- specificFieldName := field.At(1).Get().(string)
- if specificFieldName == "crc" || specificFieldName == "_vl_msg_id" ||
- specificFieldName == "client_index" || specificFieldName == "context" {
- continue
- }
- err := processMessageField(testCtx, &fields, field, false)
- Expect(err).ShouldNot(HaveOccurred())
- Expect(fields[customIndex]).To(BeEquivalentTo(expectedFields[customIndex]))
- customIndex++
- if customIndex >= len(expectedFields) {
- // there is too much fields now for one UT...
- return
- }
- }
- }
- }
-}
-
-func TestGeneratePackageHeader(t *testing.T) {
- RegisterTestingT(t)
- // prepare context
- testCtx := new(context)
- testCtx.packageName = "test-package-name"
-
- // prepare input/output output files
- inputData, err := readFile("testdata/acl.api.json")
- Expect(err).ShouldNot(HaveOccurred())
- inFile, err := parseJSON(inputData)
- Expect(err).ShouldNot(HaveOccurred())
- outDir := "test_output_directory"
- outFile, err := os.Create(outDir)
- Expect(err).ShouldNot(HaveOccurred())
- defer os.RemoveAll(outDir)
- // prepare writer
- writer := bufio.NewWriter(outFile)
- Expect(writer.Buffered()).To(BeZero())
- generateHeader(testCtx, writer, inFile)
- Expect(writer.Buffered()).ToNot(BeZero())
-}
-
-func TestGenerateMessageCommentType(t *testing.T) {
- RegisterTestingT(t)
- // prepare context
- testCtx := new(context)
- testCtx.packageName = "test-package-name"
- testCtx.inputBuff = bytes.NewBuffer([]byte("test content"))
-
- outDir := "test_output_directory"
- outFile, err := os.Create(outDir)
- Expect(err).ShouldNot(HaveOccurred())
- writer := bufio.NewWriter(outFile)
- defer os.RemoveAll(outDir)
- Expect(writer.Buffered()).To(BeZero())
- generateMessageComment(testCtx, writer, "test-struct", "msg-name", true)
- Expect(writer.Buffered()).ToNot(BeZero())
-}
-
-func TestGenerateMessageCommentMessage(t *testing.T) {
- RegisterTestingT(t)
- // prepare context
- testCtx := new(context)
- testCtx.packageName = "test-package-name"
- testCtx.inputBuff = bytes.NewBuffer([]byte("test content"))
-
- outDir := "test_output_directory"
- outFile, err := os.Create(outDir)
- Expect(err).ShouldNot(HaveOccurred())
- writer := bufio.NewWriter(outFile)
- defer os.RemoveAll(outDir)
- Expect(writer.Buffered()).To(BeZero())
- generateMessageComment(testCtx, writer, "test-struct", "msg-name", false)
- Expect(writer.Buffered()).ToNot(BeZero())
-}
-
-func TestGenerateMessageNameGetter(t *testing.T) {
- RegisterTestingT(t)
- outDir := "test_output_directory"
- outFile, err := os.Create(outDir)
- Expect(err).ShouldNot(HaveOccurred())
- writer := bufio.NewWriter(outFile)
- defer os.RemoveAll(outDir)
- Expect(writer.Buffered()).To(BeZero())
- generateMessageNameGetter(writer, "test-struct", "msg-name")
- Expect(writer.Buffered()).ToNot(BeZero())
-}
-
-func TestGenerateTypeNameGetter(t *testing.T) {
- RegisterTestingT(t)
- outDir := "test_output_directory"
- outFile, err := os.Create(outDir)
- Expect(err).ShouldNot(HaveOccurred())
- writer := bufio.NewWriter(outFile)
- defer os.RemoveAll(outDir)
- Expect(writer.Buffered()).To(BeZero())
- generateTypeNameGetter(writer, "test-struct", "msg-name")
- Expect(writer.Buffered()).ToNot(BeZero())
-}
-
-func TestGenerateCrcGetter(t *testing.T) {
- RegisterTestingT(t)
- outDir := "test_output_directory"
- outFile, err := os.Create(outDir)
- Expect(err).ShouldNot(HaveOccurred())
- writer := bufio.NewWriter(outFile)
- defer os.RemoveAll(outDir)
- Expect(writer.Buffered()).To(BeZero())
- generateCrcGetter(writer, "test-struct", "msg-name")
- Expect(writer.Buffered()).ToNot(BeZero())
-}
-
-func TestTranslateVppType(t *testing.T) {
- RegisterTestingT(t)
- context := new(context)
- typesToTranslate := []string{"u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "f64"}
- expected := []string{"uint8", "int8", "uint16", "int16", "uint32", "int32", "uint64", "int64", "float64"}
- var translated []string
- for _, value := range typesToTranslate {
- translated = append(translated, convertToGoType(context, value, false))
- }
- for index, value := range expected {
- Expect(value).To(BeEquivalentTo(translated[index]))
- }
-
-}
-
-func TestTranslateVppTypeArray(t *testing.T) {
- RegisterTestingT(t)
- context := new(context)
- translated := convertToGoType(context, "u8", true)
- Expect(translated).To(BeEquivalentTo("byte"))
-}
-
-func TestTranslateVppUnknownType(t *testing.T) {
- defer func() {
- if recovery := recover(); recovery != nil {
- t.Logf("Recovered from panic: %v", recovery)
- }
- }()
- context := new(context)
- convertToGoType(context, "?", false)
-}
-
-func TestCamelCase(t *testing.T) {
- RegisterTestingT(t)
- // test camel case functionality
- expected := "allYourBaseAreBelongToUs"
- result := camelCaseName("all_your_base_are_belong_to_us")
- Expect(expected).To(BeEquivalentTo(result))
- // test underscore
- expected = "_"
- result = camelCaseName(expected)
- Expect(expected).To(BeEquivalentTo(result))
- // test all lower
- expected = "lower"
- result = camelCaseName(expected)
- Expect(expected).To(BeEquivalentTo(result))
-}
-
-func TestCommonInitialisms(t *testing.T) {
- RegisterTestingT(t)
-
- for key, value := range commonInitialisms {
- Expect(value).ShouldNot(BeFalse())
- Expect(key).ShouldNot(BeEmpty())
- }
-}
-*/
diff --git a/cmd/binapi-generator/main.go b/cmd/binapi-generator/main.go
index fcd85ae..c0bdbb9 100644
--- a/cmd/binapi-generator/main.go
+++ b/cmd/binapi-generator/main.go
@@ -15,324 +15,102 @@
package main
import (
- "bytes"
- "encoding/json"
"flag"
"fmt"
- "go/format"
- "io/ioutil"
"os"
- "path/filepath"
- "strings"
- "github.com/bennyscetbun/jsongo"
"github.com/sirupsen/logrus"
+ "git.fd.io/govpp.git/binapigen"
+ "git.fd.io/govpp.git/binapigen/vppapi"
"git.fd.io/govpp.git/version"
)
-var (
- theInputFile = flag.String("input-file", "", "Input file with VPP API in JSON format.")
- theInputTypes = flag.String("input-types", "", "Types input file with VPP API in JSON format. (split by comma)")
- theInputDir = flag.String("input-dir", "/usr/share/vpp/api", "Input directory with VPP API files in JSON format.")
- theOutputDir = flag.String("output-dir", ".", "Output directory where package folders will be generated.")
-
- includeAPIVer = flag.Bool("include-apiver", true, "Include APIVersion constant for each module.")
- includeServices = flag.Bool("include-services", true, "Include RPC service api and client implementation.")
- includeComments = flag.Bool("include-comments", false, "Include JSON API source in comments for each object.")
- includeBinapiNames = flag.Bool("include-binapi-names", false, "Include binary API names in struct tag.")
- importPrefix = flag.String("import-prefix", "", "Define import path prefix to be used to import types.")
-
- continueOnError = flag.Bool("continue-onerror", false, "Continue with next file on error.")
- debugMode = flag.Bool("debug", os.Getenv("GOVPP_DEBUG") != "", "Enable debug mode.")
-
- printVersion = flag.Bool("version", false, "Prints current version and exits.")
-)
+func init() {
+ flag.Usage = func() {
+ fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [OPTION]... [API]...\n", os.Args[0])
+ fmt.Fprintln(flag.CommandLine.Output(), "Generate code for each API.")
+ fmt.Fprintf(flag.CommandLine.Output(), "Example: %s -output-dir=binapi acl interface l2\n", os.Args[0])
+ fmt.Fprintln(flag.CommandLine.Output())
+ fmt.Fprintln(flag.CommandLine.Output(), "Options:")
+ flag.CommandLine.PrintDefaults()
+ }
+}
func main() {
+ var (
+ theInputFile = flag.String("input-file", "", "Input VPP API file. (DEPRECATED: Use program arguments to define VPP API files)")
+ theApiDir = flag.String("input-dir", vppapi.DefaultAPIDir, "Directory with VPP API files.")
+ theOutputDir = flag.String("output-dir", ".", "Output directory where code will be generated.")
+
+ importPrefix = flag.String("import-prefix", "", "Define import path prefix to be used to import types.")
+ importTypes = flag.Bool("import-types", false, "Generate packages for imported types.")
+ includeAPIVer = flag.Bool("include-apiver", true, "Include APIVersion constant for each module.")
+ includeServices = flag.Bool("include-services", true, "Include RPC service api and client implementation.")
+ includeComments = flag.Bool("include-comments", false, "Include JSON API source in comments for each object.")
+ includeBinapiNames = flag.Bool("include-binapi-names", true, "Include binary API names in struct tag.")
+ includeVppVersion = flag.Bool("include-vpp-version", true, "Include version of the VPP that provided input files.")
+
+ debugMode = flag.Bool("debug", os.Getenv("DEBUG_GOVPP") != "", "Enable debug mode.")
+ printVersion = flag.Bool("version", false, "Prints version and exits.")
+ )
flag.Parse()
- if flag.NArg() > 1 {
- flag.Usage()
- os.Exit(1)
- }
-
- if flag.NArg() > 0 {
- switch cmd := flag.Arg(0); cmd {
- case "version":
- fmt.Fprintln(os.Stdout, version.Verbose())
- os.Exit(0)
-
- default:
- fmt.Fprintf(os.Stderr, "unknown command: %s\n", cmd)
- flag.Usage()
- os.Exit(2)
- }
- }
-
if *printVersion {
fmt.Fprintln(os.Stdout, version.Info())
os.Exit(0)
}
- if *debugMode {
- logrus.SetLevel(logrus.DebugLevel)
- logrus.Info("debug mode enabled")
- }
-
- if err := run(*theInputFile, *theInputDir, *theOutputDir, *continueOnError); err != nil {
- logrus.Errorln("binapi-generator:", err)
- os.Exit(1)
+ if flag.NArg() == 1 && flag.Arg(0) == "version" {
+ fmt.Fprintln(os.Stdout, version.Verbose())
+ os.Exit(0)
}
-}
-func run(inputFile, inputDir string, outputDir string, continueErr bool) (err error) {
- if inputFile == "" && inputDir == "" {
- return fmt.Errorf("input-file or input-dir must be specified")
- }
+ var opts binapigen.Options
- var typesPkgs []*context
- if *theInputTypes != "" {
- types := strings.Split(*theInputTypes, ",")
- typesPkgs, err = loadTypesPackages(types...)
- if err != nil {
- return fmt.Errorf("loading types input failed: %v", err)
- }
- }
-
- if inputFile != "" {
- // process one input file
- if err := generateFromFile(inputFile, outputDir, typesPkgs); err != nil {
- return fmt.Errorf("code generation from %s failed: %v\n", inputFile, err)
+ if *theInputFile != "" {
+ if flag.NArg() > 0 {
+ fmt.Fprintln(os.Stderr, "input-file cannot be combined with files to generate in arguments")
+ os.Exit(1)
}
+ opts.FilesToGenerate = append(opts.FilesToGenerate, *theInputFile)
} else {
- // process all files in specified directory
- dir, err := filepath.Abs(inputDir)
- if err != nil {
- return fmt.Errorf("invalid input directory: %v\n", err)
- }
- files, err := getInputFiles(inputDir, 1)
- if err != nil {
- return fmt.Errorf("problem getting files from input directory: %v\n", err)
- } else if len(files) == 0 {
- return fmt.Errorf("no input files found in input directory: %v\n", dir)
- }
- for _, file := range files {
- if err := generateFromFile(file, outputDir, typesPkgs); err != nil {
- if continueErr {
- logrus.Warnf("code generation from %s failed: %v (error ignored)\n", file, err)
- continue
- } else {
- return fmt.Errorf("code generation from %s failed: %v\n", file, err)
- }
- }
- }
- }
-
- return nil
-}
-
-// getInputFiles returns all input files located in specified directory
-func getInputFiles(inputDir string, deep int) (files []string, err error) {
- entries, err := ioutil.ReadDir(inputDir)
- if err != nil {
- return nil, fmt.Errorf("reading directory %s failed: %v", inputDir, err)
- }
- for _, e := range entries {
- if e.IsDir() && deep > 0 {
- nestedDir := filepath.Join(inputDir, e.Name())
- if nested, err := getInputFiles(nestedDir, deep-1); err != nil {
- return nil, err
- } else {
- files = append(files, nested...)
- }
- } else if strings.HasSuffix(e.Name(), inputFileExt) {
- files = append(files, filepath.Join(inputDir, e.Name()))
- }
- }
- return files, nil
-}
-
-func parseInputJSON(inputData []byte) (*jsongo.Node, error) {
- jsonRoot := new(jsongo.Node)
- if err := json.Unmarshal(inputData, jsonRoot); err != nil {
- return nil, fmt.Errorf("unmarshalling JSON failed: %v", err)
- }
- return jsonRoot, nil
-}
-
-// generateFromFile generates Go package from one input JSON file
-func generateFromFile(inputFile, outputDir string, typesPkgs []*context) error {
- // create generator context
- ctx, err := newContext(inputFile, outputDir)
- if err != nil {
- return err
+ opts.FilesToGenerate = append(opts.FilesToGenerate, flag.Args()...)
}
- logf("------------------------------------------------------------")
- logf("module: %s", ctx.moduleName)
- logf(" - input: %s", ctx.inputFile)
- logf(" - output: %s", ctx.outputFile)
- logf("------------------------------------------------------------")
-
// prepare options
- ctx.includeAPIVersion = *includeAPIVer
- ctx.includeComments = *includeComments
- ctx.includeBinapiNames = *includeBinapiNames
- ctx.includeServices = *includeServices
- ctx.importPrefix = *importPrefix
-
- // read API definition from input file
- ctx.inputData, err = ioutil.ReadFile(ctx.inputFile)
- if err != nil {
- return fmt.Errorf("reading input file %s failed: %v", ctx.inputFile, err)
- }
- // parse JSON data into objects
- jsonRoot, err := parseInputJSON(ctx.inputData)
- if err != nil {
- return fmt.Errorf("parsing JSON input failed: %v", err)
- }
- ctx.packageData, err = parsePackage(ctx, jsonRoot)
- if err != nil {
- return fmt.Errorf("parsing package %s failed: %v", ctx.packageName, err)
- }
-
- if len(typesPkgs) > 0 {
- err = loadTypeAliases(ctx, typesPkgs)
- if err != nil {
- return fmt.Errorf("loading type aliases failed: %v", err)
- }
- }
-
- // generate Go package
- var buf bytes.Buffer
- if err := generatePackage(ctx, &buf); err != nil {
- return fmt.Errorf("generating Go package for %s failed: %v", ctx.packageName, err)
- }
- // format generated source code
- gosrc, err := format.Source(buf.Bytes())
- if err != nil {
- return fmt.Errorf("formatting source code for package %s failed: %v", ctx.packageName, err)
+ if ver := os.Getenv("VPP_API_VERSION"); ver != "" {
+ // use version from env var if set
+ opts.VPPVersion = ver
+ } else {
+ opts.VPPVersion = ResolveVppVersion(*theApiDir)
}
+ opts.IncludeAPIVersion = *includeAPIVer
+ opts.IncludeComments = *includeComments
+ opts.IncludeBinapiNames = *includeBinapiNames
+ opts.IncludeServices = *includeServices
+ opts.IncludeVppVersion = *includeVppVersion
+ opts.ImportPrefix = *importPrefix
+ opts.ImportTypes = *importTypes
- // create output directory
- packageDir := filepath.Dir(ctx.outputFile)
- if err := os.MkdirAll(packageDir, 0775); err != nil {
- return fmt.Errorf("creating output dir %s failed: %v", packageDir, err)
- }
- // write generated code to output file
- if err := ioutil.WriteFile(ctx.outputFile, gosrc, 0666); err != nil {
- return fmt.Errorf("writing to output file %s failed: %v", ctx.outputFile, err)
+ if *debugMode {
+ logrus.SetLevel(logrus.DebugLevel)
+ logrus.Debug("debug mode enabled")
}
- return nil
-}
+ apiDir := *theApiDir
+ outputDir := *theOutputDir
-func loadTypesPackages(types ...string) ([]*context, error) {
- var ctxs []*context
- for _, inputFile := range types {
- // create generator context
- ctx, err := newContext(inputFile, "")
- if err != nil {
- return nil, err
- }
- // read API definition from input file
- ctx.inputData, err = ioutil.ReadFile(ctx.inputFile)
- if err != nil {
- return nil, fmt.Errorf("reading input file %s failed: %v", ctx.inputFile, err)
- }
- // parse JSON data into objects
- jsonRoot, err := parseInputJSON(ctx.inputData)
- if err != nil {
- return nil, fmt.Errorf("parsing JSON input failed: %v", err)
- }
- ctx.packageData, err = parsePackage(ctx, jsonRoot)
- if err != nil {
- return nil, fmt.Errorf("parsing package %s failed: %v", ctx.packageName, err)
- }
- ctxs = append(ctxs, ctx)
- }
- return ctxs, nil
-}
-
-func loadTypeAliases(ctx *context, typesCtxs []*context) error {
- for _, t := range ctx.packageData.Types {
- for _, c := range typesCtxs {
- if _, ok := ctx.packageData.Imports[t.Name]; ok {
- break
- }
- for _, at := range c.packageData.Types {
- if at.Name != t.Name {
- continue
- }
- if len(at.Fields) != len(t.Fields) {
- continue
- }
- ctx.packageData.Imports[t.Name] = Import{
- Package: c.packageName,
- }
- }
- }
- }
- for _, t := range ctx.packageData.Aliases {
- for _, c := range typesCtxs {
- if _, ok := ctx.packageData.Imports[t.Name]; ok {
- break
- }
- for _, at := range c.packageData.Aliases {
- if at.Name != t.Name {
- continue
- }
- if at.Length != t.Length {
- continue
- }
- if at.Type != t.Type {
- continue
- }
- ctx.packageData.Imports[t.Name] = Import{
- Package: c.packageName,
- }
- }
- }
- }
- for _, t := range ctx.packageData.Enums {
- for _, c := range typesCtxs {
- if _, ok := ctx.packageData.Imports[t.Name]; ok {
- break
+ binapigen.Run(apiDir, opts, func(g *binapigen.Generator) error {
+ for _, file := range g.Files {
+ if !file.Generate {
+ continue
}
- for _, at := range c.packageData.Enums {
- if at.Name != t.Name {
- continue
- }
- if at.Type != t.Type {
- continue
- }
- ctx.packageData.Imports[t.Name] = Import{
- Package: c.packageName,
- }
+ binapigen.GenerateBinapiFile(g, file, outputDir)
+ if g.IncludeServices && file.Service != nil {
+ binapigen.GenerateRPC(g, file, outputDir)
}
}
- }
- for _, t := range ctx.packageData.Unions {
- for _, c := range typesCtxs {
- if _, ok := ctx.packageData.Imports[t.Name]; ok {
- break
- }
- for _, at := range c.packageData.Unions {
- if at.Name != t.Name {
- continue
- }
- ctx.packageData.Imports[t.Name] = Import{
- Package: c.packageName,
- }
- }
- }
- }
- return nil
-}
-
-func logf(f string, v ...interface{}) {
- if *debugMode {
- logrus.Debugf(f, v...)
- }
+ return nil
+ })
}
diff --git a/cmd/binapi-generator/objects.go b/cmd/binapi-generator/objects.go
deleted file mode 100644
index 9871abc..0000000
--- a/cmd/binapi-generator/objects.go
+++ /dev/null
@@ -1,139 +0,0 @@
-package main
-
-import "fmt"
-
-// Package represents collection of objects parsed from VPP binary API JSON data
-type Package struct {
- Name string
- Version string
- CRC string
- Services []Service
- Enums []Enum
- Aliases []Alias
- Types []Type
- Unions []Union
- Messages []Message
- RefMap map[string]string
- Imports map[string]Import
-}
-
-type Import struct {
- Package string
-}
-
-// Service represents VPP binary API service
-type Service struct {
- Name string
- RequestType string
- ReplyType string
- Stream bool
- Events []string
-}
-
-// Enum represents VPP binary API enum
-type Enum struct {
- Name string
- Type string
- Entries []EnumEntry
-}
-
-// EnumEntry represents VPP binary API enum entry
-type EnumEntry struct {
- Name string
- Value interface{}
-}
-
-// Alias represents VPP binary API alias
-type Alias struct {
- Name string
- Type string
- Length int
-}
-
-// Type represents VPP binary API type
-type Type struct {
- Name string
- CRC string
- Fields []Field
-}
-
-// Field represents VPP binary API object field
-type Field struct {
- Name string
- Type string
- Length int
- SpecifiedLen bool
- SizeFrom string
- Meta FieldMeta
-}
-
-// FieldMeta represents VPP binary API meta info for field
-type FieldMeta struct {
- Limit int
- Default string
-}
-
-// Union represents VPP binary API union
-type Union struct {
- Name string
- CRC string
- Fields []Field
-}
-
-// Message represents VPP binary API message
-type Message struct {
- Name string
- CRC string
- Fields []Field
-}
-
-// MessageType represents the type of a VPP message
-type MessageType int
-
-const (
- requestMessage MessageType = iota // VPP request message
- replyMessage // VPP reply message
- eventMessage // VPP event message
- otherMessage // other VPP message
-)
-
-// printPackage prints all loaded objects for package
-func printPackage(pkg *Package) {
- logf("package: %s %s (%s)", pkg.Name, pkg.Version, pkg.CRC)
- if len(pkg.Enums) > 0 {
- logf(" %d enums:", len(pkg.Enums))
- for _, enum := range pkg.Enums {
- logf(" - %s: %+v", enum.Name, enum)
- }
- }
- if len(pkg.Unions) > 0 {
- logf(" %d unions:", len(pkg.Unions))
- for _, union := range pkg.Unions {
- logf(" - %s: %+v", union.Name, union)
- }
- }
- if len(pkg.Types) > 0 {
- logf(" %d types:", len(pkg.Types))
- for _, typ := range pkg.Types {
- logf(" - %s (%d fields): %+v", typ.Name, len(typ.Fields), typ)
- }
- }
- if len(pkg.Messages) > 0 {
- logf(" %d messages:", len(pkg.Messages))
- for _, msg := range pkg.Messages {
- logf(" - %s (%d fields) %s", msg.Name, len(msg.Fields), msg.CRC)
- }
- }
- if len(pkg.Services) > 0 {
- logf(" %d services:", len(pkg.Services))
- for _, svc := range pkg.Services {
- var info string
- if svc.Stream {
- info = "(STREAM)"
- } else if len(svc.Events) > 0 {
- info = fmt.Sprintf("(EVENTS: %v)", svc.Events)
- }
- logf(" - %s: %q -> %q %s", svc.Name, svc.RequestType, svc.ReplyType, info)
- }
- }
-}
diff --git a/cmd/binapi-generator/parse.go b/cmd/binapi-generator/parse.go
deleted file mode 100644
index 6598b7b..0000000
--- a/cmd/binapi-generator/parse.go
+++ /dev/null
@@ -1,557 +0,0 @@
-// Copyright (c) 2018 Cisco and/or its affiliates.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at:
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
- "errors"
- "fmt"
- "sort"
- "strings"
-
- "github.com/bennyscetbun/jsongo"
- "github.com/sirupsen/logrus"
-)
-
-// top level objects
-const (
- objTypes = "types"
- objMessages = "messages"
- objUnions = "unions"
- objEnums = "enums"
- objServices = "services"
- objAliases = "aliases"
- vlAPIVersion = "vl_api_version"
- objOptions = "options"
-)
-
-// various object fields
-const (
- crcField = "crc"
- msgIdField = "_vl_msg_id"
-
- clientIndexField = "client_index"
- contextField = "context"
-
- aliasLengthField = "length"
- aliasTypeField = "type"
-
- replyField = "reply"
- streamField = "stream"
- eventsField = "events"
-)
-
-// service name parts
-const (
- serviceEventPrefix = "want_"
- serviceDumpSuffix = "_dump"
- serviceDetailsSuffix = "_details"
- serviceReplySuffix = "_reply"
- serviceNoReply = "null"
-)
-
-// field meta info
-const (
- fieldMetaLimit = "limit"
- fieldMetaDefault = "default"
-)
-
-// module options
-const (
- versionOption = "version"
-)
-
-// parsePackage parses provided JSON data into objects prepared for code generation
-func parsePackage(ctx *context, jsonRoot *jsongo.Node) (*Package, error) {
- pkg := Package{
- Name: ctx.packageName,
- RefMap: make(map[string]string),
- Imports: map[string]Import{},
- }
-
- // parse CRC for API version
- if crc := jsonRoot.At(vlAPIVersion); crc.GetType() == jsongo.TypeValue {
- pkg.CRC = crc.Get().(string)
- }
-
- // parse version string
- if opt := jsonRoot.Map(objOptions); opt.GetType() == jsongo.TypeMap {
- if ver := opt.Map(versionOption); ver.GetType() == jsongo.TypeValue {
- pkg.Version = ver.Get().(string)
- }
- }
-
- logf("parsing package %s (version: %s, CRC: %s)", pkg.Name, pkg.Version, pkg.CRC)
- logf(" consists of:")
- for _, key := range jsonRoot.GetKeys() {
- logf(" - %d %s", jsonRoot.At(key).Len(), key)
- }
-
- // parse enums
- enums := jsonRoot.Map(objEnums)
- pkg.Enums = make([]Enum, 0)
- for i := 0; i < enums.Len(); i++ {
- enumNode := enums.At(i)
-
- enum, err := parseEnum(ctx, enumNode)
- if err != nil {
- return nil, err
- }
-
- enumApi := toApiType(enum.Name)
- if _, ok := pkg.RefMap[enumApi]; ok {
- logf("enum %v already known", enumApi)
- continue
- }
- pkg.RefMap[enumApi] = enum.Name
- pkg.Enums = append(pkg.Enums, *enum)
- }
- // sort enums
- sort.SliceStable(pkg.Enums, func(i, j int) bool {
- return pkg.Enums[i].Name < pkg.Enums[j].Name
- })
-
- // parse aliases
- aliases := jsonRoot.Map(objAliases)
- if aliases.GetType() == jsongo.TypeMap {
- pkg.Aliases = make([]Alias, 0)
- for _, key := range aliases.GetKeys() {
- aliasNode := aliases.At(key)
-
- alias, err := parseAlias(ctx, key.(string), aliasNode)
- if err != nil {
- return nil, err
- }
-
- aliasApi := toApiType(alias.Name)
- if _, ok := pkg.RefMap[aliasApi]; ok {
- logf("alias %v already known", aliasApi)
- continue
- }
- pkg.RefMap[aliasApi] = alias.Name
- pkg.Aliases = append(pkg.Aliases, *alias)
- }
- }
- // sort aliases to ensure consistent order
- sort.Slice(pkg.Aliases, func(i, j int) bool {
- return pkg.Aliases[i].Name < pkg.Aliases[j].Name
- })
-
- // parse types
- types := jsonRoot.Map(objTypes)
- pkg.Types = make([]Type, 0)
- for i := 0; i < types.Len(); i++ {
- typNode := types.At(i)
-
- typ, err := parseType(ctx, typNode)
- if err != nil {
- return nil, err
- }
-
- typApi := toApiType(typ.Name)
- if _, ok := pkg.RefMap[typApi]; ok {
- logf("type %v already known", typApi)
- continue
- }
- pkg.RefMap[typApi] = typ.Name
- pkg.Types = append(pkg.Types, *typ)
- }
- // sort types
- sort.SliceStable(pkg.Types, func(i, j int) bool {
- return pkg.Types[i].Name < pkg.Types[j].Name
- })
-
- // parse unions
- unions := jsonRoot.Map(objUnions)
- pkg.Unions = make([]Union, 0)
- for i := 0; i < unions.Len(); i++ {
- unionNode := unions.At(i)
-
- union, err := parseUnion(ctx, unionNode)
- if err != nil {
- return nil, err
- }
-
- unionApi := toApiType(union.Name)
- if _, ok := pkg.RefMap[unionApi]; ok {
- logf("union %v already known", unionApi)
- continue
- }
- pkg.RefMap[unionApi] = union.Name
- pkg.Unions = append(pkg.Unions, *union)
- }
- // sort unions
- sort.SliceStable(pkg.Unions, func(i, j int) bool {
- return pkg.Unions[i].Name < pkg.Unions[j].Name
- })
-
- // parse messages
- messages := jsonRoot.Map(objMessages)
- pkg.Messages = make([]Message, messages.Len())
- for i := 0; i < messages.Len(); i++ {
- msgNode := messages.At(i)
-
- msg, err := parseMessage(ctx, msgNode)
- if err != nil {
- return nil, err
- }
- pkg.Messages[i] = *msg
- }
- // sort messages
- sort.SliceStable(pkg.Messages, func(i, j int) bool {
- return pkg.Messages[i].Name < pkg.Messages[j].Name
- })
-
- // parse services
- services := jsonRoot.Map(objServices)
- if services.GetType() == jsongo.TypeMap {
- pkg.Services = make([]Service, services.Len())
- for i, key := range services.GetKeys() {
- svcNode := services.At(key)
-
- svc, err := parseService(ctx, key.(string), svcNode)
- if err != nil {
- return nil, err
- }
- pkg.Services[i] = *svc
- }
- }
- // sort services
- sort.Slice(pkg.Services, func(i, j int) bool {
- // dumps first
- if pkg.Services[i].Stream != pkg.Services[j].Stream {
- return pkg.Services[i].Stream
- }
- return pkg.Services[i].RequestType < pkg.Services[j].RequestType
- })
-
- printPackage(&pkg)
-
- return &pkg, nil
-}
-
-// parseEnum parses VPP binary API enum object from JSON node
-func parseEnum(ctx *context, enumNode *jsongo.Node) (*Enum, error) {
- if enumNode.Len() == 0 || enumNode.At(0).GetType() != jsongo.TypeValue {
- return nil, errors.New("invalid JSON for enum specified")
- }
-
- enumName, ok := enumNode.At(0).Get().(string)
- if !ok {
- return nil, fmt.Errorf("enum name is %T, not a string", enumNode.At(0).Get())
- }
- enumType, ok := enumNode.At(enumNode.Len() - 1).At("enumtype").Get().(string)
- if !ok {
- return nil, fmt.Errorf("enum type invalid or missing")
- }
-
- enum := Enum{
- Name: enumName,
- Type: enumType,
- }
-
- // loop through enum entries, skip first (name) and last (enumtype)
- for j := 1; j < enumNode.Len()-1; j++ {
- if enumNode.At(j).GetType() == jsongo.TypeArray {
- entry := enumNode.At(j)
-
- if entry.Len() < 2 || entry.At(0).GetType() != jsongo.TypeValue || entry.At(1).GetType() != jsongo.TypeValue {
- return nil, errors.New("invalid JSON for enum entry specified")
- }
-
- entryName, ok := entry.At(0).Get().(string)
- if !ok {
- return nil, fmt.Errorf("enum entry name is %T, not a string", entry.At(0).Get())
- }
- entryVal := entry.At(1).Get()
-
- enum.Entries = append(enum.Entries, EnumEntry{
- Name: entryName,
- Value: entryVal,
- })
- }
- }
-
- return &enum, nil
-}
-
-// parseUnion parses VPP binary API union object from JSON node
-func parseUnion(ctx *context, unionNode *jsongo.Node) (*Union, error) {
- if unionNode.Len() == 0 || unionNode.At(0).GetType() != jsongo.TypeValue {
- return nil, errors.New("invalid JSON for union specified")
- }
-
- unionName, ok := unionNode.At(0).Get().(string)
- if !ok {
- return nil, fmt.Errorf("union name is %T, not a string", unionNode.At(0).Get())
- }
- var unionCRC string
- if unionNode.At(unionNode.Len()-1).GetType() == jsongo.TypeMap {
- unionCRC = unionNode.At(unionNode.Len() - 1).At(crcField).Get().(string)
- }
-
- union := Union{
- Name: unionName,
- CRC: unionCRC,
- }
-
- // loop through union fields, skip first (name)
- for j := 1; j < unionNode.Len(); j++ {
- if unionNode.At(j).GetType() == jsongo.TypeArray {
- fieldNode := unionNode.At(j)
-
- field, err := parseField(ctx, fieldNode)
- if err != nil {
- return nil, err
- }
-
- union.Fields = append(union.Fields, *field)
- }
- }
-
- return &union, nil
-}
-
-// parseType parses VPP binary API type object from JSON node
-func parseType(ctx *context, typeNode *jsongo.Node) (*Type, error) {
- if typeNode.Len() == 0 || typeNode.At(0).GetType() != jsongo.TypeValue {
- return nil, errors.New("invalid JSON for type specified")
- }
-
- typeName, ok := typeNode.At(0).Get().(string)
- if !ok {
- return nil, fmt.Errorf("type name is %T, not a string", typeNode.At(0).Get())
- }
- var typeCRC string
- if lastField := typeNode.At(typeNode.Len() - 1); lastField.GetType() == jsongo.TypeMap {
- typeCRC = lastField.At(crcField).Get().(string)
- }
-
- typ := Type{
- Name: typeName,
- CRC: typeCRC,
- }
-
- // loop through type fields, skip first (name)
- for j := 1; j < typeNode.Len(); j++ {
- if typeNode.At(j).GetType() == jsongo.TypeArray {
- fieldNode := typeNode.At(j)
-
- field, err := parseField(ctx, fieldNode)
- if err != nil {
- return nil, err
- }
-
- typ.Fields = append(typ.Fields, *field)
- }
- }
-
- return &typ, nil
-}
-
-// parseAlias parses VPP binary API alias object from JSON node
-func parseAlias(ctx *context, aliasName string, aliasNode *jsongo.Node) (*Alias, error) {
- if aliasNode.Len() == 0 || aliasNode.At(aliasTypeField).GetType() != jsongo.TypeValue {
- return nil, errors.New("invalid JSON for alias specified")
- }
-
- alias := Alias{
- Name: aliasName,
- }
-
- if typeNode := aliasNode.At(aliasTypeField); typeNode.GetType() == jsongo.TypeValue {
- typ, ok := typeNode.Get().(string)
- if !ok {
- return nil, fmt.Errorf("alias type is %T, not a string", typeNode.Get())
- }
- if typ != "null" {
- alias.Type = typ
- }
- }
-
- if lengthNode := aliasNode.At(aliasLengthField); lengthNode.GetType() == jsongo.TypeValue {
- length, ok := lengthNode.Get().(float64)
- if !ok {
- return nil, fmt.Errorf("alias length is %T, not a float64", lengthNode.Get())
- }
- alias.Length = int(length)
- }
-
- return &alias, nil
-}
-
-// parseMessage parses VPP binary API message object from JSON node
-func parseMessage(ctx *context, msgNode *jsongo.Node) (*Message, error) {
- if msgNode.Len() == 0 || msgNode.At(0).GetType() != jsongo.TypeValue {
- return nil, errors.New("invalid JSON for message specified")
- }
-
- msgName, ok := msgNode.At(0).Get().(string)
- if !ok {
- return nil, fmt.Errorf("message name is %T, not a string", msgNode.At(0).Get())
- }
- msgCRC, ok := msgNode.At(msgNode.Len() - 1).At(crcField).Get().(string)
- if !ok {
-
- return nil, fmt.Errorf("message crc invalid or missing")
- }
-
- msg := Message{
- Name: msgName,
- CRC: msgCRC,
- }
-
- // loop through message fields, skip first (name) and last (crc)
- for j := 1; j < msgNode.Len()-1; j++ {
- if msgNode.At(j).GetType() == jsongo.TypeArray {
- fieldNode := msgNode.At(j)
-
- field, err := parseField(ctx, fieldNode)
- if err != nil {
- return nil, err
- }
-
- msg.Fields = append(msg.Fields, *field)
- }
- }
-
- return &msg, nil
-}
-
-// parseField parses VPP binary API object field from JSON node
-func parseField(ctx *context, field *jsongo.Node) (*Field, error) {
- if field.Len() < 2 || field.At(0).GetType() != jsongo.TypeValue || field.At(1).GetType() != jsongo.TypeValue {
- return nil, errors.New("invalid JSON for field specified")
- }
-
- fieldType, ok := field.At(0).Get().(string)
- if !ok {
- return nil, fmt.Errorf("field type is %T, not a string", field.At(0).Get())
- }
- fieldName, ok := field.At(1).Get().(string)
- if !ok {
- return nil, fmt.Errorf("field name is %T, not a string", field.At(1).Get())
- }
-
- f := &Field{
- Name: fieldName,
- Type: fieldType,
- }
-
- if field.Len() >= 3 {
- switch field.At(2).GetType() {
- case jsongo.TypeValue:
- fieldLength, ok := field.At(2).Get().(float64)
- if !ok {
- return nil, fmt.Errorf("field length is %T, not float64", field.At(2).Get())
- }
- f.Length = int(fieldLength)
- f.SpecifiedLen = true
-
- case jsongo.TypeMap:
- fieldMeta := field.At(2)
-
- for _, key := range fieldMeta.GetKeys() {
- metaNode := fieldMeta.At(key)
-
- switch metaName := key.(string); metaName {
- case fieldMetaLimit:
- f.Meta.Limit = int(metaNode.Get().(float64))
- case fieldMetaDefault:
- f.Meta.Default = fmt.Sprint(metaNode.Get())
- default:
- logrus.Warnf("unknown meta info (%s) for field (%s)", metaName, fieldName)
- }
- }
- default:
- return nil, errors.New("invalid JSON for field specified")
- }
- }
- if field.Len() >= 4 {
- fieldLengthFrom, ok := field.At(3).Get().(string)
- if !ok {
- return nil, fmt.Errorf("field length from is %T, not a string", field.At(3).Get())
- }
- f.SizeFrom = fieldLengthFrom
- }
-
- return f, nil
-}
-
-// parseService parses VPP binary API service object from JSON node
-func parseService(ctx *context, svcName string, svcNode *jsongo.Node) (*Service, error) {
- if svcNode.Len() == 0 || svcNode.At(replyField).GetType() != jsongo.TypeValue {
- return nil, errors.New("invalid JSON for service specified")
- }
-
- svc := Service{
- Name: svcName,
- RequestType: svcName,
- }
-
- if replyNode := svcNode.At(replyField); replyNode.GetType() == jsongo.TypeValue {
- reply, ok := replyNode.Get().(string)
- if !ok {
- return nil, fmt.Errorf("service reply is %T, not a string", replyNode.Get())
- }
- if reply != serviceNoReply {
- svc.ReplyType = reply
- }
- }
-
- // stream service (dumps)
- if streamNode := svcNode.At(streamField); streamNode.GetType() == jsongo.TypeValue {
- var ok bool
- svc.Stream, ok = streamNode.Get().(bool)
- if !ok {
- return nil, fmt.Errorf("service stream is %T, not a string", streamNode.Get())
- }
- }
-
- // events service (event subscription)
- if eventsNode := svcNode.At(eventsField); eventsNode.GetType() == jsongo.TypeArray {
- for j := 0; j < eventsNode.Len(); j++ {
- event := eventsNode.At(j).Get().(string)
- svc.Events = append(svc.Events, event)
- }
- }
-
- // validate service
- if len(svc.Events) > 0 {
- // EVENT service
- if !strings.HasPrefix(svc.RequestType, serviceEventPrefix) {
- logrus.Debugf("unusual EVENTS service: %+v\n"+
- "- events service %q does not have %q prefix in request.",
- svc, svc.Name, serviceEventPrefix)
- }
- } else if svc.Stream {
- // STREAM service
- if !strings.HasSuffix(svc.RequestType, serviceDumpSuffix) ||
- !strings.HasSuffix(svc.ReplyType, serviceDetailsSuffix) {
- logrus.Debugf("unusual STREAM service: %+v\n"+
- "- stream service %q does not have %q suffix in request or reply does not have %q suffix.",
- svc, svc.Name, serviceDumpSuffix, serviceDetailsSuffix)
- }
- } else if svc.ReplyType != "" && svc.ReplyType != serviceNoReply {
- // REQUEST service
- // some messages might have `null` reply (for example: memclnt)
- if !strings.HasSuffix(svc.ReplyType, serviceReplySuffix) {
- logrus.Debugf("unusual REQUEST service: %+v\n"+
- "- service %q does not have %q suffix in reply.",
- svc, svc.Name, serviceReplySuffix)
- }
- }
-
- return &svc, nil
-}
diff --git a/cmd/binapi-generator/parse_test.go b/cmd/binapi-generator/parse_test.go
deleted file mode 100644
index e145979..0000000
--- a/cmd/binapi-generator/parse_test.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package main
-
-import (
- "testing"
-)
-
-func TestBinapiTypeSizes(t *testing.T) {
- tests := []struct {
- name string
- input string
- expsize int
- }{
- {name: "basic1", input: "u8", expsize: 1},
- {name: "basic2", input: "i8", expsize: 1},
- {name: "basic3", input: "u16", expsize: 2},
- {name: "basic4", input: "i32", expsize: 4},
- {name: "invalid1", input: "x", expsize: -1},
- }
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- size := getBinapiTypeSize(test.input)
- if size != test.expsize {
- t.Errorf("expected %d, got %d", test.expsize, size)
- }
- })
- }
-}
-
-func TestSizeOfType(t *testing.T) {
- tests := []struct {
- name string
- input Type
- expsize int
- }{
- {
- name: "basic1",
- input: Type{
- Fields: []Field{
- {Type: "u8"},
- },
- },
- expsize: 1,
- },
- {
- name: "basic2",
- input: Type{
- Fields: []Field{
- {Type: "u8", Length: 4},
- },
- },
- expsize: 4,
- },
- {
- name: "basic3",
- input: Type{
- Fields: []Field{
- {Type: "u8", Length: 16},
- },
- },
- expsize: 16,
- },
- {
- name: "withEnum",
- input: Type{
- Fields: []Field{
- {Type: "u16"},
- {Type: "vl_api_myenum_t"},
- },
- },
- expsize: 6,
- },
- {
- name: "invalid1",
- input: Type{
- Fields: []Field{
- {Type: "x", Length: 16},
- },
- },
- expsize: 0,
- },
- }
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- ctx := &context{
- packageData: &Package{
- Enums: []Enum{
- {Name: "myenum", Type: "u32"},
- },
- },
- }
- size := getSizeOfType(ctx, &test.input)
- if size != test.expsize {
- t.Errorf("expected %d, got %d", test.expsize, size)
- }
- })
- }
-}
diff --git a/cmd/binapi-generator/testdata/acl.api.json b/cmd/binapi-generator/testdata/acl.api.json
deleted file mode 100644
index 4c6653c..0000000
--- a/cmd/binapi-generator/testdata/acl.api.json
+++ /dev/null
@@ -1,929 +0,0 @@
-{
- "services": [
- {
- "acl_interface_add_del": {
- "reply": "acl_interface_add_del_reply"
- }
- },
- {
- "acl_del": {
- "reply": "acl_del_reply"
- }
- },
- {
- "macip_acl_del": {
- "reply": "macip_acl_del_reply"
- }
- },
- {
- "acl_plugin_get_version": {
- "reply": "acl_plugin_get_version_reply"
- }
- },
- {
- "macip_acl_interface_add_del": {
- "reply": "macip_acl_interface_add_del_reply"
- }
- },
- {
- "acl_interface_set_acl_list": {
- "reply": "acl_interface_set_acl_list_reply"
- }
- },
- {
- "acl_dump": {
- "reply": "acl_details",
- "stream": true
- }
- },
- {
- "acl_interface_list_dump": {
- "reply": "acl_interface_list_details",
- "stream": true
- }
- },
- {
- "macip_acl_interface_list_dump": {
- "reply": "macip_acl_interface_list_details",
- "stream": true
- }
- },
- {
- "acl_add_replace": {
- "reply": "acl_add_replace_reply"
- }
- },
- {
- "acl_plugin_control_ping": {
- "reply": "acl_plugin_control_ping_reply"
- }
- },
- {
- "macip_acl_interface_get": {
- "reply": "macip_acl_interface_get_reply"
- }
- },
- {
- "macip_acl_add": {
- "reply": "macip_acl_add_reply"
- }
- },
- {
- "macip_acl_add_replace": {
- "reply": "macip_acl_add_replace_reply"
- }
- },
- {
- "macip_acl_dump": {
- "reply": "macip_acl_details",
- "stream": true
- }
- }
- ],
- "vl_api_version": "0x1db2ece9",
- "enums": [],
- "messages": [
- [
- "acl_plugin_get_version",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- {
- "crc": "0x51077d14"
- }
- ],
- [
- "acl_plugin_get_version_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "major"
- ],
- [
- "u32",
- "minor"
- ],
- {
- "crc": "0x9b32cf86"
- }
- ],
- [
- "acl_plugin_control_ping",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- {
- "crc": "0x51077d14"
- }
- ],
- [
- "acl_plugin_control_ping_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "vpe_pid"
- ],
- {
- "crc": "0xf6b0b8ca"
- }
- ],
- [
- "acl_add_replace",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "acl_index"
- ],
- [
- "u8",
- "tag",
- 64
- ],
- [
- "u32",
- "count"
- ],
- [
- "vl_api_acl_rule_t",
- "r",
- 0,
- "count"
- ],
- {
- "crc": "0xe839997e"
- }
- ],
- [
- "acl_add_replace_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "acl_index"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xac407b0c"
- }
- ],
- [
- "acl_del",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "acl_index"
- ],
- {
- "crc": "0xef34fea4"
- }
- ],
- [
- "acl_del_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "acl_interface_add_del",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u8",
- "is_add"
- ],
- [
- "u8",
- "is_input"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u32",
- "acl_index"
- ],
- {
- "crc": "0x0b2aedd1"
- }
- ],
- [
- "acl_interface_add_del_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "acl_interface_set_acl_list",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "count"
- ],
- [
- "u8",
- "n_input"
- ],
- [
- "u32",
- "acls",
- 0,
- "count"
- ],
- {
- "crc": "0x8baece38"
- }
- ],
- [
- "acl_interface_set_acl_list_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "acl_dump",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "acl_index"
- ],
- {
- "crc": "0xef34fea4"
- }
- ],
- [
- "acl_details",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "acl_index"
- ],
- [
- "u8",
- "tag",
- 64
- ],
- [
- "u32",
- "count"
- ],
- [
- "vl_api_acl_rule_t",
- "r",
- 0,
- "count"
- ],
- {
- "crc": "0x5bd895be"
- }
- ],
- [
- "acl_interface_list_dump",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- {
- "crc": "0x529cb13f"
- }
- ],
- [
- "acl_interface_list_details",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "count"
- ],
- [
- "u8",
- "n_input"
- ],
- [
- "u32",
- "acls",
- 0,
- "count"
- ],
- {
- "crc": "0xd5e80809"
- }
- ],
- [
- "macip_acl_add",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u8",
- "tag",
- 64
- ],
- [
- "u32",
- "count"
- ],
- [
- "vl_api_macip_acl_rule_t",
- "r",
- 0,
- "count"
- ],
- {
- "crc": "0xb3d3d65a"
- }
- ],
- [
- "macip_acl_add_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "acl_index"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xac407b0c"
- }
- ],
- [
- "macip_acl_add_replace",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "acl_index"
- ],
- [
- "u8",
- "tag",
- 64
- ],
- [
- "u32",
- "count"
- ],
- [
- "vl_api_macip_acl_rule_t",
- "r",
- 0,
- "count"
- ],
- {
- "crc": "0xa0e8c01b"
- }
- ],
- [
- "macip_acl_add_replace_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "acl_index"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xac407b0c"
- }
- ],
- [
- "macip_acl_del",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "acl_index"
- ],
- {
- "crc": "0xef34fea4"
- }
- ],
- [
- "macip_acl_del_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "macip_acl_interface_add_del",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u8",
- "is_add"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u32",
- "acl_index"
- ],
- {
- "crc": "0x6a6be97c"
- }
- ],
- [
- "macip_acl_interface_add_del_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "macip_acl_dump",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "acl_index"
- ],
- {
- "crc": "0xef34fea4"
- }
- ],
- [
- "macip_acl_details",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "acl_index"
- ],
- [
- "u8",
- "tag",
- 64
- ],
- [
- "u32",
- "count"
- ],
- [
- "vl_api_macip_acl_rule_t",
- "r",
- 0,
- "count"
- ],
- {
- "crc": "0xdd2b55ba"
- }
- ],
- [
- "macip_acl_interface_get",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- {
- "crc": "0x51077d14"
- }
- ],
- [
- "macip_acl_interface_get_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "count"
- ],
- [
- "u32",
- "acls",
- 0,
- "count"
- ],
- {
- "crc": "0xaccf9b05"
- }
- ],
- [
- "macip_acl_interface_list_dump",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- {
- "crc": "0x529cb13f"
- }
- ],
- [
- "macip_acl_interface_list_details",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "count"
- ],
- [
- "u32",
- "acls",
- 0,
- "count"
- ],
- {
- "crc": "0x29783fa0"
- }
- ]
- ],
- "types": [
- [
- "acl_rule",
- [
- "u8",
- "is_permit"
- ],
- [
- "u8",
- "is_ipv6"
- ],
- [
- "u8",
- "src_ip_addr",
- 16
- ],
- [
- "u8",
- "src_ip_prefix_len"
- ],
- [
- "u8",
- "dst_ip_addr",
- 16
- ],
- [
- "u8",
- "dst_ip_prefix_len"
- ],
- [
- "u8",
- "proto"
- ],
- [
- "u16",
- "srcport_or_icmptype_first"
- ],
- [
- "u16",
- "srcport_or_icmptype_last"
- ],
- [
- "u16",
- "dstport_or_icmpcode_first"
- ],
- [
- "u16",
- "dstport_or_icmpcode_last"
- ],
- [
- "u8",
- "tcp_flags_mask"
- ],
- [
- "u8",
- "tcp_flags_value"
- ],
- {
- "crc": "0x6f99bf4d"
- }
- ],
- [
- "macip_acl_rule",
- [
- "u8",
- "is_permit"
- ],
- [
- "u8",
- "is_ipv6"
- ],
- [
- "u8",
- "src_mac",
- 6
- ],
- [
- "u8",
- "src_mac_mask",
- 6
- ],
- [
- "u8",
- "src_ip_addr",
- 16
- ],
- [
- "u8",
- "src_ip_prefix_len"
- ],
- {
- "crc": "0x70589f1e"
- }
- ]
- ]
-}
diff --git a/cmd/binapi-generator/testdata/af_packet.api.json b/cmd/binapi-generator/testdata/af_packet.api.json
deleted file mode 100644
index 81ee2f9..0000000
--- a/cmd/binapi-generator/testdata/af_packet.api.json
+++ /dev/null
@@ -1,163 +0,0 @@
-{
- "services": [
- {
- "af_packet_delete": {
- "reply": "af_packet_delete_reply"
- }
- },
- {
- "af_packet_create": {
- "reply": "af_packet_create_reply"
- }
- },
- {
- "af_packet_set_l4_cksum_offload": {
- "reply": "af_packet_set_l4_cksum_offload_reply"
- }
- }
- ],
- "vl_api_version": "0x8957ca8b",
- "enums": [],
- "messages": [
- [
- "af_packet_create",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u8",
- "host_if_name",
- 64
- ],
- [
- "u8",
- "hw_addr",
- 6
- ],
- [
- "u8",
- "use_random_hw_addr"
- ],
- {
- "crc": "0x6d5d30d6"
- }
- ],
- [
- "af_packet_create_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- {
- "crc": "0xfda5941f"
- }
- ],
- [
- "af_packet_delete",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u8",
- "host_if_name",
- 64
- ],
- {
- "crc": "0x3efceda3"
- }
- ],
- [
- "af_packet_delete_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "af_packet_set_l4_cksum_offload",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u8",
- "sw_if_index"
- ],
- [
- "u8",
- "set"
- ],
- {
- "crc": "0x86538585"
- }
- ],
- [
- "af_packet_set_l4_cksum_offload_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ]
- ],
- "types": []
-}
diff --git a/cmd/binapi-generator/testdata/input-generate-error.json b/cmd/binapi-generator/testdata/input-generate-error.json
deleted file mode 100644
index d5df76e..0000000
--- a/cmd/binapi-generator/testdata/input-generate-error.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "key": "value"
-} \ No newline at end of file
diff --git a/cmd/binapi-generator/testdata/input-read-json-error.json b/cmd/binapi-generator/testdata/input-read-json-error.json
deleted file mode 100644
index 02691e3..0000000
--- a/cmd/binapi-generator/testdata/input-read-json-error.json
+++ /dev/null
@@ -1 +0,0 @@
-% \ No newline at end of file
diff --git a/cmd/binapi-generator/testdata/input.txt b/cmd/binapi-generator/testdata/input.txt
deleted file mode 100644
index e69de29..0000000
--- a/cmd/binapi-generator/testdata/input.txt
+++ /dev/null
diff --git a/cmd/binapi-generator/testdata/ip.api.json b/cmd/binapi-generator/testdata/ip.api.json
deleted file mode 100644
index 530b6d6..0000000
--- a/cmd/binapi-generator/testdata/ip.api.json
+++ /dev/null
@@ -1,2246 +0,0 @@
-{
- "services": [
- {
- "ip_source_and_port_range_check_add_del": {
- "reply": "ip_source_and_port_range_check_add_del_reply"
- }
- },
- {
- "ip6_fib_dump": {
- "reply": "ip6_fib_details",
- "stream": true
- }
- },
- {
- "want_ip6_nd_events": {
- "reply": "want_ip6_nd_events_reply"
- }
- },
- {
- "ip_punt_police": {
- "reply": "ip_punt_police_reply"
- }
- },
- {
- "set_arp_neighbor_limit": {
- "reply": "set_arp_neighbor_limit_reply"
- }
- },
- {
- "ip6nd_proxy_add_del": {
- "reply": "ip6nd_proxy_add_del_reply"
- }
- },
- {
- "ioam_disable": {
- "reply": "ioam_disable_reply"
- }
- },
- {
- "ip_table_add_del": {
- "reply": "ip_table_add_del_reply"
- }
- },
- {
- "ip_neighbor_dump": {
- "reply": "ip_neighbor_details",
- "stream": true
- }
- },
- {
- "ip4_arp_event": {
- "reply": null
- }
- },
- {
- "ip_punt_redirect": {
- "reply": "ip_punt_redirect_reply"
- }
- },
- {
- "sw_interface_ip6nd_ra_prefix": {
- "reply": "sw_interface_ip6nd_ra_prefix_reply"
- }
- },
- {
- "reset_fib": {
- "reply": "reset_fib_reply"
- }
- },
- {
- "ip6_mfib_dump": {
- "reply": "ip6_mfib_details",
- "stream": true
- }
- },
- {
- "sw_interface_ip6nd_ra_config": {
- "reply": "sw_interface_ip6nd_ra_config_reply"
- }
- },
- {
- "sw_interface_ip6_enable_disable": {
- "reply": "sw_interface_ip6_enable_disable_reply"
- }
- },
- {
- "sw_interface_ip6_set_link_local_address": {
- "reply": "sw_interface_ip6_set_link_local_address_reply"
- }
- },
- {
- "mfib_signal_dump": {
- "reply": "mfib_signal_details",
- "stream": true
- }
- },
- {
- "ip_container_proxy_add_del": {
- "reply": "ip_container_proxy_add_del_reply"
- }
- },
- {
- "ip_mfib_dump": {
- "reply": "ip_mfib_details",
- "stream": true
- }
- },
- {
- "ip_address_dump": {
- "reply": "ip_address_details",
- "stream": true
- }
- },
- {
- "ip_dump": {
- "reply": "ip_details",
- "stream": true
- }
- },
- {
- "ip_neighbor_add_del": {
- "reply": "ip_neighbor_add_del_reply"
- }
- },
- {
- "proxy_arp_intfc_enable_disable": {
- "reply": "proxy_arp_intfc_enable_disable_reply"
- }
- },
- {
- "proxy_arp_add_del": {
- "reply": "proxy_arp_add_del_reply"
- }
- },
- {
- "ip_add_del_route": {
- "reply": "ip_add_del_route_reply"
- }
- },
- {
- "ip6nd_proxy_dump": {
- "reply": "ip6nd_proxy_details",
- "stream": true
- }
- },
- {
- "ip_fib_dump": {
- "reply": "ip_fib_details",
- "stream": true
- }
- },
- {
- "want_ip4_arp_events": {
- "reply": "want_ip4_arp_events_reply"
- }
- },
- {
- "ioam_enable": {
- "reply": "ioam_enable_reply"
- }
- },
- {
- "ip6_nd_event": {
- "reply": null
- }
- },
- {
- "ip_mroute_add_del": {
- "reply": "ip_mroute_add_del_reply"
- }
- },
- {
- "ip_source_and_port_range_check_interface_add_del": {
- "reply": "ip_source_and_port_range_check_interface_add_del_reply"
- }
- },
- {
- "set_ip_flow_hash": {
- "reply": "set_ip_flow_hash_reply"
- }
- }
- ],
- "vl_api_version": "0xb395c625",
- "enums": [],
- "messages": [
- [
- "ip_table_add_del",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "table_id"
- ],
- [
- "u8",
- "is_ipv6"
- ],
- [
- "u8",
- "is_add"
- ],
- [
- "u8",
- "name",
- 64
- ],
- {
- "crc": "0x0240c89d"
- }
- ],
- [
- "ip_table_add_del_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "ip_fib_dump",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- {
- "crc": "0x51077d14"
- }
- ],
- [
- "ip_fib_details",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "table_id"
- ],
- [
- "u8",
- "table_name",
- 64
- ],
- [
- "u8",
- "address_length"
- ],
- [
- "u8",
- "address",
- 4
- ],
- [
- "u32",
- "count"
- ],
- [
- "vl_api_fib_path_t",
- "path",
- 0,
- "count"
- ],
- {
- "crc": "0x99dfd73b"
- }
- ],
- [
- "ip6_fib_dump",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- {
- "crc": "0x51077d14"
- }
- ],
- [
- "ip6_fib_details",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "table_id"
- ],
- [
- "u8",
- "table_name",
- 64
- ],
- [
- "u8",
- "address_length"
- ],
- [
- "u8",
- "address",
- 16
- ],
- [
- "u32",
- "count"
- ],
- [
- "vl_api_fib_path_t",
- "path",
- 0,
- "count"
- ],
- {
- "crc": "0xabd0060e"
- }
- ],
- [
- "ip_neighbor_dump",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "is_ipv6"
- ],
- {
- "crc": "0x6b7bcd0a"
- }
- ],
- [
- "ip_neighbor_details",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "is_static"
- ],
- [
- "u8",
- "is_ipv6"
- ],
- [
- "u8",
- "mac_address",
- 6
- ],
- [
- "u8",
- "ip_address",
- 16
- ],
- {
- "crc": "0x85e32a72"
- }
- ],
- [
- "ip_neighbor_add_del",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "is_add"
- ],
- [
- "u8",
- "is_ipv6"
- ],
- [
- "u8",
- "is_static"
- ],
- [
- "u8",
- "is_no_adj_fib"
- ],
- [
- "u8",
- "mac_address",
- 6
- ],
- [
- "u8",
- "dst_address",
- 16
- ],
- {
- "crc": "0x4711eb25"
- }
- ],
- [
- "ip_neighbor_add_del_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "set_ip_flow_hash",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "vrf_id"
- ],
- [
- "u8",
- "is_ipv6"
- ],
- [
- "u8",
- "src"
- ],
- [
- "u8",
- "dst"
- ],
- [
- "u8",
- "sport"
- ],
- [
- "u8",
- "dport"
- ],
- [
- "u8",
- "proto"
- ],
- [
- "u8",
- "reverse"
- ],
- {
- "crc": "0x32ebf737"
- }
- ],
- [
- "set_ip_flow_hash_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "sw_interface_ip6nd_ra_config",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "suppress"
- ],
- [
- "u8",
- "managed"
- ],
- [
- "u8",
- "other"
- ],
- [
- "u8",
- "ll_option"
- ],
- [
- "u8",
- "send_unicast"
- ],
- [
- "u8",
- "cease"
- ],
- [
- "u8",
- "is_no"
- ],
- [
- "u8",
- "default_router"
- ],
- [
- "u32",
- "max_interval"
- ],
- [
- "u32",
- "min_interval"
- ],
- [
- "u32",
- "lifetime"
- ],
- [
- "u32",
- "initial_count"
- ],
- [
- "u32",
- "initial_interval"
- ],
- {
- "crc": "0xc3f02daa"
- }
- ],
- [
- "sw_interface_ip6nd_ra_config_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "sw_interface_ip6nd_ra_prefix",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "address",
- 16
- ],
- [
- "u8",
- "address_length"
- ],
- [
- "u8",
- "use_default"
- ],
- [
- "u8",
- "no_advertise"
- ],
- [
- "u8",
- "off_link"
- ],
- [
- "u8",
- "no_autoconfig"
- ],
- [
- "u8",
- "no_onlink"
- ],
- [
- "u8",
- "is_no"
- ],
- [
- "u32",
- "val_lifetime"
- ],
- [
- "u32",
- "pref_lifetime"
- ],
- {
- "crc": "0xca763c9a"
- }
- ],
- [
- "sw_interface_ip6nd_ra_prefix_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "ip6nd_proxy_add_del",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "is_del"
- ],
- [
- "u8",
- "address",
- 16
- ],
- {
- "crc": "0xd95f0fa0"
- }
- ],
- [
- "ip6nd_proxy_add_del_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "ip6nd_proxy_details",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "address",
- 16
- ],
- {
- "crc": "0xd73bf1ab"
- }
- ],
- [
- "ip6nd_proxy_dump",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- {
- "crc": "0x51077d14"
- }
- ],
- [
- "sw_interface_ip6_enable_disable",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "enable"
- ],
- {
- "crc": "0xa36fadc0"
- }
- ],
- [
- "sw_interface_ip6_enable_disable_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "sw_interface_ip6_set_link_local_address",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "address",
- 16
- ],
- {
- "crc": "0xd73bf1ab"
- }
- ],
- [
- "sw_interface_ip6_set_link_local_address_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "ip_add_del_route",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "next_hop_sw_if_index"
- ],
- [
- "u32",
- "table_id"
- ],
- [
- "u32",
- "classify_table_index"
- ],
- [
- "u32",
- "next_hop_table_id"
- ],
- [
- "u32",
- "next_hop_id"
- ],
- [
- "u8",
- "is_add"
- ],
- [
- "u8",
- "is_drop"
- ],
- [
- "u8",
- "is_unreach"
- ],
- [
- "u8",
- "is_prohibit"
- ],
- [
- "u8",
- "is_ipv6"
- ],
- [
- "u8",
- "is_local"
- ],
- [
- "u8",
- "is_classify"
- ],
- [
- "u8",
- "is_multipath"
- ],
- [
- "u8",
- "is_resolve_host"
- ],
- [
- "u8",
- "is_resolve_attached"
- ],
- [
- "u8",
- "is_dvr"
- ],
- [
- "u8",
- "is_source_lookup"
- ],
- [
- "u8",
- "is_udp_encap"
- ],
- [
- "u8",
- "next_hop_weight"
- ],
- [
- "u8",
- "next_hop_preference"
- ],
- [
- "u8",
- "next_hop_proto"
- ],
- [
- "u8",
- "dst_address_length"
- ],
- [
- "u8",
- "dst_address",
- 16
- ],
- [
- "u8",
- "next_hop_address",
- 16
- ],
- [
- "u8",
- "next_hop_n_out_labels"
- ],
- [
- "u32",
- "next_hop_via_label"
- ],
- [
- "u32",
- "next_hop_out_label_stack",
- 0,
- "next_hop_n_out_labels"
- ],
- {
- "crc": "0xc85f8290"
- }
- ],
- [
- "ip_add_del_route_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "ip_mroute_add_del",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "next_hop_sw_if_index"
- ],
- [
- "u32",
- "table_id"
- ],
- [
- "u32",
- "entry_flags"
- ],
- [
- "u32",
- "itf_flags"
- ],
- [
- "u32",
- "rpf_id"
- ],
- [
- "u32",
- "bier_imp"
- ],
- [
- "u16",
- "grp_address_length"
- ],
- [
- "u8",
- "next_hop_afi"
- ],
- [
- "u8",
- "is_add"
- ],
- [
- "u8",
- "is_ipv6"
- ],
- [
- "u8",
- "is_local"
- ],
- [
- "u8",
- "grp_address",
- 16
- ],
- [
- "u8",
- "src_address",
- 16
- ],
- {
- "crc": "0xc37112f7"
- }
- ],
- [
- "ip_mroute_add_del_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "ip_mfib_dump",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- {
- "crc": "0x51077d14"
- }
- ],
- [
- "ip_mfib_details",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "table_id"
- ],
- [
- "u32",
- "entry_flags"
- ],
- [
- "u32",
- "rpf_id"
- ],
- [
- "u8",
- "address_length"
- ],
- [
- "u8",
- "grp_address",
- 4
- ],
- [
- "u8",
- "src_address",
- 4
- ],
- [
- "u32",
- "count"
- ],
- [
- "vl_api_fib_path_t",
- "path",
- 0,
- "count"
- ],
- {
- "crc": "0x5e530d5e"
- }
- ],
- [
- "ip6_mfib_dump",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- {
- "crc": "0x51077d14"
- }
- ],
- [
- "ip6_mfib_details",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "table_id"
- ],
- [
- "u8",
- "address_length"
- ],
- [
- "u8",
- "grp_address",
- 16
- ],
- [
- "u8",
- "src_address",
- 16
- ],
- [
- "u32",
- "count"
- ],
- [
- "vl_api_fib_path_t",
- "path",
- 0,
- "count"
- ],
- {
- "crc": "0xe02dcb4b"
- }
- ],
- [
- "ip_address_details",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u8",
- "ip",
- 16
- ],
- [
- "u8",
- "prefix_length"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "is_ipv6"
- ],
- {
- "crc": "0xbc7442f2"
- }
- ],
- [
- "ip_address_dump",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "is_ipv6"
- ],
- {
- "crc": "0x6b7bcd0a"
- }
- ],
- [
- "ip_details",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u8",
- "is_ipv6"
- ],
- {
- "crc": "0x452ffc5a"
- }
- ],
- [
- "ip_dump",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u8",
- "is_ipv6"
- ],
- {
- "crc": "0xde883da4"
- }
- ],
- [
- "mfib_signal_dump",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- {
- "crc": "0x51077d14"
- }
- ],
- [
- "mfib_signal_details",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u32",
- "table_id"
- ],
- [
- "u16",
- "grp_address_len"
- ],
- [
- "u8",
- "grp_address",
- 16
- ],
- [
- "u8",
- "src_address",
- 16
- ],
- [
- "u16",
- "ip_packet_len"
- ],
- [
- "u8",
- "ip_packet_data",
- 256
- ],
- {
- "crc": "0x791bbeab"
- }
- ],
- [
- "ip_punt_police",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "policer_index"
- ],
- [
- "u8",
- "is_add"
- ],
- [
- "u8",
- "is_ip6"
- ],
- {
- "crc": "0x38691592"
- }
- ],
- [
- "ip_punt_police_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "ip_punt_redirect",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "rx_sw_if_index"
- ],
- [
- "u32",
- "tx_sw_if_index"
- ],
- [
- "u8",
- "is_add"
- ],
- [
- "u8",
- "is_ip6"
- ],
- [
- "u8",
- "nh",
- 16
- ],
- {
- "crc": "0x996b6603"
- }
- ],
- [
- "ip_punt_redirect_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "ip_container_proxy_add_del",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u8",
- "ip",
- 16
- ],
- [
- "u8",
- "is_ip4"
- ],
- [
- "u8",
- "plen"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "is_add"
- ],
- {
- "crc": "0x0a355d39"
- }
- ],
- [
- "ip_container_proxy_add_del_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "ip_source_and_port_range_check_add_del",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u8",
- "is_ipv6"
- ],
- [
- "u8",
- "is_add"
- ],
- [
- "u8",
- "mask_length"
- ],
- [
- "u8",
- "address",
- 16
- ],
- [
- "u8",
- "number_of_ranges"
- ],
- [
- "u16",
- "low_ports",
- 32
- ],
- [
- "u16",
- "high_ports",
- 32
- ],
- [
- "u32",
- "vrf_id"
- ],
- {
- "crc": "0x03d6b03a"
- }
- ],
- [
- "ip_source_and_port_range_check_add_del_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "ip_source_and_port_range_check_interface_add_del",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u8",
- "is_add"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u32",
- "tcp_in_vrf_id"
- ],
- [
- "u32",
- "tcp_out_vrf_id"
- ],
- [
- "u32",
- "udp_in_vrf_id"
- ],
- [
- "u32",
- "udp_out_vrf_id"
- ],
- {
- "crc": "0x6966bc44"
- }
- ],
- [
- "ip_source_and_port_range_check_interface_add_del_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "want_ip4_arp_events",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u8",
- "enable_disable"
- ],
- [
- "u32",
- "pid"
- ],
- [
- "u32",
- "address"
- ],
- {
- "crc": "0x77e06379"
- }
- ],
- [
- "want_ip4_arp_events_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "ip4_arp_event",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "address"
- ],
- [
- "u32",
- "pid"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "new_mac",
- 6
- ],
- [
- "u8",
- "mac_ip"
- ],
- {
- "crc": "0xef7235f7"
- }
- ],
- [
- "want_ip6_nd_events",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u8",
- "enable_disable"
- ],
- [
- "u32",
- "pid"
- ],
- [
- "u8",
- "address",
- 16
- ],
- {
- "crc": "0x1cf65fbb"
- }
- ],
- [
- "want_ip6_nd_events_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "ip6_nd_event",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "pid"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "address",
- 16
- ],
- [
- "u8",
- "new_mac",
- 6
- ],
- [
- "u8",
- "mac_ip"
- ],
- {
- "crc": "0x96ab2fdd"
- }
- ],
- [
- "proxy_arp_add_del",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "vrf_id"
- ],
- [
- "u8",
- "is_add"
- ],
- [
- "u8",
- "low_address",
- 4
- ],
- [
- "u8",
- "hi_address",
- 4
- ],
- {
- "crc": "0xc2442918"
- }
- ],
- [
- "proxy_arp_add_del_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "proxy_arp_intfc_enable_disable",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u8",
- "enable_disable"
- ],
- {
- "crc": "0x69d24598"
- }
- ],
- [
- "proxy_arp_intfc_enable_disable_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "reset_fib",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u32",
- "vrf_id"
- ],
- [
- "u8",
- "is_ipv6"
- ],
- {
- "crc": "0x8553ebd9"
- }
- ],
- [
- "reset_fib_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "set_arp_neighbor_limit",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u8",
- "is_ipv6"
- ],
- [
- "u32",
- "arp_neighbor_limit"
- ],
- {
- "crc": "0x97d01fd6"
- }
- ],
- [
- "set_arp_neighbor_limit_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "ioam_enable",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u16",
- "id"
- ],
- [
- "u8",
- "seqno"
- ],
- [
- "u8",
- "analyse"
- ],
- [
- "u8",
- "pot_enable"
- ],
- [
- "u8",
- "trace_enable"
- ],
- [
- "u32",
- "node_id"
- ],
- {
- "crc": "0x9392e032"
- }
- ],
- [
- "ioam_enable_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ],
- [
- "ioam_disable",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "client_index"
- ],
- [
- "u32",
- "context"
- ],
- [
- "u16",
- "id"
- ],
- {
- "crc": "0x6b16a45e"
- }
- ],
- [
- "ioam_disable_reply",
- [
- "u16",
- "_vl_msg_id"
- ],
- [
- "u32",
- "context"
- ],
- [
- "i32",
- "retval"
- ],
- {
- "crc": "0xe8d4e804"
- }
- ]
- ],
- "types": [
- [
- "fib_path",
- [
- "u32",
- "sw_if_index"
- ],
- [
- "u32",
- "table_id"
- ],
- [
- "u8",
- "weight"
- ],
- [
- "u8",
- "preference"
- ],
- [
- "u8",
- "is_local"
- ],
- [
- "u8",
- "is_drop"
- ],
- [
- "u8",
- "is_unreach"
- ],
- [
- "u8",
- "is_prohibit"
- ],
- [
- "u8",
- "afi"
- ],
- [
- "u8",
- "next_hop",
- 16
- ],
- {
- "crc": "0xcd899e0a"
- }
- ]
- ]
-}
diff --git a/cmd/binapi-generator/types.go b/cmd/binapi-generator/types.go
deleted file mode 100644
index 90c890f..0000000
--- a/cmd/binapi-generator/types.go
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright (c) 2019 Cisco and/or its affiliates.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at:
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
- "fmt"
- "strconv"
- "strings"
-
- "github.com/sirupsen/logrus"
-)
-
-// toApiType returns name that is used as type reference in VPP binary API
-func toApiType(name string) string {
- return fmt.Sprintf("vl_api_%s_t", name)
-}
-
-// binapiTypes is a set of types used VPP binary API for translation to Go types
-var binapiTypes = map[string]string{
- "u8": "uint8",
- "i8": "int8",
- "u16": "uint16",
- "i16": "int16",
- "u32": "uint32",
- "i32": "int32",
- "u64": "uint64",
- "i64": "int64",
- "f64": "float64",
-}
-
-func getBinapiTypeSize(binapiType string) int {
- if _, ok := binapiTypes[binapiType]; ok {
- b, err := strconv.Atoi(strings.TrimLeft(binapiType, "uif"))
- if err == nil {
- return b / 8
- }
- }
- return -1
-}
-
-// convertToGoType translates the VPP binary API type into Go type
-func convertToGoType(ctx *context, binapiType string) (typ string) {
- if t, ok := binapiTypes[binapiType]; ok {
- // basic types
- typ = t
- } else if r, ok := ctx.packageData.RefMap[binapiType]; ok {
- // specific types (enums/types/unions)
- typ = camelCaseName(r)
- } else {
- switch binapiType {
- case "bool", "string":
- typ = binapiType
- default:
- // fallback type
- logrus.Warnf("found unknown VPP binary API type %q, using byte", binapiType)
- typ = "byte"
- }
- }
- return typ
-}
-
-func getSizeOfType(ctx *context, typ *Type) (size int) {
- for _, field := range typ.Fields {
- enum := getEnumByRef(ctx, field.Type)
- if enum != nil {
- size += getSizeOfBinapiTypeLength(enum.Type, field.Length)
- continue
- }
- size += getSizeOfBinapiTypeLength(field.Type, field.Length)
- }
- return size
-}
-
-func getSizeOfBinapiTypeLength(typ string, length int) (size int) {
- if n := getBinapiTypeSize(typ); n > 0 {
- if length > 0 {
- return n * length
- } else {
- return n
- }
- }
-
- return
-}
-
-func getEnumByRef(ctx *context, ref string) *Enum {
- for _, typ := range ctx.packageData.Enums {
- if ref == toApiType(typ.Name) {
- return &typ
- }
- }
- return nil
-}
-
-func getTypeByRef(ctx *context, ref string) *Type {
- for _, typ := range ctx.packageData.Types {
- if ref == toApiType(typ.Name) {
- return &typ
- }
- }
- return nil
-}
-
-func getAliasByRef(ctx *context, ref string) *Alias {
- for _, alias := range ctx.packageData.Aliases {
- if ref == toApiType(alias.Name) {
- return &alias
- }
- }
- return nil
-}
-
-func getUnionSize(ctx *context, union *Union) (maxSize int) {
- for _, field := range union.Fields {
- typ := getTypeByRef(ctx, field.Type)
- if typ != nil {
- if size := getSizeOfType(ctx, typ); size > maxSize {
- maxSize = size
- }
- continue
- }
- alias := getAliasByRef(ctx, field.Type)
- if alias != nil {
- if size := getSizeOfBinapiTypeLength(alias.Type, alias.Length); size > maxSize {
- maxSize = size
- }
- continue
- } else {
- logf("no type or alias found for union %s field type %q", union.Name, field.Type)
- continue
- }
- }
- logf("getUnionSize: %s %+v max=%v", union.Name, union.Fields, maxSize)
- return
-}
diff --git a/cmd/binapi-generator/util.go b/cmd/binapi-generator/util.go
new file mode 100644
index 0000000..8738963
--- /dev/null
+++ b/cmd/binapi-generator/util.go
@@ -0,0 +1,81 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "io/ioutil"
+ "os/exec"
+ "path"
+ "strings"
+
+ "github.com/sirupsen/logrus"
+)
+
+const (
+ versionScriptPath = "./src/scripts/version"
+ defaultVppApiDir = "/usr/share/vpp/api"
+)
+
+func ResolveVppVersion(inputDir string) string {
+ // assuming VPP package is installed
+ if inputDir == defaultVppApiDir {
+ // resolve VPP version using dpkg
+ cmd := exec.Command("dpkg-query", "-f", "${Version}", "-W", "vpp")
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ logrus.Warnf("resolving VPP version from installed package failed: %v", err)
+ logrus.Warnf("command output: %s", out)
+ } else {
+ version := strings.TrimSpace(string(out))
+ logrus.Debugf("resolved VPP version from installed package: %v", version)
+ return version
+ }
+ }
+ // check if inside VPP git repo
+ if inputDir != "" {
+ repo := findVppGitRepo(inputDir)
+ if repo != "" {
+ cmd := exec.Command(versionScriptPath)
+ cmd.Dir = repo
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ logrus.Warnf("resolving VPP version from version script failed: %v", err)
+ logrus.Warnf("command output: %s", out)
+ } else {
+ version := strings.TrimSpace(string(out))
+ logrus.Debugf("resolved VPP version from version script: %v", version)
+ return version
+ }
+ }
+ file, err := ioutil.ReadFile(path.Join(inputDir, "VPP_VERSION"))
+ if err == nil {
+ return strings.TrimSpace(string(file))
+ }
+ }
+ logrus.Warnf("VPP version could not be resolved, you can set it manually using VPP_API_VERSION env var")
+ return "unknown"
+}
+
+func findVppGitRepo(dir string) string {
+ cmd := exec.Command("git", "rev-parse", "--show-toplevel")
+ cmd.Dir = dir
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ logrus.Warnf("checking VPP git repo failed: %v", err)
+ logrus.Warnf("command output: %s", out)
+ return ""
+ }
+ return strings.TrimSpace(string(out))
+}