tests: replace pycodestyle with black
[vpp.git] / src / tools / vppapigen / generate_go.py
1 #!/usr/bin/env python3
2
3 import argparse
4 import os
5 import pathlib
6 import subprocess
7 import tarfile
8 import shutil
9
10 import requests
11 import sys
12
13 #
14 # GoVPP API generator generates Go bindings compatible with the local VPP
15 #
16
17
18 def get_go_version(go_root):
19     # Returns version of the installed Go
20     p = subprocess.Popen(
21         ["./go", "version"],
22         cwd=go_root + "/bin",
23         stdout=subprocess.PIPE,
24         universal_newlines=True,
25     )
26     output, _ = p.communicate()
27     output_fmt = output.replace("go version go", "", 1)
28
29     return output_fmt.rstrip("\n")
30
31
32 # Returns version of the installed binary API generator
33 def get_binapi_gen_version(go_path):
34     p = subprocess.Popen(
35         ["./binapi-generator", "-version"],
36         cwd=go_path + "/bin",
37         stdout=subprocess.PIPE,
38         universal_newlines=True,
39     )
40     output, _ = p.communicate()
41     output_fmt = output.replace("govpp", "", 1)
42
43     return output_fmt.rstrip("\n")
44
45
46 # Verifies local Go installation and installs the latest
47 # one if missing
48 def install_golang(go_root):
49     go_bin = go_root + "/bin/go"
50
51     if os.path.exists(go_bin) and os.path.isfile(go_bin):
52         print("Go " + get_go_version(go_root) + " is already installed")
53         return
54
55     filename = (
56         requests.get("https://golang.org/VERSION?m=text").text + ".linux-amd64.tar.gz"
57     )
58     url = "https://dl.google.com/go/" + filename
59
60     print("Go binary not found, installing the latest version...")
61     print("Download url      = %s" % url)
62     print("Install directory = %s" % go_root)
63     text = input("[Y/n] ?")
64
65     if text.strip().lower() != "y" and text.strip().lower() != "yes":
66         print("Aborting...")
67         exit(1)
68
69     go_folders = ["src", "pkg", "bin"]
70
71     for f in go_folders:
72         if not os.path.exists(os.path.join(go_root, f)):
73             os.makedirs(os.path.join(go_root, f))
74     r = requests.get(url)
75     with open("/tmp/" + filename, "wb") as f:
76         f.write(r.content)
77
78     go_tf = tarfile.open("/tmp/" + filename)
79     # Strip /go dir from the go_root path as it will
80     # be created while extracting the tar file
81     go_root_head, _ = os.path.split(go_root)
82     go_tf.extractall(path=go_root_head)
83     go_tf.close()
84     os.remove("/tmp/" + filename)
85
86     print("Go " + get_go_version(go_root) + " was installed")
87
88
89 # Installs latest binary API generator
90 def install_binapi_gen(c, go_root, go_path):
91     os.environ["GO111MODULE"] = "on"
92     if os.path.exists(go_root + "/bin/go") and os.path.isfile(go_root + "/bin/go"):
93         p = subprocess.Popen(
94             ["./go", "get", "git.fd.io/govpp.git/cmd/binapi-generator@" + c],
95             cwd=go_root + "/bin",
96             stdout=subprocess.PIPE,
97             stderr=subprocess.PIPE,
98             universal_newlines=True,
99         )
100         _, error = p.communicate()
101         if p.returncode != 0:
102             print("binapi generator installation failed: %d %s" % (p.returncode, error))
103             sys.exit(1)
104     bg_ver = get_binapi_gen_version(go_path)
105     print("Installed binary API generator " + bg_ver)
106
107
108 # Creates generated bindings using GoVPP binapigen to the target folder
109 def generate_api(output_dir, vpp_dir, api_list, import_prefix, no_source, go_path):
110     json_dir = vpp_dir + "/build-root/install-vpp-native/vpp/share/vpp/api"
111
112     if not os.path.exists(json_dir):
113         print("Missing JSON api definitions")
114         sys.exit(1)
115
116     print("Generating API")
117     cmd = ["./binapi-generator", "--input-dir=" + json_dir]
118     if output_dir:
119         cmd += ["--output-dir=" + output_dir]
120     if len(api_list):
121         print("Following API files were requested by 'GO_API_FILES': " + str(api_list))
122         print("Note that dependency requirements may generate " "additional API files")
123         cmd.append(api_list)
124     if import_prefix:
125         cmd.append("-import-prefix=" + import_prefix)
126     if no_source:
127         cmd.append("-no-source-path-info")
128     p = subprocess.Popen(
129         cmd,
130         cwd=go_path + "/bin",
131         stdout=subprocess.PIPE,
132         stderr=subprocess.PIPE,
133         universal_newlines=True,
134     )
135
136     out = p.communicate()[1]
137     if p.returncode != 0:
138         print("go api generate failed: %d %s" % (p.returncode, out))
139         sys.exit(1)
140
141     # Print nice output of the binapi generator
142     for msg in out.split():
143         if "=" in msg:
144             print()
145         print(msg, end=" ")
146
147     print("\n")
148     print("Go API bindings were generated to " + output_dir)
149
150
151 def main():
152     # project root directory
153     root = pathlib.Path(os.path.dirname(os.path.abspath(__file__)))
154     vpp_dir = root.parent.parent.parent
155
156     parser = argparse.ArgumentParser()
157     parser.add_argument(
158         "-govpp-commit",
159         "--govpp-commit",
160         help="GoVPP commit or branch " "(defaults to v0.3.5-45-g671f16c)",
161         default="671f16c",  # fixed GoVPP version
162         type=str,
163     )
164     parser.add_argument(
165         "-output-dir",
166         "--output-dir",
167         help="output target directory for generated bindings",
168         type=str,
169         default=os.path.join(vpp_dir, "vppbinapi"),
170     )
171     parser.add_argument(
172         "-api-files",
173         "--api-files",
174         help="api files to generate (without commas)",
175         nargs="+",
176         type=str,
177         default=[],
178     )
179     parser.add_argument(
180         "-import-prefix",
181         "--import-prefix",
182         help="prefix imports in the generated go code",
183         default="",
184         type=str,
185     )
186     parser.add_argument(
187         "-no-source-path-info",
188         "--no-source-path-info",
189         help="disable source path info in generated files",
190         nargs="?",
191         const=True,
192         default=True,
193     )
194     args = parser.parse_args()
195
196     # go specific environment variables
197     if "GOROOT" in os.environ:
198         go_root = os.environ["GOROOT"]
199     else:
200         go_binary = shutil.which("go")
201         if go_binary != "":
202             go_binary_dir, _ = os.path.split(go_binary)
203             go_root = os.path.join(go_binary_dir, "..")
204         else:
205             go_root = os.environ["HOME"] + "/.go"
206     if "GOPATH" in os.environ:
207         go_path = os.environ["GOPATH"]
208     else:
209         go_path = os.environ["HOME"] + "/go"
210
211     install_golang(go_root)
212     install_binapi_gen(args.govpp_commit, go_root, go_path)
213     generate_api(
214         args.output_dir,
215         str(vpp_dir),
216         args.api_files,
217         args.import_prefix,
218         args.no_source_path_info,
219         go_path,
220     )
221
222
223 if __name__ == "__main__":
224     main()