diff options
| author | Martin Ridgers <[email protected]> | 2022-02-01 10:19:40 +0100 |
|---|---|---|
| committer | Martin Ridgers <[email protected]> | 2022-02-01 10:19:46 +0100 |
| commit | 9039c8066a97b09ee5848abc95d31e84529450be (patch) | |
| tree | da4ad315a40ee671a52e429c54be4ffa6f89eb65 /scripts | |
| parent | Merge branch 'main' of https://github.com/EpicGames/zen (diff) | |
| download | zen-9039c8066a97b09ee5848abc95d31e84529450be.tar.xz zen-9039c8066a97b09ee5848abc95d31e84529450be.zip | |
Script for building a branch over SSH
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/remote_build.py | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/scripts/remote_build.py b/scripts/remote_build.py new file mode 100644 index 000000000..e83f95d8d --- /dev/null +++ b/scripts/remote_build.py @@ -0,0 +1,237 @@ +import os +import sys +import argparse +import subprocess +from pathlib import Path + +# {{{1 misc -------------------------------------------------------------------- + +#------------------------------------------------------------------------------- +def _header(*args, ansi=96): + print(f"\x1b[{ansi}m##", *args, end="") + print("\x1b[0m") + +#------------------------------------------------------------------------------- +def _run_checked(cmd, *args, **kwargs): + _header(cmd, *args, ansi=97) + ret = subprocess.run((cmd, *args), **kwargs, bufsize=0) + if ret.returncode: + raise RuntimeError("Failed running " + str(cmd)) + +#------------------------------------------------------------------------------- +def _get_ip(): + import socket + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + s.connect(("172.31.255.255", 1)) + return s.getsockname()[0] + except: + return "127.0.0.1" + finally: + s.close() + +#------------------------------------------------------------------------------- +def _find_binary(name): + name += ".exe" if os.name == "nt" else "" + for prefix in os.getenv("PATH", "").split(os.pathsep): + path = Path(prefix) / name + if path.is_file(): + return path + raise EnvironmentError(f"Unable to find '{name}' in the path") + +#------------------------------------------------------------------------------- +class _AutoKill(object): + def __init__(self, proc): + self._proc = proc + + def __del__(self): + self._proc.kill() + self._proc.wait() + pass + + + +# {{{1 local ------------------------------------------------------------------- + +#------------------------------------------------------------------------------- +def _local(args): + # Parse arguments + desc = "Build Zen on a remote host" + parser = argparse.ArgumentParser(description=desc) + parser.add_argument("host", help="") + parser.add_argument("action", default="build", nargs="?", help="") + parser.add_argument("--keyfile", default=None, help="SSH key file") + args = parser.parse_args(args) + + # Find the binaries we'll need + _header("Finding tools") + git_bin = _find_binary("git") + print(f"Using git from '{git_bin.name}' from '{git_bin.parent}'") + + def find_git_tool(git_bin, tool_name): + print(f"Locating {tool_name}...") + tool_suffix = "usr/bin/" + tool_name + tool_suffix += ".exe" if os.name == "nt" else "" + for parent in git_bin.parents: + tool_path = parent / tool_suffix + if tool_path.is_file(): + return tool_path + return _find_binary(tool_name) + + ssh_bin = find_git_tool(git_bin, "ssh") + scp_bin = find_git_tool(git_bin, "scp") + print(f"Using ssh from '{ssh_bin.name}' from '{ssh_bin.parent}'") + print(f"Using scp from '{scp_bin.name}' from '{scp_bin.parent}'") + + # Find the Zen repository root + for parent in Path(__file__).resolve().parents: + if (parent / ".git").is_dir(): + zen_dir = parent + break; + else: + raise EnvironmentError("Unable to find '.git/' directory") + + # Start a git daemon to use as a transfer mechanism + _header("Starting a git daemon") + print("Port: 4493") + print("Base-path: ", zen_dir) + print("Host: ", _get_ip()) + daemon = subprocess.Popen( + ( git_bin, + "daemon", + "--port=4493", + "--export-all", + "--reuseaddr", + "--verbose", + "--informative-errors", + "--base-path=" + str(zen_dir) ), + #stdout = daemon_log, + stderr = subprocess.STDOUT + ) + daemon_killer = _AutoKill(daemon) + + # Run this script on the remote machine + _header("Running SSH") + + remote_zen_dir = "%s_%s" % (os.getlogin(), _get_ip()) + + host = args.host + if host == "linux": host = os.getenv("ZEN_REMOTE_HOST_LINUX", "arn-lin-12345") + if host == "mac": host = os.getenv("ZEN_REMOTE_HOST_MAC", "imacpro-arn.local") + host = "zenbuild@" + host + print(f"Using host '{host}'") + print(f"Using zen '~/{remote_zen_dir}'") + + print(f"Running {__file__} remotely") + ssh_args = ("-tA",) + if args.keyfile: + ssh_args = (*ssh_args, "-i", args.keyfile) + with open(__file__, "rt") as self_file: + _run_checked( + ssh_bin, + *ssh_args, + host, + f"python3 -u - !remote {_get_ip()} '{remote_zen_dir}' main '{args.action}'", + stdin=self_file) + + # If we're bundling, collect zip files from the remote machine + if args.action == "bundle": + build_dir = zen_dir / "build" + build_dir.mkdir(exist_ok=True) + scp_args = (host + f":zen/{remote_zen_dir}/build/*.zip", build_dir) + if args.keyfile: + scp_args = ("-i", args.keyfile, *scp_args) + _run_checked("scp", *scp_args) + + + +# {{{1 remote ------------------------------------------------------------------ + +#------------------------------------------------------------------------------- +def _remote(args): + # Parse arguments + desc = "Build Zen on a remote host" + parser = argparse.ArgumentParser(description=desc) + parser.add_argument("ip", help="Host's IP address") + parser.add_argument("reponame", help="Repository name clone into and work in") + parser.add_argument("branch", help="Zen branch to operate on") + parser.add_argument("action", help="The action to do") + args = parser.parse_args(args) + + # Homeward bound and out + zen_dir = Path().home() / "zen" + os.chdir(zen_dir) + + # Mutual exclusion + """ + lock_path = zen_dir / "../.remote_lock" + try: lock_file = open(lock_path, "xb") + except: raise RuntimeError("Failed to lock", lock_path) + """ + + # Check for a clone, create it, chdir to it + _header("REMOTE:", f"Clone/pull from {args.ip}") + clone_dir = zen_dir / args.reponame + if not clone_dir.is_dir(): + _run_checked("git", "clone", f"git://{args.ip}:4493/", clone_dir) + os.chdir(clone_dir) + + _run_checked("git", "checkout", args.branch) + _run_checked("git", "pull", "-r") + + _header("REMOTE:", f"Performing action '{args.action}'") + + # Find xmake + xmake_bin = max(x for x in (zen_dir / "xmake").glob("*")) + xmake_bin /= "usr/local/bin/xmake" + + # Run xmake + xmake_env = {} + xmake_env["VCPKG_ROOT"] = zen_dir / "vcpkg" + if sys.platform == "linux": + xmake_env["CXX"] = "g++-11" + print("xmake environment:") + for key, value in xmake_env.items(): + print(" ", key, "=", value) + xmake_env.update(os.environ) + + def run_xmake(*args): + print("starting xmake...", end="\r") + _run_checked(xmake_bin, args[0], "--yes", *args[1:], env=xmake_env) + + if args.action.startswith("build"): + mode = "debug" if args.action == "build.debug" else "release" + run_xmake("config", "--mode=" + mode) + run_xmake("build") + + elif args.action == "bundle": + run_xmake("bundle") + + elif args.action == "test": + run_xmake("config", "--mode=debug") + run_xmake("test") + + elif args.action == "clean": + _run_checked("git", "reset") + _run_checked("git", "checkout", ".") + _run_checked("git", "clean", "-xdf") + + + +# {{{1 entry ------------------------------------------------------------------- +if __name__ == "__main__": + if "!remote" in sys.argv[1:2]: + ret = _remote(sys.argv[2:]) + raise SystemExit(ret) + + try: + ret = _local(sys.argv[1:]) + raise SystemExit(ret) + except: + raise + finally: + # Roundabout way to avoid orphaned git-daemon processes + if os.name == "nt": + os.system("taskkill /f /im git-daemon.exe") + +# vim: expandtab foldlevel=1 foldmethod=marker |