Add support for using multiple generated versions
[govpp.git] / cmd / binapi-generator / generate.go
index 48c3a41..f22a035 100644 (file)
@@ -25,9 +25,11 @@ import (
 )
 
 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
-       inputFileExt       = ".api.json"               // file extension of the VPP binary API files
-       outputFileExt      = ".ba.go"                  // file extension of the Go generated files
+       constAPIVersionCrc = "APIVersionCrc"           // name for the API version CRC constant
 )
 
 // context is a structure storing data for code generation
@@ -37,6 +39,9 @@ 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
+
        moduleName  string // name of the source VPP module
        packageName string // name of the Go package being generated
 
@@ -83,10 +88,9 @@ func generatePackage(ctx *context, w *bufio.Writer) error {
        generateHeader(ctx, w)
        generateImports(ctx, w)
 
-       if *includeAPIVer {
-               const APIVerConstName = "VlAPIVersion"
-               fmt.Fprintf(w, "// %s represents version of the binary API module.\n", APIVerConstName)
-               fmt.Fprintf(w, "const %s = %v\n", APIVerConstName, ctx.packageData.APIVersion)
+       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)
        }
 
@@ -148,6 +152,14 @@ func generatePackage(ctx *context, w *bufio.Writer) error {
                        fmt.Fprintf(w, "\tapi.RegisterMessage((*%s)(nil), \"%s\")\n", name, ctx.moduleName+"."+name)
                }
                fmt.Fprintln(w, "}")
+               fmt.Fprintln(w)
+
+               fmt.Fprintln(w, "var Messages = []api.Message{")
+               for _, msg := range ctx.packageData.Messages {
+                       name := camelCaseName(msg.Name)
+                       fmt.Fprintf(w, "\t(*%s)(nil),\n", name)
+               }
+               fmt.Fprintln(w, "}")
        }
 
        // flush the data:
@@ -194,9 +206,9 @@ 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 \"%s\"\n", govppApiImportPath)
-       fmt.Fprintf(w, "import \"%s\"\n", "github.com/lunixbochs/struc")
-       fmt.Fprintf(w, "import \"%s\"\n", "bytes")
+       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.Fprintln(w)
 
        fmt.Fprintf(w, "// Reference imports to suppress errors if they are not otherwise used.\n")
@@ -204,6 +216,13 @@ func generateImports(ctx *context, w io.Writer) {
        fmt.Fprintf(w, "var _ = struc.Pack\n")
        fmt.Fprintf(w, "var _ = bytes.NewBuffer\n")
        fmt.Fprintln(w)
+
+       /*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)*/
 }
 
 // generateComment writes generated comment for the object into w
@@ -214,6 +233,10 @@ func generateComment(ctx *context, w io.Writer, goName string, vppName string, o
                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)
        }
@@ -271,7 +294,7 @@ func generateServices(ctx *context, w *bufio.Writer, services []Service) {
 
        // generate interface
        fmt.Fprintf(w, "type %s interface {\n", "Services")
-       for _, svc := range ctx.packageData.Services {
+       for _, svc := range services {
                generateService(ctx, w, &svc)
        }
        fmt.Fprintln(w, "}")
@@ -284,7 +307,14 @@ func generateService(ctx *context, w io.Writer, svc *Service) {
        reqTyp := camelCaseName(svc.RequestType)
 
        // method name is same as parameter type name by default
-       method := svc.MethodName()
+       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("*%s", reqTyp)
        returns := "error"
        if replyType := camelCaseName(svc.ReplyType); replyType != "" {
@@ -450,7 +480,7 @@ func generateType(ctx *context, w io.Writer, typ *Type) {
        for i, field := range typ.Fields {
                // skip internal fields
                switch strings.ToLower(field.Name) {
-               case "crc", "_vl_msg_id":
+               case crcField, msgIdField:
                        continue
                }
 
@@ -488,17 +518,17 @@ func generateMessage(ctx *context, w io.Writer, msg *Message) {
        n := 0
        for i, field := range msg.Fields {
                if i == 1 {
-                       if field.Name == "client_index" {
+                       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 == "context" {
+                       } else if field.Name == contextField {
                                // reply needs "context" as the second member
                                msgType = replyMessage
                        }
                } else if i == 2 {
-                       if wasClientIndex && field.Name == "context" {
+                       if wasClientIndex && field.Name == contextField {
                                // request needs "client_index" as the second member
                                // and "context" as the third member
                                msgType = requestMessage
@@ -507,9 +537,9 @@ func generateMessage(ctx *context, w io.Writer, msg *Message) {
 
                // skip internal fields
                switch strings.ToLower(field.Name) {
-               case "crc", "_vl_msg_id":
+               case crcField, msgIdField:
                        continue
-               case "client_index", "context":
+               case clientIndexField, contextField:
                        if n == 0 {
                                continue
                        }
@@ -550,9 +580,10 @@ func generateField(ctx *context, w io.Writer, fields []Field, i int) {
        }
 
        dataType := convertToGoType(ctx, field.Type)
-
        fieldType := dataType
-       if field.IsArray() {
+
+       // check if it is array
+       if field.Length > 0 || field.SizeFrom != "" {
                if dataType == "uint8" {
                        dataType = "byte"
                }