Change module name to go.fd.io/govpp
[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         "go.fd.io/govpp/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         NoSourcePathInfo bool   // disables the 'source: /path' comment
36 }
37
38 func Run(apiDir string, filesToGenerate []string, opts Options, f func(*Generator) error) {
39         if err := run(apiDir, filesToGenerate, opts, f); err != nil {
40                 fmt.Fprintf(os.Stderr, "%s: %v\n", filepath.Base(os.Args[0]), err)
41                 os.Exit(1)
42         }
43 }
44
45 func run(apiDir string, filesToGenerate []string, opts Options, fn func(*Generator) error) error {
46         apiFiles, err := vppapi.ParseDir(apiDir)
47         if err != nil {
48                 return err
49         }
50
51         if opts.ImportPrefix == "" {
52                 opts.ImportPrefix, err = resolveImportPath(opts.OutputDir)
53                 if err != nil {
54                         return fmt.Errorf("cannot resolve import path for output dir %s: %w", opts.OutputDir, err)
55                 }
56                 logrus.Debugf("resolved import path prefix: %s", opts.ImportPrefix)
57         }
58
59         gen, err := New(opts, apiFiles, filesToGenerate)
60         if err != nil {
61                 return err
62         }
63
64         gen.vppVersion = vppapi.ResolveVPPVersion(apiDir)
65         if gen.vppVersion == "" {
66                 gen.vppVersion = "unknown"
67         }
68
69         if fn == nil {
70                 GenerateDefault(gen)
71         } else {
72                 if err := fn(gen); err != nil {
73                         return err
74                 }
75         }
76         if err = gen.Generate(); err != nil {
77                 return err
78         }
79
80         return nil
81 }
82
83 func GenerateDefault(gen *Generator) {
84         for _, file := range gen.Files {
85                 if !file.Generate {
86                         continue
87                 }
88                 GenerateAPI(gen, file)
89                 GenerateRPC(gen, file)
90         }
91 }
92
93 var Logger = logrus.New()
94
95 func init() {
96         if debug := os.Getenv("DEBUG_GOVPP"); strings.Contains(debug, "binapigen") {
97                 Logger.SetLevel(logrus.DebugLevel)
98                 logrus.SetLevel(logrus.DebugLevel)
99         }
100 }
101
102 func logf(f string, v ...interface{}) {
103         Logger.Debugf(f, v...)
104 }
105
106 // resolveImportPath tries to resolve import path for a directory.
107 func resolveImportPath(dir string) (string, error) {
108         absPath, err := filepath.Abs(dir)
109         if err != nil {
110                 return "", err
111         }
112         modRoot := findGoModuleRoot(absPath)
113         if modRoot == "" {
114                 return "", err
115         }
116         modPath, err := readModulePath(path.Join(modRoot, "go.mod"))
117         if err != nil {
118                 return "", err
119         }
120         relDir, err := filepath.Rel(modRoot, absPath)
121         if err != nil {
122                 return "", err
123         }
124         return filepath.Join(modPath, relDir), nil
125 }
126
127 // findGoModuleRoot looks for enclosing Go module.
128 func findGoModuleRoot(dir string) (root string) {
129         dir = filepath.Clean(dir)
130         for {
131                 if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
132                         return dir
133                 }
134                 d := filepath.Dir(dir)
135                 if d == dir {
136                         break
137                 }
138                 dir = d
139         }
140         return ""
141 }
142
143 var modulePathRE = regexp.MustCompile(`module[ \t]+([^ \t\r\n]+)`)
144
145 // readModulePath reads module path from go.mod file.
146 func readModulePath(gomod string) (string, error) {
147         data, err := ioutil.ReadFile(gomod)
148         if err != nil {
149                 return "", err
150         }
151         m := modulePathRE.FindSubmatch(data)
152         if m == nil {
153                 return "", err
154         }
155         return string(m[1]), nil
156 }