88e32b76c2e9270078d3d041a657fc49a73ea4c4
[govpp.git] / binapigen / run.go
1 //  Copyright (c) 2020 Cisco and/or its affiliates.
2 //
3 //  Licensed under the Apache License, Version 2.0 (the "License");
4 //  you may not use this file except in compliance with the License.
5 //  You may obtain a copy of the License at:
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 //  Unless required by applicable law or agreed to in writing, software
10 //  distributed under the License is distributed on an "AS IS" BASIS,
11 //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 //  See the License for the specific language governing permissions and
13 //  limitations under the License.
14
15 package binapigen
16
17 import (
18         "fmt"
19         "io/ioutil"
20         "os"
21         "path"
22         "path/filepath"
23         "regexp"
24         "strings"
25
26         "github.com/sirupsen/logrus"
27
28         "git.fd.io/govpp.git/binapigen/vppapi"
29 )
30
31 type Options struct {
32         OutputDir     string // output directory for generated files
33         ImportPrefix  string // prefix for import paths
34         NoVersionInfo bool   // disables generating version info
35 }
36
37 func Run(apiDir string, filesToGenerate []string, opts Options, f func(*Generator) error) {
38         if err := run(apiDir, filesToGenerate, opts, f); err != nil {
39                 fmt.Fprintf(os.Stderr, "%s: %v\n", filepath.Base(os.Args[0]), err)
40                 os.Exit(1)
41         }
42 }
43
44 func run(apiDir string, filesToGenerate []string, opts Options, fn func(*Generator) error) error {
45         apifiles, err := vppapi.ParseDir(apiDir)
46         if err != nil {
47                 return err
48         }
49
50         if opts.ImportPrefix == "" {
51                 opts.ImportPrefix = resolveImportPath(opts.OutputDir)
52                 logrus.Debugf("resolved import prefix: %s", opts.ImportPrefix)
53         }
54
55         gen, err := New(opts, apifiles, filesToGenerate)
56         if err != nil {
57                 return err
58         }
59
60         gen.vppVersion = vppapi.ResolveVPPVersion(apiDir)
61         if gen.vppVersion == "" {
62                 gen.vppVersion = "unknown"
63         }
64
65         if fn == nil {
66                 GenerateDefault(gen)
67         } else {
68                 if err := fn(gen); err != nil {
69                         return err
70                 }
71         }
72
73         if err = gen.Generate(); err != nil {
74                 return err
75         }
76
77         return nil
78 }
79
80 func GenerateDefault(gen *Generator) {
81         for _, file := range gen.Files {
82                 if !file.Generate {
83                         continue
84                 }
85                 GenerateAPI(gen, file)
86                 GenerateRPC(gen, file)
87         }
88 }
89
90 var Logger = logrus.New()
91
92 func init() {
93         if debug := os.Getenv("DEBUG_GOVPP"); strings.Contains(debug, "binapigen") {
94                 Logger.SetLevel(logrus.DebugLevel)
95                 logrus.SetLevel(logrus.DebugLevel)
96         } else if debug != "" {
97                 Logger.SetLevel(logrus.InfoLevel)
98         } else {
99                 Logger.SetLevel(logrus.WarnLevel)
100         }
101 }
102
103 func logf(f string, v ...interface{}) {
104         Logger.Debugf(f, v...)
105 }
106
107 func resolveImportPath(dir string) string {
108         absPath, err := filepath.Abs(dir)
109         if err != nil {
110                 panic(err)
111         }
112         modRoot := findGoModuleRoot(absPath)
113         if modRoot == "" {
114                 logrus.Fatalf("module root not found at: %s", absPath)
115         }
116         modPath := findModulePath(path.Join(modRoot, "go.mod"))
117         if modPath == "" {
118                 logrus.Fatalf("module path not found")
119         }
120         relDir, err := filepath.Rel(modRoot, absPath)
121         if err != nil {
122                 panic(err)
123         }
124         return filepath.Join(modPath, relDir)
125 }
126
127 func findGoModuleRoot(dir string) (root string) {
128         if dir == "" {
129                 panic("dir not set")
130         }
131         dir = filepath.Clean(dir)
132         // Look for enclosing go.mod.
133         for {
134                 if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
135                         return dir
136                 }
137                 d := filepath.Dir(dir)
138                 if d == dir {
139                         break
140                 }
141                 dir = d
142         }
143         return ""
144 }
145
146 var (
147         modulePathRE = regexp.MustCompile(`module[ \t]+([^ \t\r\n]+)`)
148 )
149
150 func findModulePath(file string) string {
151         data, err := ioutil.ReadFile(file)
152         if err != nil {
153                 return ""
154         }
155         m := modulePathRE.FindSubmatch(data)
156         if m == nil {
157                 return ""
158         }
159         return string(m[1])
160 }