X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=cmd%2Fbinapi-generator%2Fmain.go;h=a730df4944355e90f2559e5d6b82076db3ccae2b;hb=c94a962279858fb13eaacc689f47aed358373e44;hp=c66fc4fa28736bd39b9997fdb9ea175d773610e3;hpb=8c64ee581e9cda9030c79c3d85a5ac76d8fd1bca;p=govpp.git diff --git a/cmd/binapi-generator/main.go b/cmd/binapi-generator/main.go index c66fc4f..a730df4 100644 --- a/cmd/binapi-generator/main.go +++ b/cmd/binapi-generator/main.go @@ -15,203 +15,105 @@ package main import ( - "bytes" - "encoding/json" "flag" "fmt" - "io/ioutil" "os" - "os/exec" "path/filepath" "strings" + "unicode" - "github.com/bennyscetbun/jsongo" "github.com/sirupsen/logrus" - "git.fd.io/govpp.git/version" + "git.fd.io/govpp.git/binapigen" + "git.fd.io/govpp.git/binapigen/vppapi" + "git.fd.io/govpp.git/internal/version" ) -var ( - theInputFile = flag.String("input-file", "", "Input file with VPP API in JSON format.") - 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.") - - continueOnError = flag.Bool("continue-onerror", false, "Continue with next file on error.") - debugMode = flag.Bool("debug", os.Getenv("GOVPP_DEBUG") != "", "Enable debug mode.") +func init() { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "USAGE\n") + fmt.Fprintf(os.Stderr, " Parse API_FILES and generate Go bindings\n") + fmt.Fprintf(os.Stderr, " Provide API_FILES by file name, or with full path including extension.\n") + fmt.Fprintf(os.Stderr, " %s [OPTION] API_FILES\n\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "OPTIONS\n") + flag.PrintDefaults() + fmt.Fprintf(os.Stderr, "\nEXAMPLES:\n") + fmt.Fprintf(os.Stderr, " %s \\\n", os.Args[0]) + fmt.Fprintf(os.Stderr, " --input-dir=$VPP/build-root/install-vpp-native/vpp/share/vpp/api/ \\\n") + fmt.Fprintf(os.Stderr, " --output-dir=~/output \\\n") + fmt.Fprintf(os.Stderr, " interface ip\n") + fmt.Fprintf(os.Stderr, " Assuming --input-dir contains interface.api.json & ip.api.json\n") + } +} - printVersion = flag.Bool("version", false, "Prints current version and exits.") -) +func printErrorAndExit(msg string) { + fmt.Fprintf(os.Stderr, "Error: %s\n\n", msg) + flag.Usage() + os.Exit(1) +} func main() { + var ( + theApiDir = flag.String("input-dir", vppapi.DefaultDir, "Input directory containing API files. (e.g. )") + theOutputDir = flag.String("output-dir", "binapi", "Output directory where code will be generated.") + importPrefix = flag.String("import-prefix", "", "Prefix imports in the generated go code. \nE.g. other API Files (e.g. api_file.ba.go) will be imported with :\nimport (\n api_file \"/api_file\"\n)") + generatorPlugins = flag.String("gen", "rpc", "List of generator plugins to run for files.") + theInputFile = flag.String("input-file", "", "DEPRECATED: Use program arguments to define files to generate.") + + printVersion = flag.Bool("version", false, "Prints version and exits.") + debugLog = flag.Bool("debug", false, "Enable verbose logging.") + noVersionInfo = flag.Bool("no-version-info", false, "Disable version info in generated files.") + noSourcePathInfo = flag.Bool("no-source-path-info", false, "Disable source path info in generated files.") + ) 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 { + if *debugLog { 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) - } -} - -func run(inputFile, inputDir string, outputDir string, continueErr bool) error { - if inputFile == "" && inputDir == "" { - return fmt.Errorf("input-file or input-dir must be specified") } - if inputFile != "" { - // process one input file - if err := generateFromFile(inputFile, outputDir); err != nil { - return fmt.Errorf("code generation from %s failed: %v\n", inputFile, err) + var filesToGenerate []string + if *theInputFile != "" { + if flag.NArg() > 0 { + printErrorAndExit("input-file cannot be combined with files to generate in arguments") } + filesToGenerate = append(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); 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) - } - } - } + filesToGenerate = append(filesToGenerate, flag.Args()...) } - 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) + opts := binapigen.Options{ + ImportPrefix: *importPrefix, + OutputDir: *theOutputDir, + NoVersionInfo: *noVersionInfo, + NoSourcePathInfo: *noSourcePathInfo, } - 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())) + if opts.OutputDir == "binapi" { + if wd, _ := os.Getwd(); filepath.Base(wd) == "binapi" { + opts.OutputDir = "." } } - return files, nil -} + apiDir := *theApiDir + genPlugins := strings.FieldsFunc(*generatorPlugins, func(c rune) bool { + return !unicode.IsLetter(c) && !unicode.IsNumber(c) + }) -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) error { - // create generator context - ctx, err := newContext(inputFile, outputDir) - if err != nil { - return err - } - - 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 - - // 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) - } - - // 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, 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, 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) - 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)) - } - - return nil -} - -func logf(f string, v ...interface{}) { - if *debugMode { - logrus.Debugf(f, v...) - } + binapigen.Run(apiDir, filesToGenerate, opts, func(gen *binapigen.Generator) error { + for _, file := range gen.Files { + if !file.Generate { + continue + } + binapigen.GenerateAPI(gen, file) + for _, p := range genPlugins { + if err := binapigen.RunPlugin(p, gen, file); err != nil { + return err + } + } + } + return nil + }) }