X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=cmd%2Fbinapi-generator%2Fmain.go;h=85ef0eb42a417f2f4b096f4af05ef398883f28a8;hb=e9567fa8c853dda16c54afbd2ba99b7263fa37f1;hp=89a2b2dd6f70e8b9bcb97ac4b3f2c422799c82c1;hpb=ef471318d66dd2832df4dc929d312f7cd5f7009a;p=govpp.git diff --git a/cmd/binapi-generator/main.go b/cmd/binapi-generator/main.go index 89a2b2d..85ef0eb 100644 --- a/cmd/binapi-generator/main.go +++ b/cmd/binapi-generator/main.go @@ -15,169 +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" -) - -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.") - - debug = flag.Bool("debug", debugMode, "Enable debug mode.") + "git.fd.io/govpp.git/binapigen" + "git.fd.io/govpp.git/binapigen/vppapi" + "git.fd.io/govpp.git/version" ) -var debugMode = os.Getenv("DEBUG_BINAPI_GENERATOR") != "" +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") + } +} + +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 *debug { - logrus.SetLevel(logrus.DebugLevel) - } - if err := run(*theInputFile, *theInputDir, *theOutputDir, *continueOnError); err != nil { - logrus.Errorln("binapi-generator:", err) - os.Exit(1) + if *printVersion { + fmt.Fprintln(os.Stdout, version.Info()) + os.Exit(0) } -} -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 *debugLog { + logrus.SetLevel(logrus.DebugLevel) } - 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 -} - -// 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 := 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 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 *debug { - logrus.Debugf(f, v...) - } + apiDir := *theApiDir + genPlugins := strings.FieldsFunc(*generatorPlugins, func(c rune) bool { + return !unicode.IsLetter(c) && !unicode.IsNumber(c) + }) + + 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 + }) }