Recognize stat_dir_type_empty
[govpp.git] / binapigen / run.go
index 441c43d..d632519 100644 (file)
@@ -16,74 +16,141 @@ package binapigen
 
 import (
        "fmt"
+       "io/ioutil"
        "os"
+       "path"
        "path/filepath"
+       "regexp"
+       "strings"
 
        "github.com/sirupsen/logrus"
 
        "git.fd.io/govpp.git/binapigen/vppapi"
 )
 
-var debugMode = true
+type Options struct {
+       OutputDir        string // output directory for generated files
+       ImportPrefix     string // prefix for import paths
+       NoVersionInfo    bool   // disables generating version info
+       NoSourcePathInfo bool   // disables the 'source: /path' comment
+}
 
-func logf(f string, v ...interface{}) {
-       if debugMode {
-               logrus.Debugf(f, v...)
+func Run(apiDir string, filesToGenerate []string, opts Options, f func(*Generator) error) {
+       if err := run(apiDir, filesToGenerate, opts, f); err != nil {
+               fmt.Fprintf(os.Stderr, "%s: %v\n", filepath.Base(os.Args[0]), err)
+               os.Exit(1)
        }
 }
 
-func GenerateBinapiFile(gen *Generator, file *File, outputDir string) *GenFile {
-       packageDir := filepath.Join(outputDir, file.PackageName)
-       filename := filepath.Join(packageDir, file.PackageName+outputFileExt)
-
-       g := gen.NewGenFile(filename)
-       g.file = file
-       g.packageDir = filepath.Join(outputDir, file.PackageName)
+func run(apiDir string, filesToGenerate []string, opts Options, fn func(*Generator) error) error {
+       apiFiles, err := vppapi.ParseDir(apiDir)
+       if err != nil {
+               return err
+       }
 
-       generatePackage(g, &g.buf)
+       if opts.ImportPrefix == "" {
+               opts.ImportPrefix, err = resolveImportPath(opts.OutputDir)
+               if err != nil {
+                       return fmt.Errorf("cannot resolve import path for output dir %s: %w", opts.OutputDir, err)
+               }
+               logrus.Debugf("resolved import path prefix: %s", opts.ImportPrefix)
+       }
 
-       return g
-}
+       gen, err := New(opts, apiFiles, filesToGenerate)
+       if err != nil {
+               return err
+       }
 
-func GenerateRPC(gen *Generator, file *File, outputDir string) *GenFile {
-       packageDir := filepath.Join(outputDir, file.PackageName)
-       filename := filepath.Join(packageDir, file.PackageName+rpcFileSuffix+outputFileExt)
+       gen.vppVersion = vppapi.ResolveVPPVersion(apiDir)
+       if gen.vppVersion == "" {
+               gen.vppVersion = "unknown"
+       }
 
-       g := gen.NewGenFile(filename)
-       g.file = file
-       g.packageDir = filepath.Join(outputDir, file.PackageName)
+       if fn == nil {
+               GenerateDefault(gen)
+       } else {
+               if err := fn(gen); err != nil {
+                       return err
+               }
+       }
+       if err = gen.Generate(); err != nil {
+               return err
+       }
 
-       generatePackageRPC(g, &g.buf)
+       return nil
+}
 
-       return g
+func GenerateDefault(gen *Generator) {
+       for _, file := range gen.Files {
+               if !file.Generate {
+                       continue
+               }
+               GenerateAPI(gen, file)
+               GenerateRPC(gen, file)
+       }
 }
 
-func Run(apiDir string, opts Options, f func(*Generator) error) {
-       if err := run(apiDir, opts, f); err != nil {
-               fmt.Fprintf(os.Stderr, "%s: %v\n", filepath.Base(os.Args[0]), err)
-               os.Exit(1)
+var Logger = logrus.New()
+
+func init() {
+       if debug := os.Getenv("DEBUG_GOVPP"); strings.Contains(debug, "binapigen") {
+               Logger.SetLevel(logrus.DebugLevel)
+               logrus.SetLevel(logrus.DebugLevel)
        }
 }
 
-func run(apiDir string, opts Options, f func(*Generator) error) error {
-       // parse API files
-       apifiles, err := vppapi.ParseDir(apiDir)
+func logf(f string, v ...interface{}) {
+       Logger.Debugf(f, v...)
+}
+
+// resolveImportPath tries to resolve import path for a directory.
+func resolveImportPath(dir string) (string, error) {
+       absPath, err := filepath.Abs(dir)
        if err != nil {
-               return err
+               return "", err
        }
-
-       g, err := New(opts, apifiles)
+       modRoot := findGoModuleRoot(absPath)
+       if modRoot == "" {
+               return "", err
+       }
+       modPath, err := readModulePath(path.Join(modRoot, "go.mod"))
        if err != nil {
-               return err
+               return "", err
        }
-
-       if err := f(g); err != nil {
-               return err
+       relDir, err := filepath.Rel(modRoot, absPath)
+       if err != nil {
+               return "", err
        }
+       return filepath.Join(modPath, relDir), nil
+}
 
-       if err = g.Generate(); err != nil {
-               return err
+// findGoModuleRoot looks for enclosing Go module.
+func findGoModuleRoot(dir string) (root string) {
+       dir = filepath.Clean(dir)
+       for {
+               if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
+                       return dir
+               }
+               d := filepath.Dir(dir)
+               if d == dir {
+                       break
+               }
+               dir = d
        }
+       return ""
+}
 
-       return nil
+var modulePathRE = regexp.MustCompile(`module[ \t]+([^ \t\r\n]+)`)
+
+// readModulePath reads module path from go.mod file.
+func readModulePath(gomod string) (string, error) {
+       data, err := ioutil.ReadFile(gomod)
+       if err != nil {
+               return "", err
+       }
+       m := modulePathRE.FindSubmatch(data)
+       if m == nil {
+               return "", err
+       }
+       return string(m[1]), nil
 }