import argparse import sys import os import fileinput import colorama import shutil import vswhere import subprocess from peafour import P4 from colorama import Fore, Back, Style # Jazzy helpers def jazz_print(tag, detail = ""): print(f"{Fore.WHITE}{Style.BRIGHT}||> {tag}{Style.RESET_ALL} {detail}") def jazz_fail(tag, detail = ""): print(f"{Fore.RED}{Style.BRIGHT}||> {tag}{Style.RESET_ALL} {detail}") def copy_file(src, dst): print(f"{Fore.WHITE}{Style.BRIGHT}||> COPY {Style.RESET_ALL} {src} -> {Fore.GREEN}{Style.BRIGHT}{dst}") shutil.copy(src, dst) colorama.init() origcwd = os.getcwd() # Parse and validate arguments parser = argparse.ArgumentParser(description='Deploy a zen build to an UE tree') parser.add_argument("root", help="Path to an UE5 root directory") parser.add_argument("--sentry", action="store_true", help="Whether to upload symobls to Sentry") args = parser.parse_args() engineroot = args.root upload_symbols = args.sentry if not os.path.isfile(os.path.join(engineroot, "RunUAT.bat")): print(f"{Fore.RED}Not a valid UE5 engine root directory: '{engineroot}'") print(Style.RESET_ALL) exit(1) # Establish root of zen tree zenroot = __file__ while not os.path.exists(os.path.join(zenroot, "deploy_build.bat")): zenroot = os.path.dirname(zenroot) jazz_print("Zen root:", zenroot) # Build fresh binaries try: subprocess.run(["xmake.exe", "config", "-p", "windows", "-a", "x64", "-m", "release"], check=True) build_cmd = ["xmake.exe", "build", "--rebuild", "zenserver"] build_output_dir = r'build\windows\x64\release' subprocess.run(build_cmd, check=True) except: jazz_fail("Build failed!") exit(1) build_output_binary_path = os.path.join(zenroot, build_output_dir, "zenserver.exe") build_output_binary_pdb_path = os.path.join(zenroot, build_output_dir, "zenserver.pdb") # Upload symbols etc to Sentry if upload_symbols: jazz_print("Uploading symbols", "to Sentry") subprocess.run(["scripts\sentry-cli.exe", "upload-dif", "--org", "to", "--project", "zen-server", build_output_binary_path, build_output_binary_pdb_path]) # Change into root directory to pick up Perforce environment jazz_print(f"Determining P4 environment", f"for directory '{engineroot}'") os.chdir(engineroot) p4info = P4.info().run() if p4info is None: jazz_fail("Unable to query P4 info", "do you have source control connectivity?") exit(1) if not os.path.samefile(p4info.clientRoot, engineroot): print(f"{Fore.RED}Could not find P4 client for UE5 engine root directory '{engineroot}'") print(Style.RESET_ALL) exit(1) # check out the binaries jazz_print("Reverting", "any previous unsubmitted deploy") try: P4.revert("Engine/Binaries/Win64/zenserver.*").run() P4.revert("Engine/Binaries/Win64/crashpad_handler.exe").run() except Exception as e: # it's not super important to report failure here, it's likely # due to the user not actually having the file checked out (yet) pass jazz_print("Checking out", "(at head) zenserver executables") try: P4.edit("Engine/Binaries/Win64/zenserver.*").run() P4.edit("Engine/Binaries/Win64/crashpad_handler.exe").run() except Exception as e: jazz_fail("edit failed", str(e)) exit(1) target_bin_dir = os.path.join(engineroot, "Engine\\Binaries\\Win64") jazz_print("Placing zenserver", f"executables into tree at '{target_bin_dir}'") crashpadtarget = os.path.join(target_bin_dir, "crashpad_handler.exe") try: copy_file(build_output_binary_path, os.path.join(target_bin_dir, "zenserver.exe")) copy_file(build_output_binary_pdb_path, os.path.join(target_bin_dir, "zenserver.pdb")) copy_file(os.path.join(zenroot, r'vcpkg_installed\x64-windows-static\tools\sentry-native\crashpad_handler.exe'), crashpadtarget) P4.add(crashpadtarget).run() except Exception as e: print(f"Caught exception while copying: {e.args}") exit(1) jazz_print("SUCCESS", "binaries ready for check-in")