blob: 4ed507b5d7304ac6e035671fdce78dfb0ab41869 [file] [log] [blame]
Vladimir Lavor3ff15982021-04-08 13:08:04 +02001#!/usr/bin/env python3
2
3import argparse
4import os
5import pathlib
6import subprocess
7import tarfile
Nathan Skrzypczakd591b822022-03-10 12:38:31 +01008import shutil
Vladimir Lavor3ff15982021-04-08 13:08:04 +02009
10import requests
11import sys
12
13#
14# GoVPP API generator generates Go bindings compatible with the local VPP
15#
16
Nathan Skrzypczak78d24f32022-05-18 13:46:24 +020017DefaultGoVppCommit = "16a47ef937b3a5ce6acf45885386062b323c8d25"
18
19
20def version_geq(ver_a, ver_b):
21 major_a, minor_a, patch_a = ver_a.split(".")
22 major_b, minor_b, patch_b = ver_b.split(".")
23 if major_a > major_b:
24 return True
25 elif major_a == major_b and minor_a > minor_b:
26 return True
27 elif major_a == major_b and minor_a == minor_b and patch_a >= patch_b:
28 return True
29 return False
30
31
32def execute(cli, cwd=None):
33 p = subprocess.Popen(
34 cli.split(),
35 cwd=cwd,
36 stdout=subprocess.PIPE,
37 stderr=subprocess.PIPE,
38 universal_newlines=True,
39 )
40 output, error = p.communicate()
41 if p.returncode != 0:
42 print("Command `%s` failed: %d %s" % (cli, p.returncode, error))
43 sys.exit(1)
44 return output, error
45
Vladimir Lavor3ff15982021-04-08 13:08:04 +020046
Vladimir Lavor3ff15982021-04-08 13:08:04 +020047def get_go_version(go_root):
Nathan Skrzypczakd591b822022-03-10 12:38:31 +010048 # Returns version of the installed Go
Nathan Skrzypczak78d24f32022-05-18 13:46:24 +020049 output, _ = execute("./go version", cwd=go_root + "/bin")
50 return output.replace("go version go", "", 1).rstrip("\n")
Vladimir Lavor3ff15982021-04-08 13:08:04 +020051
52
53# Returns version of the installed binary API generator
54def get_binapi_gen_version(go_path):
Nathan Skrzypczak78d24f32022-05-18 13:46:24 +020055 output, _ = execute("./binapi-generator -version", cwd=go_path + "/bin")
56 return output.replace("govpp", "", 1).rstrip("\n")
Vladimir Lavor3ff15982021-04-08 13:08:04 +020057
58
59# Verifies local Go installation and installs the latest
60# one if missing
61def install_golang(go_root):
62 go_bin = go_root + "/bin/go"
63
64 if os.path.exists(go_bin) and os.path.isfile(go_bin):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020065 print("Go " + get_go_version(go_root) + " is already installed")
Vladimir Lavor3ff15982021-04-08 13:08:04 +020066 return
67
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020068 filename = (
69 requests.get("https://golang.org/VERSION?m=text").text + ".linux-amd64.tar.gz"
70 )
Nathan Skrzypczakd591b822022-03-10 12:38:31 +010071 url = "https://dl.google.com/go/" + filename
72
Vladimir Lavor3ff15982021-04-08 13:08:04 +020073 print("Go binary not found, installing the latest version...")
Nathan Skrzypczakd591b822022-03-10 12:38:31 +010074 print("Download url = %s" % url)
75 print("Install directory = %s" % go_root)
76 text = input("[Y/n] ?")
77
78 if text.strip().lower() != "y" and text.strip().lower() != "yes":
79 print("Aborting...")
80 exit(1)
81
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020082 go_folders = ["src", "pkg", "bin"]
Vladimir Lavor3ff15982021-04-08 13:08:04 +020083
84 for f in go_folders:
85 if not os.path.exists(os.path.join(go_root, f)):
86 os.makedirs(os.path.join(go_root, f))
Vladimir Lavor3ff15982021-04-08 13:08:04 +020087 r = requests.get(url)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020088 with open("/tmp/" + filename, "wb") as f:
Vladimir Lavor3ff15982021-04-08 13:08:04 +020089 f.write(r.content)
90
91 go_tf = tarfile.open("/tmp/" + filename)
92 # Strip /go dir from the go_root path as it will
93 # be created while extracting the tar file
94 go_root_head, _ = os.path.split(go_root)
95 go_tf.extractall(path=go_root_head)
96 go_tf.close()
97 os.remove("/tmp/" + filename)
98
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020099 print("Go " + get_go_version(go_root) + " was installed")
Vladimir Lavor3ff15982021-04-08 13:08:04 +0200100
101
102# Installs latest binary API generator
103def install_binapi_gen(c, go_root, go_path):
Nathan Skrzypczak78d24f32022-05-18 13:46:24 +0200104 go_version = get_go_version(go_root)
105 if version_geq(go_version, "1.18.0"):
106 execute(
107 "./go install git.fd.io/govpp.git/cmd/binapi-generator@" + c,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200108 cwd=go_root + "/bin",
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200109 )
Nathan Skrzypczak78d24f32022-05-18 13:46:24 +0200110 else:
111 os.environ["GO111MODULE"] = "on"
112 execute(
113 "./go get git.fd.io/govpp.git/cmd/binapi-generator@" + c,
114 cwd=go_root + "/bin",
115 )
Vladimir Lavor3ff15982021-04-08 13:08:04 +0200116 bg_ver = get_binapi_gen_version(go_path)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200117 print("Installed binary API generator " + bg_ver)
Vladimir Lavor3ff15982021-04-08 13:08:04 +0200118
119
120# Creates generated bindings using GoVPP binapigen to the target folder
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200121def generate_api(output_dir, vpp_dir, api_list, import_prefix, no_source, go_path):
Vladimir Lavor3ff15982021-04-08 13:08:04 +0200122 json_dir = vpp_dir + "/build-root/install-vpp-native/vpp/share/vpp/api"
123
124 if not os.path.exists(json_dir):
125 print("Missing JSON api definitions")
126 sys.exit(1)
127
128 print("Generating API")
Nathan Skrzypczakd591b822022-03-10 12:38:31 +0100129 cmd = ["./binapi-generator", "--input-dir=" + json_dir]
130 if output_dir:
131 cmd += ["--output-dir=" + output_dir]
Vladimir Lavor3ff15982021-04-08 13:08:04 +0200132 if len(api_list):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200133 print("Following API files were requested by 'GO_API_FILES': " + str(api_list))
Nathan Skrzypczak78d24f32022-05-18 13:46:24 +0200134 print("Note that dependency requirements may generate additional API files")
Vladimir Lavor3ff15982021-04-08 13:08:04 +0200135 cmd.append(api_list)
Nathan Skrzypczakd591b822022-03-10 12:38:31 +0100136 if import_prefix:
Vladimir Lavor3ff15982021-04-08 13:08:04 +0200137 cmd.append("-import-prefix=" + import_prefix)
138 if no_source:
139 cmd.append("-no-source-path-info")
Vladimir Lavor3ff15982021-04-08 13:08:04 +0200140
Nathan Skrzypczak78d24f32022-05-18 13:46:24 +0200141 _, out = execute(" ".join(cmd), cwd=go_path + "/bin")
Vladimir Lavor3ff15982021-04-08 13:08:04 +0200142 # Print nice output of the binapi generator
143 for msg in out.split():
144 if "=" in msg:
145 print()
146 print(msg, end=" ")
147
148 print("\n")
Nathan Skrzypczakd591b822022-03-10 12:38:31 +0100149 print("Go API bindings were generated to " + output_dir)
Vladimir Lavor3ff15982021-04-08 13:08:04 +0200150
151
Nathan Skrzypczak78d24f32022-05-18 13:46:24 +0200152def get_go_variables():
153 # go specific environment variables
154 if "GOROOT" in os.environ:
155 go_root = os.environ["GOROOT"]
156 else:
157 go_binary = shutil.which("go")
158 if go_binary != "":
159 go_binary_dir, _ = os.path.split(go_binary)
160 go_root = os.path.join(go_binary_dir, "..")
161 else:
162 go_root = os.environ["HOME"] + "/.go"
163 if "GOPATH" in os.environ:
164 go_path = os.environ["GOPATH"]
165 else:
166 go_path = os.environ["HOME"] + "/go"
167
168 return go_root, go_path
169
170
Vladimir Lavor3ff15982021-04-08 13:08:04 +0200171def main():
172 # project root directory
173 root = pathlib.Path(os.path.dirname(os.path.abspath(__file__)))
Nathan Skrzypczakd591b822022-03-10 12:38:31 +0100174 vpp_dir = root.parent.parent.parent
Vladimir Lavor3ff15982021-04-08 13:08:04 +0200175
Nathan Skrzypczakd591b822022-03-10 12:38:31 +0100176 parser = argparse.ArgumentParser()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200177 parser.add_argument(
178 "-govpp-commit",
179 "--govpp-commit",
Nathan Skrzypczak78d24f32022-05-18 13:46:24 +0200180 help="GoVPP commit or branch ",
181 default=DefaultGoVppCommit,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200182 type=str,
183 )
184 parser.add_argument(
185 "-output-dir",
186 "--output-dir",
187 help="output target directory for generated bindings",
188 type=str,
189 default=os.path.join(vpp_dir, "vppbinapi"),
190 )
191 parser.add_argument(
192 "-api-files",
193 "--api-files",
194 help="api files to generate (without commas)",
195 nargs="+",
196 type=str,
197 default=[],
198 )
199 parser.add_argument(
200 "-import-prefix",
201 "--import-prefix",
202 help="prefix imports in the generated go code",
203 default="",
204 type=str,
205 )
206 parser.add_argument(
207 "-no-source-path-info",
208 "--no-source-path-info",
209 help="disable source path info in generated files",
210 nargs="?",
211 const=True,
212 default=True,
213 )
Nathan Skrzypczakd591b822022-03-10 12:38:31 +0100214 args = parser.parse_args()
Vladimir Lavor3ff15982021-04-08 13:08:04 +0200215
Nathan Skrzypczak78d24f32022-05-18 13:46:24 +0200216 go_root, go_path = get_go_variables()
Vladimir Lavor3ff15982021-04-08 13:08:04 +0200217 install_golang(go_root)
Nathan Skrzypczak78d24f32022-05-18 13:46:24 +0200218
219 if not (
220 os.path.exists(go_root + "/bin/go") and os.path.isfile(go_root + "/bin/go")
221 ):
222 print(go_root + "/bin/go does not exist")
223 sys.exit(1)
224
Nathan Skrzypczakd591b822022-03-10 12:38:31 +0100225 install_binapi_gen(args.govpp_commit, go_root, go_path)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200226 generate_api(
227 args.output_dir,
228 str(vpp_dir),
229 args.api_files,
230 args.import_prefix,
231 args.no_source_path_info,
232 go_path,
233 )
Vladimir Lavor3ff15982021-04-08 13:08:04 +0200234
235
236if __name__ == "__main__":
237 main()