aboutsummaryrefslogtreecommitdiffstats
path: root/cmd
diff options
context:
space:
mode:
authorOndrej Fabry <ofabry@cisco.com>2019-06-06 14:08:48 +0200
committerOndrej Fabry <ofabry@cisco.com>2019-06-06 14:08:48 +0200
commitc4522fe10317b1729a0820dc880afc78c663f64d (patch)
tree3c370f285b3d00feb5857ca155f7e46ae8765f7f /cmd
parent0ff02b6b1f0757f5e4c011457757bd18d0a60f01 (diff)
Add various generator improvements
- generate service implementation for modules - generate conversion maps and String() method for enums - generate module name and version as constants - rename Union_data field to XXX_UnionData for consistency - generate constant GoVppAPIPackageIsVersionN for checking compatibility with API - add example for using service clients - add some documentation to socketclient adapter - cleanup gen.go file used for generating binapi - regenerate binapi with latest VPP release (19.04.1) - change global variables Messages into a function AllMessages Change-Id: Id1ef97764570759eaa3e5a4dc14ecda7a168ee39 Signed-off-by: Ondrej Fabry <ofabry@cisco.com>
Diffstat (limited to 'cmd')
-rw-r--r--cmd/binapi-generator/doc.go3
-rw-r--r--cmd/binapi-generator/generate.go197
-rw-r--r--cmd/binapi-generator/main.go84
-rw-r--r--cmd/binapi-generator/objects.go59
-rw-r--r--cmd/binapi-generator/parse.go82
-rw-r--r--cmd/binapi-generator/types.go4
6 files changed, 260 insertions, 169 deletions
diff --git a/cmd/binapi-generator/doc.go b/cmd/binapi-generator/doc.go
index e8556ec..d74d47b 100644
--- a/cmd/binapi-generator/doc.go
+++ b/cmd/binapi-generator/doc.go
@@ -7,7 +7,6 @@
// where each Go package will be placed into its own separate directory,
// for example:
//
-// binapi-generator --input-file=examples/bin_api/acl.api.json --output-dir=examples/bin_api
-// binapi-generator --input-dir=examples/bin_api --output-dir=examples/bin_api
+// binapi-generator --input-file=/usr/share/vpp/api/core/interface.api.json --output-dir=.
//
package main
diff --git a/cmd/binapi-generator/generate.go b/cmd/binapi-generator/generate.go
index 4ffe88e..d9555e7 100644
--- a/cmd/binapi-generator/generate.go
+++ b/cmd/binapi-generator/generate.go
@@ -15,7 +15,6 @@
package main
import (
- "bufio"
"bytes"
"fmt"
"io"
@@ -25,12 +24,23 @@ import (
"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
govppApiImportPath = "git.fd.io/govpp.git/api" // import path of the govpp API package
- constAPIVersionCrc = "APIVersionCrc" // name for the API version CRC constant
+
+ constModuleName = "ModuleName" // module name constant
+ constAPIVersion = "APIVersion" // API version constant
+ constVersionCrc = "VersionCrc" // version CRC constant
+
+ unionDataField = "XXX_UnionData" // name for the union data field
)
// context is a structure storing data for code generation
@@ -40,9 +50,10 @@ type context struct {
inputData []byte // contents of the input file
- includeAPIVersionCrc bool // include constant with API version CRC string
- includeComments bool // include parts of original source in comments
- includeBinapiNames bool // include binary API names as struct tag
+ 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
@@ -83,25 +94,28 @@ func getContext(inputFile, outputDir string) (*context, error) {
}
// generatePackage generates code for the parsed package data and writes it into w
-func generatePackage(ctx *context, w *bufio.Writer) error {
+func generatePackage(ctx *context, w io.Writer) error {
logf("generating package %q", ctx.packageName)
// generate file header
generateHeader(ctx, w)
generateImports(ctx, w)
- if ctx.includeAPIVersionCrc {
- fmt.Fprintf(w, "// %s defines API version CRC of the VPP binary API module.\n", constAPIVersionCrc)
- fmt.Fprintf(w, "const %s = %v\n", constAPIVersionCrc, ctx.packageData.APIVersion)
- fmt.Fprintln(w)
- }
-
- // generate services
- if len(ctx.packageData.Services) > 0 {
- generateServices(ctx, w, ctx.packageData.Services)
+ // 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)
- // TODO: generate implementation for Services interface
+ 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 {
@@ -156,17 +170,23 @@ func generatePackage(ctx *context, w *bufio.Writer) error {
fmt.Fprintln(w, "}")
fmt.Fprintln(w)
- fmt.Fprintln(w, "var Messages = []api.Message{")
+ // 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, "}")
}
- // flush the data:
- if err := w.Flush(); err != nil {
- return fmt.Errorf("flushing data to %s failed: %v", ctx.outputFile, err)
+ if ctx.includeServices {
+ // generate services
+ if len(ctx.packageData.Services) > 0 {
+ generateServices(ctx, w, ctx.packageData.Services)
+ }
}
return nil
@@ -175,17 +195,18 @@ func generatePackage(ctx *context, w *bufio.Writer) error {
// generateHeader writes generated package header into w
func generateHeader(ctx *context, w io.Writer) {
fmt.Fprintln(w, "// Code generated by GoVPP binapi-generator. DO NOT EDIT.")
- fmt.Fprintf(w, "// source: %s\n", ctx.inputFile)
+ fmt.Fprintf(w, "// source: %s\n", ctx.inputFile)
fmt.Fprintln(w)
fmt.Fprintln(w, "/*")
- fmt.Fprintf(w, " Package %s is a generated from VPP binary API module '%s'.\n", ctx.packageName, ctx.moduleName)
+ fmt.Fprintf(w, "Package %s is a generated from VPP binary API module '%s'.\n", ctx.packageName, ctx.moduleName)
fmt.Fprintln(w)
- fmt.Fprintln(w, " It contains following objects:")
+ fmt.Fprintf(w, " The %s module consists of:\n", ctx.moduleName)
var printObjNum = func(obj string, num int) {
if num > 0 {
if num > 1 {
if strings.HasSuffix(obj, "s") {
+
obj += "es"
} else {
obj += "s"
@@ -195,13 +216,14 @@ func generateHeader(ctx *context, w io.Writer) {
}
}
- printObjNum("service", len(ctx.packageData.Services))
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)
}
@@ -209,28 +231,32 @@ func generateHeader(ctx *context, w io.Writer) {
// generateImports writes generated package imports into w
func generateImports(ctx *context, w io.Writer) {
fmt.Fprintf(w, "import api \"%s\"\n", govppApiImportPath)
- fmt.Fprintf(w, "import struc \"%s\"\n", "github.com/lunixbochs/struc")
fmt.Fprintf(w, "import bytes \"%s\"\n", "bytes")
+ fmt.Fprintf(w, "import context \"%s\"\n", "context")
+ fmt.Fprintf(w, "import strconv \"%s\"\n", "strconv")
+ fmt.Fprintf(w, "import struc \"%s\"\n", "github.com/lunixbochs/struc")
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 _ = struc.Pack\n")
fmt.Fprintf(w, "var _ = bytes.NewBuffer\n")
+ fmt.Fprintf(w, "var _ = context.Background\n")
+ fmt.Fprintf(w, "var _ = strconv.Itoa\n")
+ fmt.Fprintf(w, "var _ = struc.Pack\n")
fmt.Fprintln(w)
- /*fmt.Fprintln(w, "// This is a compile-time assertion to ensure that this generated file")
+ 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.Fprintln(w, "const _ = api.GoVppAPIPackageIsVersion1 // please upgrade the GoVPP api package")
- fmt.Fprintln(w)*/
+ fmt.Fprintf(w, "const _ = api.GoVppAPIPackageIsVersion%d // please upgrade the GoVPP api package\n", generatedCodeVersion)
+ fmt.Fprintln(w)
}
// generateComment writes generated comment for the object into w
func generateComment(ctx *context, w io.Writer, goName string, vppName string, objKind string) {
if objKind == "service" {
- fmt.Fprintf(w, "// %s represents VPP binary API services:\n", goName)
+ fmt.Fprintf(w, "// %s represents VPP binary API services in %s module.\n", ctx.moduleName, goName)
} else {
fmt.Fprintf(w, "// %s represents VPP binary API %s '%s':\n", goName, objKind, vppName)
}
@@ -290,22 +316,69 @@ func generateComment(ctx *context, w io.Writer, goName string, vppName string, o
}
// generateServices writes generated code for the Services interface into w
-func generateServices(ctx *context, w *bufio.Writer, services []Service) {
+func generateServices(ctx *context, w io.Writer, services []Service) {
+ const apiName = "Service"
+ const implName = "service"
+
// generate services comment
- generateComment(ctx, w, "Services", "services", "service")
+ generateComment(ctx, w, apiName, "services", "service")
// generate interface
- fmt.Fprintf(w, "type %s interface {\n", "Services")
+ fmt.Fprintf(w, "type %s interface {\n", apiName)
for _, svc := range services {
- generateService(ctx, w, &svc)
+ generateServiceMethod(ctx, w, &svc)
+ fmt.Fprintln(w)
}
fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+
+ // generate client implementation
+ fmt.Fprintf(w, "type %s struct {\n", implName)
+ fmt.Fprintf(w, "\tch api.Channel\n")
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+
+ fmt.Fprintf(w, "func New%[1]s(ch api.Channel) %[1]s {\n", apiName)
+ fmt.Fprintf(w, "\treturn &%s{ch}\n", implName)
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+
+ for _, svc := range services {
+ fmt.Fprintf(w, "func (c *%s) ", implName)
+ generateServiceMethod(ctx, w, &svc)
+ fmt.Fprintln(w, " {")
+ if svc.Stream {
+ // TODO: stream responses
+ //fmt.Fprintf(w, "\tstream := make(chan *%s)\n", camelCaseName(svc.ReplyType))
+ replyTyp := camelCaseName(svc.ReplyType)
+ fmt.Fprintf(w, "\tvar dump []*%s\n", replyTyp)
+ fmt.Fprintf(w, "\treq := c.ch.SendMultiRequest(in)\n")
+ fmt.Fprintf(w, "\tfor {\n")
+ fmt.Fprintf(w, "\tm := new(%s)\n", replyTyp)
+ fmt.Fprintf(w, "\tstop, err := req.ReceiveReply(m)\n")
+ fmt.Fprintf(w, "\tif stop { break }\n")
+ fmt.Fprintf(w, "\tif err != nil { return nil, err }\n")
+ fmt.Fprintf(w, "\tdump = append(dump, m)\n")
+ fmt.Fprintln(w, "}")
+ fmt.Fprintf(w, "\treturn dump, 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)
+ }
fmt.Fprintln(w)
}
-// generateService writes generated code for the service into w
-func generateService(ctx *context, w io.Writer, svc *Service) {
+// generateServiceMethod writes generated code for the service into w
+func generateServiceMethod(ctx *context, w io.Writer, svc *Service) {
reqTyp := camelCaseName(svc.RequestType)
// method name is same as parameter type name by default
@@ -317,17 +390,19 @@ func generateService(ctx *context, w io.Writer, svc *Service) {
}
}
- params := fmt.Sprintf("*%s", reqTyp)
+ params := fmt.Sprintf("in *%s", reqTyp)
returns := "error"
if replyType := camelCaseName(svc.ReplyType); replyType != "" {
- repTyp := fmt.Sprintf("*%s", replyType)
+ replyTyp := fmt.Sprintf("*%s", replyType)
if svc.Stream {
- repTyp = fmt.Sprintf("[]%s", repTyp)
+ // TODO: stream responses
+ //replyTyp = fmt.Sprintf("<-chan %s", replyTyp)
+ replyTyp = fmt.Sprintf("[]%s", replyTyp)
}
- returns = fmt.Sprintf("(%s, error)", repTyp)
+ returns = fmt.Sprintf("(%s, error)", replyTyp)
}
- fmt.Fprintf(w, "\t%s(%s) %s\n", method, params, returns)
+ fmt.Fprintf(w, "\t%s(ctx context.Context, %s) %s", method, params, returns)
}
// generateEnum writes generated code for the enum into w
@@ -344,15 +419,34 @@ func generateEnum(ctx *context, w io.Writer, enum *Enum) {
fmt.Fprintf(w, "type %s %s\n", name, typ)
fmt.Fprintln(w)
- fmt.Fprintln(w, "const (")
-
// 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)
}
@@ -394,8 +488,7 @@ func generateUnion(ctx *context, w io.Writer, union *Union) {
maxSize := getUnionSize(ctx, union)
// generate data field
- fieldName := "Union_data"
- fmt.Fprintf(w, "\t%s [%d]byte\n", fieldName, maxSize)
+ fmt.Fprintf(w, "\t%s [%d]byte\n", unionDataField, maxSize)
// generate end of the struct
fmt.Fprintln(w, "}")
@@ -420,9 +513,9 @@ func generateUnion(ctx *context, w io.Writer, union *Union) {
}
// generateUnionMethods generates methods that implement struc.Custom
-// interface to allow having Union_data field unexported
+// 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) {
+/*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) {
@@ -443,7 +536,7 @@ 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, `
@@ -456,14 +549,14 @@ func (u *%[1]s) Set%[2]s(a %[3]s) {
if err := struc.Pack(b, &a); err != nil {
return
}
- copy(u.Union_data[:], b.Bytes())
+ copy(u.%[4]s[:], b.Bytes())
}
func (u *%[1]s) Get%[2]s() (a %[3]s) {
- var b = bytes.NewReader(u.Union_data[:])
+ var b = bytes.NewReader(u.%[4]s[:])
struc.Unpack(b, &a)
return
}
-`, structName, getterField, getterStruct)
+`, structName, getterField, getterStruct, unionDataField)
}
// generateType writes generated code for the type into w
diff --git a/cmd/binapi-generator/main.go b/cmd/binapi-generator/main.go
index faed54f..75926e1 100644
--- a/cmd/binapi-generator/main.go
+++ b/cmd/binapi-generator/main.go
@@ -15,7 +15,7 @@
package main
import (
- "bufio"
+ "bytes"
"encoding/json"
"flag"
"fmt"
@@ -33,21 +33,16 @@ var (
inputFile = flag.String("input-file", "", "Input file with VPP API in JSON format.")
inputDir = flag.String("input-dir", ".", "Input directory with VPP API files in JSON format.")
outputDir = flag.String("output-dir", ".", "Output directory where package folders will be generated.")
- includeAPIVer = flag.Bool("include-apiver", false, "Include APIVersion constant for each module.")
+ includeAPIVer = flag.Bool("include-apiver", true, "Include APIVersion constant for each module.")
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.")
+ includeServices = flag.Bool("include-services", false, "Include service interface with client implementation.")
continueOnError = flag.Bool("continue-onerror", false, "Continue with next file on error.")
debug = flag.Bool("debug", debugMode, "Enable debug mode.")
)
var debugMode = os.Getenv("DEBUG_BINAPI_GENERATOR") != ""
-var log = logrus.Logger{
- Level: logrus.InfoLevel,
- Formatter: &logrus.TextFormatter{},
- Out: os.Stdout,
-}
-
func main() {
flag.Parse()
if *debug {
@@ -100,49 +95,51 @@ func getInputFiles(inputDir string) (res []string, err error) {
// generateFromFile generates Go package from one input JSON file
func generateFromFile(inputFile, outputDir string) error {
- logf("generating from file: %q", inputFile)
- defer logf("--------------------------------------")
+ logf("generating from file: %s", inputFile)
+ logf("------------------------------------------------------------")
+ defer logf("------------------------------------------------------------")
ctx, err := getContext(inputFile, outputDir)
if err != nil {
return err
}
- ctx.includeAPIVersionCrc = *includeAPIVer
+ // prepare options
+ ctx.includeAPIVersion = *includeAPIVer
ctx.includeComments = *includeComments
ctx.includeBinapiNames = *includeBinapiNames
+ ctx.includeServices = *includeServices
- // read input file contents
- ctx.inputData, err = readFile(inputFile)
+ // read API definition from input file
+ ctx.inputData, err = ioutil.ReadFile(ctx.inputFile)
if err != nil {
- return err
+ return fmt.Errorf("reading input file %s failed: %v", ctx.inputFile, err)
}
+
// parse JSON data into objects
- jsonRoot, err := parseJSON(ctx.inputData)
- if err != nil {
- return err
+ jsonRoot := new(jsongo.JSONNode)
+ if err := json.Unmarshal(ctx.inputData, jsonRoot); err != nil {
+ return fmt.Errorf("unmarshalling JSON failed: %v", err)
}
ctx.packageData, err = parsePackage(ctx, jsonRoot)
if err != nil {
- return err
+ return fmt.Errorf("parsing package %s failed: %v", ctx.packageName, err)
+ }
+
+ // generate Go package code
+ var buf bytes.Buffer
+ if err := generatePackage(ctx, &buf); err != nil {
+ return fmt.Errorf("generating code for package %s failed: %v", ctx.packageName, err)
}
// create output directory
packageDir := filepath.Dir(ctx.outputFile)
- if err := os.MkdirAll(packageDir, 0777); err != nil {
- return fmt.Errorf("creating output directory %q failed: %v", packageDir, err)
+ if err := os.MkdirAll(packageDir, 06); err != nil {
+ return fmt.Errorf("creating output dir %s failed: %v", packageDir, err)
}
- // open output file
- f, err := os.Create(ctx.outputFile)
- if err != nil {
- return fmt.Errorf("creating output file %q failed: %v", ctx.outputFile, err)
- }
- defer f.Close()
-
- // generate Go package code
- w := bufio.NewWriter(f)
- if err := generatePackage(ctx, w); err != nil {
- return err
+ // write generated code to output file
+ if err := ioutil.WriteFile(ctx.outputFile, buf.Bytes(), 0666); err != nil {
+ return fmt.Errorf("writing to output file %s failed: %v", ctx.outputFile, err)
}
// go format the output file (fail probably means the output is not compilable)
@@ -154,35 +151,14 @@ func generateFromFile(inputFile, outputDir string) error {
// count number of lines in generated output file
cmd = exec.Command("wc", "-l", ctx.outputFile)
if output, err := cmd.CombinedOutput(); err != nil {
- log.Warnf("wc command failed: %v\n%s", err, string(output))
+ logf("wc command failed: %v\n%s", err, string(output))
} else {
- logf("generated lines: %s", output)
+ logf("number of generated lines: %s", output)
}
return nil
}
-// readFile reads content of a file into memory
-func readFile(inputFile string) ([]byte, error) {
- inputData, err := ioutil.ReadFile(inputFile)
- if err != nil {
- return nil, fmt.Errorf("reading data from file failed: %v", err)
- }
-
- return inputData, nil
-}
-
-// parseJSON parses a JSON data into an in-memory tree
-func parseJSON(inputData []byte) (*jsongo.JSONNode, error) {
- root := jsongo.JSONNode{}
-
- if err := json.Unmarshal(inputData, &root); err != nil {
- return nil, fmt.Errorf("unmarshalling JSON failed: %v", err)
- }
-
- return &root, nil
-}
-
func logf(f string, v ...interface{}) {
if *debug {
logrus.Debugf(f, v...)
diff --git a/cmd/binapi-generator/objects.go b/cmd/binapi-generator/objects.go
index 8f5e8ef..e3270de 100644
--- a/cmd/binapi-generator/objects.go
+++ b/cmd/binapi-generator/objects.go
@@ -1,15 +1,18 @@
package main
+import "fmt"
+
// Package represents collection of objects parsed from VPP binary API JSON data
type Package struct {
- APIVersion string
- Services []Service
- Enums []Enum
- Aliases []Alias
- Types []Type
- Unions []Union
- Messages []Message
- RefMap map[string]string
+ Version string
+ CRC string
+ Services []Service
+ Enums []Enum
+ Aliases []Alias
+ Types []Type
+ Unions []Union
+ Messages []Message
+ RefMap map[string]string
}
// Service represents VPP binary API service
@@ -85,3 +88,43 @@ const (
eventMessage // VPP event message
otherMessage // other VPP message
)
+
+// printPackage prints all loaded objects for package
+func printPackage(pkg *Package) {
+ if len(pkg.Enums) > 0 {
+ logf("loaded %d enums:", len(pkg.Enums))
+ for k, enum := range pkg.Enums {
+ logf(" - enum #%d\t%+v", k, enum)
+ }
+ }
+ if len(pkg.Unions) > 0 {
+ logf("loaded %d unions:", len(pkg.Unions))
+ for k, union := range pkg.Unions {
+ logf(" - union #%d\t%+v", k, union)
+ }
+ }
+ if len(pkg.Types) > 0 {
+ logf("loaded %d types:", len(pkg.Types))
+ for _, typ := range pkg.Types {
+ logf(" - type: %q (%d fields)", typ.Name, len(typ.Fields))
+ }
+ }
+ if len(pkg.Messages) > 0 {
+ logf("loaded %d messages:", len(pkg.Messages))
+ for _, msg := range pkg.Messages {
+ logf(" - message: %q (%d fields)", msg.Name, len(msg.Fields))
+ }
+ }
+ if len(pkg.Services) > 0 {
+ logf("loaded %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(" - service: %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
index 662ed34..562abab 100644
--- a/cmd/binapi-generator/parse.go
+++ b/cmd/binapi-generator/parse.go
@@ -21,6 +21,7 @@ import (
"strings"
"github.com/bennyscetbun/jsongo"
+ "github.com/sirupsen/logrus"
)
// top level objects
@@ -32,6 +33,7 @@ const (
objServices = "services"
objAliases = "aliases"
vlAPIVersion = "vl_api_version"
+ objOptions = "options"
)
// various object fields
@@ -64,11 +66,32 @@ const (
fieldMetaLimit = "limit"
)
+// module options
+const (
+ versionOption = "version"
+)
+
// parsePackage parses provided JSON data into objects prepared for code generation
func parsePackage(ctx *context, jsonRoot *jsongo.JSONNode) (*Package, error) {
- logf(" %s (version: %s) contains: %d services, %d messages, %d types, %d enums, %d unions, %d aliases",
+ pkg := Package{
+ RefMap: make(map[string]string),
+ }
+
+ // 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) contains: %d services, %d messages, %d types, %d enums, %d unions, %d aliases",
ctx.packageName,
- jsonRoot.Map(vlAPIVersion).Get(),
+ pkg.Version, pkg.CRC,
jsonRoot.Map(objServices).Len(),
jsonRoot.Map(objMessages).Len(),
jsonRoot.Map(objTypes).Len(),
@@ -77,11 +100,6 @@ func parsePackage(ctx *context, jsonRoot *jsongo.JSONNode) (*Package, error) {
jsonRoot.Map(objAliases).Len(),
)
- pkg := Package{
- APIVersion: jsonRoot.Map(vlAPIVersion).Get().(string),
- RefMap: make(map[string]string),
- }
-
// parse enums
enums := jsonRoot.Map(objEnums)
pkg.Enums = make([]Enum, enums.Len())
@@ -201,46 +219,6 @@ func parsePackage(ctx *context, jsonRoot *jsongo.JSONNode) (*Package, error) {
return &pkg, nil
}
-// printPackage prints all loaded objects for package
-func printPackage(pkg *Package) {
- if len(pkg.Enums) > 0 {
- logf("loaded %d enums:", len(pkg.Enums))
- for k, enum := range pkg.Enums {
- logf(" - enum #%d\t%+v", k, enum)
- }
- }
- if len(pkg.Unions) > 0 {
- logf("loaded %d unions:", len(pkg.Unions))
- for k, union := range pkg.Unions {
- logf(" - union #%d\t%+v", k, union)
- }
- }
- if len(pkg.Types) > 0 {
- logf("loaded %d types:", len(pkg.Types))
- for _, typ := range pkg.Types {
- logf(" - type: %q (%d fields)", typ.Name, len(typ.Fields))
- }
- }
- if len(pkg.Messages) > 0 {
- logf("loaded %d messages:", len(pkg.Messages))
- for _, msg := range pkg.Messages {
- logf(" - message: %q (%d fields)", msg.Name, len(msg.Fields))
- }
- }
- if len(pkg.Services) > 0 {
- logf("loaded %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(" - service: %q -> %q %s", svc.RequestType, svc.ReplyType, info)
- }
- }
-}
-
// parseEnum parses VPP binary API enum object from JSON node
func parseEnum(ctx *context, enumNode *jsongo.JSONNode) (*Enum, error) {
if enumNode.Len() == 0 || enumNode.At(0).GetType() != jsongo.TypeValue {
@@ -466,7 +444,7 @@ func parseField(ctx *context, field *jsongo.JSONNode) (*Field, error) {
case fieldMetaLimit:
f.Meta.Limit = int(metaNode.Get().(float64))
default:
- log.Warnf("unknown meta info (%s) for field (%s)", metaName, fieldName)
+ logrus.Warnf("unknown meta info (%s) for field (%s)", metaName, fieldName)
}
}
} else {
@@ -491,7 +469,7 @@ func parseService(ctx *context, svcName string, svcNode *jsongo.JSONNode) (*Serv
}
svc := Service{
- Name: ctx.moduleName + "." + svcName,
+ Name: svcName,
RequestType: svcName,
}
@@ -526,7 +504,7 @@ func parseService(ctx *context, svcName string, svcNode *jsongo.JSONNode) (*Serv
if len(svc.Events) > 0 {
// EVENT service
if !strings.HasPrefix(svc.RequestType, serviceEventPrefix) {
- log.Debugf("unusual EVENTS service: %+v\n"+
+ logrus.Debugf("unusual EVENTS service: %+v\n"+
"- events service %q does not have %q prefix in request.",
svc, svc.Name, serviceEventPrefix)
}
@@ -534,7 +512,7 @@ func parseService(ctx *context, svcName string, svcNode *jsongo.JSONNode) (*Serv
// STREAM service
if !strings.HasSuffix(svc.RequestType, serviceDumpSuffix) ||
!strings.HasSuffix(svc.ReplyType, serviceDetailsSuffix) {
- log.Debugf("unusual STREAM service: %+v\n"+
+ 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)
}
@@ -542,7 +520,7 @@ func parseService(ctx *context, svcName string, svcNode *jsongo.JSONNode) (*Serv
// REQUEST service
// some messages might have `null` reply (for example: memclnt)
if !strings.HasSuffix(svc.ReplyType, serviceReplySuffix) {
- log.Debugf("unusual REQUEST service: %+v\n"+
+ logrus.Debugf("unusual REQUEST service: %+v\n"+
"- service %q does not have %q suffix in reply.",
svc, svc.Name, serviceReplySuffix)
}
diff --git a/cmd/binapi-generator/types.go b/cmd/binapi-generator/types.go
index d056251..90c890f 100644
--- a/cmd/binapi-generator/types.go
+++ b/cmd/binapi-generator/types.go
@@ -18,6 +18,8 @@ import (
"fmt"
"strconv"
"strings"
+
+ "github.com/sirupsen/logrus"
)
// toApiType returns name that is used as type reference in VPP binary API
@@ -62,7 +64,7 @@ func convertToGoType(ctx *context, binapiType string) (typ string) {
typ = binapiType
default:
// fallback type
- log.Warnf("found unknown VPP binary API type %q, using byte", binapiType)
+ logrus.Warnf("found unknown VPP binary API type %q, using byte", binapiType)
typ = "byte"
}
}