Refactored binapi generator with message encoding
[govpp.git] / cmd / binapi-generator / main.go
index b73a699..c0bdbb9 100644 (file)
 package main
 
 import (
-       "bufio"
-       "encoding/json"
        "flag"
        "fmt"
-       "io/ioutil"
        "os"
-       "os/exec"
-       "path/filepath"
-       "strings"
 
-       "github.com/bennyscetbun/jsongo"
        "github.com/sirupsen/logrus"
-)
 
-var (
-       inputFile       = flag.String("input-file", "", "Input JSON file.")
-       inputDir        = flag.String("input-dir", ".", "Input directory with JSON files.")
-       outputDir       = flag.String("output-dir", ".", "Output directory where package folders will be generated.")
-       includeAPIVer   = flag.Bool("include-apiver", false, "Whether to include VlAPIVersion in generated file.")
-       debug           = flag.Bool("debug", false, "Turn on debug mode.")
-       continueOnError = flag.Bool("continue-onerror", false, "Wheter to continue with next file on error.")
+       "git.fd.io/govpp.git/binapigen"
+       "git.fd.io/govpp.git/binapigen/vppapi"
+       "git.fd.io/govpp.git/version"
 )
 
 func init() {
-       flag.Parse()
-       if *debug {
-               logrus.SetLevel(logrus.DebugLevel)
-       }
-}
-
-func logf(f string, v ...interface{}) {
-       if *debug {
-               logrus.Debugf(f, v...)
+       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()
        }
 }
 
-var log = logrus.Logger{
-       Level:     logrus.InfoLevel,
-       Formatter: &logrus.TextFormatter{},
-       Out:       os.Stdout,
-}
-
 func main() {
-       if *inputFile == "" && *inputDir == "" {
-               fmt.Fprintln(os.Stderr, "ERROR: input-file or input-dir must be specified")
-               os.Exit(1)
-       }
-
-       if *inputFile != "" {
-               // process one input file
-               if err := generateFromFile(*inputFile, *outputDir); err != nil {
-                       fmt.Fprintf(os.Stderr, "ERROR: code generation from %s failed: %v\n", *inputFile, err)
-                       os.Exit(1)
-               }
-       } else {
-               // process all files in specified directory
-               files, err := getInputFiles(*inputDir)
-               if err != nil {
-                       fmt.Fprintf(os.Stderr, "ERROR: code generation failed: %v\n", err)
-                       os.Exit(1)
-               }
-               for _, file := range files {
-                       if err := generateFromFile(file, *outputDir); err != nil {
-                               fmt.Fprintf(os.Stderr, "ERROR: code generation from %s failed: %v\n", file, err)
-                               if *continueOnError {
-                                       continue
-                               }
-                               os.Exit(1)
-                       }
-               }
-       }
-}
-
-// getInputFiles returns all input files located in specified directory
-func getInputFiles(inputDir string) (res []string, err error) {
-       files, err := ioutil.ReadDir(inputDir)
-       if err != nil {
-               return nil, fmt.Errorf("reading directory %s failed: %v", inputDir, err)
-       }
-       for _, f := range files {
-               if strings.HasSuffix(f.Name(), inputFileExt) {
-                       res = append(res, filepath.Join(inputDir, f.Name()))
-               }
-       }
-       return res, nil
-}
-
-// generateFromFile generates Go package from one input JSON file
-func generateFromFile(inputFile, outputDir string) error {
-       logf("generating from file: %q", inputFile)
-       defer logf("--------------------------------------")
+       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()
 
-       ctx, err := getContext(inputFile, outputDir)
-       if err != nil {
-               return err
+       if *printVersion {
+               fmt.Fprintln(os.Stdout, version.Info())
+               os.Exit(0)
        }
 
-       // read input file contents
-       ctx.inputData, err = readFile(inputFile)
-       if err != nil {
-               return err
-       }
-       // parse JSON data into objects
-       jsonRoot, err := parseJSON(ctx.inputData)
-       if err != nil {
-               return err
-       }
-       ctx.packageData, err = parsePackage(ctx, jsonRoot)
-       if err != nil {
-               return err
+       if flag.NArg() == 1 && flag.Arg(0) == "version" {
+               fmt.Fprintln(os.Stdout, version.Verbose())
+               os.Exit(0)
        }
 
-       // 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)
-       }
-       // 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
-       }
+       var opts binapigen.Options
 
-       // go format the output file (fail probably means the output is not compilable)
-       cmd := exec.Command("gofmt", "-w", ctx.outputFile)
-       if output, err := cmd.CombinedOutput(); err != nil {
-               return fmt.Errorf("gofmt failed: %v\n%s", err, string(output))
+       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 {
+               opts.FilesToGenerate = append(opts.FilesToGenerate, flag.Args()...)
        }
 
-       // 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))
+       // prepare options
+       if ver := os.Getenv("VPP_API_VERSION"); ver != "" {
+               // use version from env var if set
+               opts.VPPVersion = ver
        } else {
-               logf("generated lines: %s", output)
+               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
 
-       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)
+       if *debugMode {
+               logrus.SetLevel(logrus.DebugLevel)
+               logrus.Debug("debug mode enabled")
        }
 
-       return inputData, nil
-}
-
-// parseJSON parses a JSON data into an in-memory tree
-func parseJSON(inputData []byte) (*jsongo.JSONNode, error) {
-       root := jsongo.JSONNode{}
+       apiDir := *theApiDir
+       outputDir := *theOutputDir
 
-       if err := json.Unmarshal(inputData, &root); err != nil {
-               return nil, fmt.Errorf("unmarshalling JSON failed: %v", err)
-       }
-
-       return &root, nil
+       binapigen.Run(apiDir, opts, func(g *binapigen.Generator) error {
+               for _, file := range g.Files {
+                       if !file.Generate {
+                               continue
+                       }
+                       binapigen.GenerateBinapiFile(g, file, outputDir)
+                       if g.IncludeServices && file.Service != nil {
+                               binapigen.GenerateRPC(g, file, outputDir)
+                       }
+               }
+               return nil
+       })
 }