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
+ })
}