blob: 4ed507b5d7304ac6e035671fdce78dfb0ab41869 [file] [log] [blame]
#!/usr/bin/env python3
import argparse
import os
import pathlib
import subprocess
import tarfile
import shutil
import requests
import sys
#
# GoVPP API generator generates Go bindings compatible with the local VPP
#
DefaultGoVppCommit = "16a47ef937b3a5ce6acf45885386062b323c8d25"
def version_geq(ver_a, ver_b):
major_a, minor_a, patch_a = ver_a.split(".")
major_b, minor_b, patch_b = ver_b.split(".")
if major_a > major_b:
return True
elif major_a == major_b and minor_a > minor_b:
return True
elif major_a == major_b and minor_a == minor_b and patch_a >= patch_b:
return True
return False
def execute(cli, cwd=None):
p = subprocess.Popen(
cli.split(),
cwd=cwd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
)
output, error = p.communicate()
if p.returncode != 0:
print("Command `%s` failed: %d %s" % (cli, p.returncode, error))
sys.exit(1)
return output, error
def get_go_version(go_root):
# Returns version of the installed Go
output, _ = execute("./go version", cwd=go_root + "/bin")
return output.replace("go version go", "", 1).rstrip("\n")
# Returns version of the installed binary API generator
def get_binapi_gen_version(go_path):
output, _ = execute("./binapi-generator -version", cwd=go_path + "/bin")
return output.replace("govpp", "", 1).rstrip("\n")
# Verifies local Go installation and installs the latest
# one if missing
def install_golang(go_root):
go_bin = go_root + "/bin/go"
if os.path.exists(go_bin) and os.path.isfile(go_bin):
print("Go " + get_go_version(go_root) + " is already installed")
return
filename = (
requests.get("https://golang.org/VERSION?m=text").text + ".linux-amd64.tar.gz"
)
url = "https://dl.google.com/go/" + filename
print("Go binary not found, installing the latest version...")
print("Download url = %s" % url)
print("Install directory = %s" % go_root)
text = input("[Y/n] ?")
if text.strip().lower() != "y" and text.strip().lower() != "yes":
print("Aborting...")
exit(1)
go_folders = ["src", "pkg", "bin"]
for f in go_folders:
if not os.path.exists(os.path.join(go_root, f)):
os.makedirs(os.path.join(go_root, f))
r = requests.get(url)
with open("/tmp/" + filename, "wb") as f:
f.write(r.content)
go_tf = tarfile.open("/tmp/" + filename)
# Strip /go dir from the go_root path as it will
# be created while extracting the tar file
go_root_head, _ = os.path.split(go_root)
go_tf.extractall(path=go_root_head)
go_tf.close()
os.remove("/tmp/" + filename)
print("Go " + get_go_version(go_root) + " was installed")
# Installs latest binary API generator
def install_binapi_gen(c, go_root, go_path):
go_version = get_go_version(go_root)
if version_geq(go_version, "1.18.0"):
execute(
"./go install git.fd.io/govpp.git/cmd/binapi-generator@" + c,
cwd=go_root + "/bin",
)
else:
os.environ["GO111MODULE"] = "on"
execute(
"./go get git.fd.io/govpp.git/cmd/binapi-generator@" + c,
cwd=go_root + "/bin",
)
bg_ver = get_binapi_gen_version(go_path)
print("Installed binary API generator " + bg_ver)
# Creates generated bindings using GoVPP binapigen to the target folder
def generate_api(output_dir, vpp_dir, api_list, import_prefix, no_source, go_path):
json_dir = vpp_dir + "/build-root/install-vpp-native/vpp/share/vpp/api"
if not os.path.exists(json_dir):
print("Missing JSON api definitions")
sys.exit(1)
print("Generating API")
cmd = ["./binapi-generator", "--input-dir=" + json_dir]
if output_dir:
cmd += ["--output-dir=" + output_dir]
if len(api_list):
print("Following API files were requested by 'GO_API_FILES': " + str(api_list))
print("Note that dependency requirements may generate additional API files")
cmd.append(api_list)
if import_prefix:
cmd.append("-import-prefix=" + import_prefix)
if no_source:
cmd.append("-no-source-path-info")
_, out = execute(" ".join(cmd), cwd=go_path + "/bin")
# Print nice output of the binapi generator
for msg in out.split():
if "=" in msg:
print()
print(msg, end=" ")
print("\n")
print("Go API bindings were generated to " + output_dir)
def get_go_variables():
# go specific environment variables
if "GOROOT" in os.environ:
go_root = os.environ["GOROOT"]
else:
go_binary = shutil.which("go")
if go_binary != "":
go_binary_dir, _ = os.path.split(go_binary)
go_root = os.path.join(go_binary_dir, "..")
else:
go_root = os.environ["HOME"] + "/.go"
if "GOPATH" in os.environ:
go_path = os.environ["GOPATH"]
else:
go_path = os.environ["HOME"] + "/go"
return go_root, go_path
def main():
# project root directory
root = pathlib.Path(os.path.dirname(os.path.abspath(__file__)))
vpp_dir = root.parent.parent.parent
parser = argparse.ArgumentParser()
parser.add_argument(
"-govpp-commit",
"--govpp-commit",
help="GoVPP commit or branch ",
default=DefaultGoVppCommit,
type=str,
)
parser.add_argument(
"-output-dir",
"--output-dir",
help="output target directory for generated bindings",
type=str,
default=os.path.join(vpp_dir, "vppbinapi"),
)
parser.add_argument(
"-api-files",
"--api-files",
help="api files to generate (without commas)",
nargs="+",
type=str,
default=[],
)
parser.add_argument(
"-import-prefix",
"--import-prefix",
help="prefix imports in the generated go code",
default="",
type=str,
)
parser.add_argument(
"-no-source-path-info",
"--no-source-path-info",
help="disable source path info in generated files",
nargs="?",
const=True,
default=True,
)
args = parser.parse_args()
go_root, go_path = get_go_variables()
install_golang(go_root)
if not (
os.path.exists(go_root + "/bin/go") and os.path.isfile(go_root + "/bin/go")
):
print(go_root + "/bin/go does not exist")
sys.exit(1)
install_binapi_gen(args.govpp_commit, go_root, go_path)
generate_api(
args.output_dir,
str(vpp_dir),
args.api_files,
args.import_prefix,
args.no_source_path_info,
go_path,
)
if __name__ == "__main__":
main()