diff --git a/Tools/build.py b/Tools/build.py index 0bb2e434..8cb4a941 100755 --- a/Tools/build.py +++ b/Tools/build.py @@ -6,27 +6,18 @@ import platform import shutil import argparse import time +from typing import Tuple import zipfile from shutil import copytree from pathlib import Path -from concurrent.futures import ThreadPoolExecutor from datetime import datetime - -SCREENPLAY_VERSION = "0.15.0-RC1" - - -def zipdir(path, ziph): - # ziph is zipfile handle - for root, dirs, files in os.walk(path): - for file in files: - ziph.write(os.path.join(root, file), - os.path.relpath(os.path.join(root, file), - os.path.join(path, '..'))) - +from util import sha256, cd_repo_root_path, zipdir, run # Based on https://gist.github.com/l2m2/0d3146c53c767841c6ba8c4edbeb4c2c + + def get_vs_env_dict(): - vcvars = "" # We support 2019 or 2022 + vcvars: str # We support 2019 or 2022 # Hardcoded VS path # check if vcvars64.bat is available. @@ -55,13 +46,6 @@ def get_vs_env_dict(): return dict((e[0].upper(), e[1]) for e in [p.rstrip().split("=", 1) for p in output] if len(e) == 2) -def run_io_tasks_in_parallel(tasks): - with ThreadPoolExecutor() as executor: - running_tasks = [executor.submit(task) for task in tasks] - for running_task in running_tasks: - running_task.result() - - def clean_build_dir(build_dir): if isinstance(build_dir, str): build_dir = Path(build_dir) @@ -72,284 +56,402 @@ def clean_build_dir(build_dir): build_dir.mkdir(parents=True, exist_ok=True) -def run(cmd, cwd=Path.cwd()): - result = subprocess.run(cmd, shell=True, cwd=cwd) - if result.returncode != 0: - raise RuntimeError(f"Failed to execute {cmd}") +class BuildResult: # Windows example with absolute paths: + build: Path # [...]/build-x64-windows-release/ + binary: Path # [...]/build-x64-windows-release/bin + # [...]/build-x64-windows-release/ScreenPlay-Installer.exe + installer: Path + # [...]/build-x64-windows-release/ScreenPlay-Installer.zip + installer_zip: Path + # [...]/build-x64-windows-release/ScreenPlay-0.15.0-RC1-x64-windows-release.zip + build_zip: Path + # [...]/build-x64-windows-release/ScreenPlay-0.15.0-RC1-x64-windows-release.txt + build_hash: Path # sha256, needed for scoop + build_arch: str # x64, arm64, universal -def build( - qt_version: str, - qt_ifw_version: str, - build_steam: str, - build_tests: str, - build_release: str, - create_installer: str, - build_type: str, - sign_build: str, - use_aqt: bool, +class BuildConfig: + root_path: str + cmake_osx_architectures: str + cmake_target_triplet: str + deploy_command: str + executable_file_ending: str + qt_path: str + qt_bin_path: str + qt_version: str + qt_ifw_version: str + ifw_root_path: str + cmake_toolchain_file: str + use_aqt: bool + aqt_install_qt_packages: str + aqt_install_tool_packages: str + aqt_path: str + executable_file_ending: str + build_folder: str + bin_dir: str + sign_build: bool + screenplay_version: str + # CMake variables need str: "ON" or "OFF" + build_steam: str + build_tests: str + build_release: str + build_type: str build_architecture: str -): + create_installer: str + + +def execute( + build_config: BuildConfig +) -> BuildResult: # Make sure the script is always started from the same folder - root_path = Path.cwd() - if root_path.name == "Tools": - root_path = root_path.parent - print(f"Change root directory to: {root_path}") - os.chdir(root_path) + build_config.root_path = cd_repo_root_path() - aqt_path = "" - if use_aqt: - aqt_path = Path(f"{root_path}/../aqt/").resolve() + build_result = BuildResult() - if not Path(aqt_path).exists(): + # Sets all platform spesific paths, arguments etc. + setup_tuple = setup(build_config, build_result) + build_config = setup_tuple[0] + build_result = setup_tuple[1] + + build_result.build = Path(build_config.build_folder) + build_result.binary = Path(build_config.bin_dir) + + # Make sure to always delete everything first. + # 3rd party tools like the crashreporter create local + # temporary files in the build directory. + clean_build_dir(build_config.build_folder) + + start_time = time.time() + + # Runs cmake configure and cmake build + build_result = build(build_config, build_result) + + # Copies all needed libraries and assets into the bin folder + deploy(build_config) + + # Creates a Qt InstallerFrameWork (IFW) installer + if build_config.create_installer == "ON": + create_installer(build_config, build_result) + + # Mac needs signed builds for user to run the app + if platform.system() == "Darwin" and build_config.sign_build: + sign(build_config) + + # Create a zip file for scoop + build_result = zip(build_config, build_result) + + print("Time taken: {}s".format(time.time() - start_time)) + + # Print BuildConfig & BuildResult member for easier debugging + print("BuildResult:\n") + print(' '.join("- %s: \t\t%s\n" % item for item in vars(build_result).items())) + print("BuildConfig:\n") + print(' '.join("- %s: \t\t%s\n" % item for item in vars(build_config).items())) + + return build_result + + +def setup(build_config: BuildConfig, build_result: BuildResult) -> Tuple[BuildConfig, BuildResult]: + + if build_config.use_aqt: + build_config.aqt_path = Path( + f"{build_config.root_path}/../aqt/").resolve() + + if not Path(build_config.aqt_path).exists(): print( - f"aqt path does not exist at {aqt_path}. Please make sure you have installed aqt.") + f"aqt path does not exist at {build_config.aqt_path}. Please make sure you have installed aqt.") exit(2) - - CMAKE_OSX_ARCHITECTURES: str = "" - deploy_command: str = "" - executable_file_ending: str = "" - qt_path: str = "" - qt_bin_path: str = "" - aqt_install_qt_packages: str = "" - aqt_install_tool_packages: str = "" - cmake_target_triplet: str = "" - file_endings = [".ninja_deps", ".ninja", ".ninja_log", ".lib", ".a", ".exp", - ".manifest", ".cmake", ".cbp", "CMakeCache.txt"] + # Set default to empty, because it is only used on mac + build_config.cmake_osx_architectures = "" if platform.system() == "Windows": - cmake_target_triplet = "x64-windows" - windows_msvc = "msvc2019_64" - executable_file_ending = ".exe" - qt_path = aqt_path if use_aqt else Path("C:/Qt") - qt_bin_path = aqt_path.joinpath(qt_version).joinpath( - windows_msvc) if use_aqt else Path(f"C:/Qt/{qt_version}/{windows_msvc}") + build_config.cmake_target_triplet = "x64-windows" + build_config.executable_file_ending = ".exe" + build_config.qt_path = build_config.aqt_path if build_config.use_aqt else Path( + "C:/Qt") + windows_msvc = "msvc2019_64" # This will change once prebuild Qt bins change + build_config.qt_bin_path = build_config.aqt_path.joinpath(build_config.qt_version).joinpath( + windows_msvc) if build_config.use_aqt else Path(f"C:/Qt/{build_config.qt_version}/{windows_msvc}") vs_env_dict = get_vs_env_dict() vs_env_dict["PATH"] = vs_env_dict["PATH"] + \ - ";" + str(qt_bin_path) + "\\bin" + ";" + str(build_config.qt_bin_path) + "\\bin" os.environ.update(vs_env_dict) - deploy_command = "windeployqt.exe --{type} --qmldir ../../{app}/qml {app}{executable_file_ending}" + # NO f string we fill it later! + build_config.deploy_command = "windeployqt.exe --{type} --qmldir ../../{app}/qml {app}{executable_file_ending}" - aqt_install_qt_packages = f"windows desktop {qt_version} win64_msvc2019_64 -m all" - aqt_install_tool_packages = "windows desktop tools_ifw" + build_config.aqt_install_qt_packages = f"windows desktop {build_config.qt_version} win64_msvc2019_64 -m all" + build_config.aqt_install_tool_packages = "windows desktop tools_ifw" elif platform.system() == "Darwin": - if(build_architecture == "arm64"): - cmake_target_triplet = "arm64-osx" - CMAKE_OSX_ARCHITECTURES = "-DCMAKE_OSX_ARCHITECTURES=arm64" - elif(build_architecture == "x64"): - cmake_target_triplet = "x64-osx" - CMAKE_OSX_ARCHITECTURES = "-DCMAKE_OSX_ARCHITECTURES=x86_64" + if(build_config.build_architecture == "arm64"): + build_config.cmake_target_triplet = "arm64-osx" + build_config.cmake_osx_architectures = "-DCMAKE_OSX_ARCHITECTURES=arm64" + elif(build_config.build_architecture == "x64"): + build_config.cmake_target_triplet = "x64-osx" + build_config.cmake_osx_architectures = "-DCMAKE_OSX_ARCHITECTURES=x86_64" else: print("MISSING BUILD ARCH: SET arm64 or x64") exit(1) - qt_path = aqt_path if use_aqt else Path("~/Qt/") - qt_bin_path = aqt_path.joinpath( - f"{qt_version}/macos") if use_aqt else Path(f"~/Qt/{qt_version}/macos") - deploy_command = "{prefix_path}/bin/macdeployqt {app}.app -qmldir=../../{app}/qml -executable={app}.app/Contents/MacOS/{app}" + build_config.qt_path = build_config.aqt_path if build_config.use_aqt else Path( + "~/Qt/") + build_config.qt_bin_path = build_config.aqt_path.joinpath( + f"{build_config.qt_version}/macos") if build_config.use_aqt else Path(f"~/Qt/{build_config.qt_version}/macos") + # NO f string we fill it later! + build_config.deploy_command = "{prefix_path}/bin/macdeployqt {app}.app -qmldir=../../{app}/qml -executable={app}.app/Contents/MacOS/{app}" - aqt_install_qt_packages = f"mac desktop {qt_version} clang_64 -m all" - aqt_install_tool_packages = "mac desktop tools_ifw" + build_config.aqt_install_qt_packages = f"mac desktop {build_config.qt_version} clang_64 -m all" + build_config.aqt_install_tool_packages = "mac desktop tools_ifw" elif platform.system() == "Linux": - cmake_target_triplet = "x64-linux" - qt_path = aqt_path if use_aqt else Path("~/Qt/") - qt_bin_path = aqt_path.joinpath( - f"{qt_version}/gcc_64") if use_aqt else Path(f"~/Qt/{qt_version}/gcc_64") - aqt_install_qt_packages = f"linux desktop {qt_version} gcc_64 -m all" - aqt_install_tool_packages = "linux desktop tools_ifw" + build_config.cmake_target_triplet = "x64-linux" + build_config.qt_path = build_config.aqt_path if build_config.use_aqt else Path( + "~/Qt/") + build_config.qt_bin_path = build_config.aqt_path.joinpath( + f"{build_config.qt_version}/gcc_64") if build_config.use_aqt else Path(f"~/Qt/{build_config.qt_version}/gcc_64") + build_config.aqt_install_qt_packages = f"linux desktop {build_config.qt_version} gcc_64 -m all" + build_config.aqt_install_tool_packages = "linux desktop tools_ifw" if shutil.which("cqtdeployer"): - deploy_command = "cqtdeployer -qmlDir ../../{app}/qml -bin {app}" + # NO f string we fill it later! + build_config.deploy_command = "cqtdeployer -qmlDir ../../{app}/qml -bin {app}" else: print("cqtdeployer not available, build may be incomplete and incompatible with some distro (typically Ubuntu)") home_path = str(Path.home()) - qt_bin_path = aqt_path.joinpath( - f"{qt_version}/gcc_64") if use_aqt else Path(f"{home_path}/Qt/{qt_version}/gcc_64") + build_config.qt_bin_path = build_config.aqt_path.joinpath( + f"{build_config.qt_version}/gcc_64") if build_config.use_aqt else Path(f"{home_path}/Qt/{build_config.qt_version}/gcc_64") else: raise NotImplementedError( "Unsupported platform, ScreenPlay only supports Windows, macOS and Linux.") # Default to QtMaintainance installation. - if use_aqt: - print(f"qt_bin_path: {qt_bin_path}.") - if not Path(aqt_path).exists(): + if build_config.use_aqt: + print(f"qt_bin_path: {build_config.qt_bin_path}.") + if not Path(build_config.aqt_path).exists(): print( - f"aqt path does not exist at {aqt_path}. Installing now into...") - run(f"aqt install-qt -O ../aqt {aqt_install_qt_packages}", cwd=root_path) + f"aqt path does not exist at {build_config.aqt_path}. Installing now into...") + run(f"aqt install-qt -O ../aqt {build_config.aqt_install_qt_packages}", + cwd=build_config.root_path) run( - f"aqt install-tool -O ../aqt {aqt_install_tool_packages}", cwd=root_path) + f"aqt install-tool -O ../aqt {build_config.aqt_install_tool_packages}", cwd=build_config.root_path) # Prepare - cmake_toolchain_file = f"'{root_path}/../ScreenPlay-vcpkg/scripts/buildsystems/vcpkg.cmake'" - ifw_root_path = f"{qt_path}/Tools/QtInstallerFramework/{qt_ifw_version}" - print(f"cmake_toolchain_file: {cmake_toolchain_file}") + build_config.cmake_toolchain_file = f"'{build_config.root_path}/../ScreenPlay-vcpkg/scripts/buildsystems/vcpkg.cmake'" + build_config.ifw_root_path = f"{build_config.qt_path}/Tools/QtInstallerFramework/{build_config.qt_ifw_version}" + print(f"cmake_toolchain_file: {build_config.cmake_toolchain_file}") print( - f"Starting build with type {build_type}. Qt Version: {qt_version}. Root path: {root_path}") - - - # Remove old build folder to before configuring to get rid of - # all cmake chaches - build_folder = root_path.joinpath( - f"build-{cmake_target_triplet}-{build_type}") - clean_build_dir(build_folder) + f"Starting build with type {build_config.build_type}. Qt Version: {build_config.qt_version}. Root path: {build_config.root_path}") + + # Remove old build folder to before configuring to get rid of + # all cmake chaches + build_config.build_folder = build_config.root_path.joinpath( + f"build-{build_config.cmake_target_triplet}-{build_config.build_type}") + build_config.bin_dir = build_config.build_folder.joinpath("bin") + + if platform.system() == "Windows": + build_result.installer = Path(build_config.build_folder).joinpath( + "ScreenPlay-Installer.exe") + elif platform.system() == "Darwin": + build_result.installer = Path(build_config.build_folder).joinpath( + "ScreenPlay-Installer.dmg") + elif platform.system() == "Linux": + build_result.installer = Path(build_config.build_folder).joinpath( + "ScreenPlay-Installer.run") + + return build_config, build_result +def build(build_config: BuildConfig, build_result: BuildResult) -> BuildResult: cmake_configure_command = f'cmake ../ \ - {CMAKE_OSX_ARCHITECTURES} \ - -DCMAKE_PREFIX_PATH={qt_bin_path} \ - -DCMAKE_BUILD_TYPE={build_type} \ - -DVCPKG_TARGET_TRIPLET={cmake_target_triplet} \ - -DCMAKE_TOOLCHAIN_FILE={cmake_toolchain_file} \ - -DSCREENPLAY_STEAM={build_steam} \ - -DSCREENPLAY_TESTS={build_tests} \ - -DSCREENPLAY_RELEASE={build_release} \ - -DSCREENPLAY_INSTALLER={create_installer} \ - -DSCREENPLAY_IFW_ROOT:STRING={ifw_root_path} \ --G "CodeBlocks - Ninja" \ + {build_config.cmake_osx_architectures} \ + -DCMAKE_PREFIX_PATH={build_config.qt_bin_path} \ + -DCMAKE_BUILD_TYPE={build_config.build_type} \ + -DVCPKG_TARGET_TRIPLET={build_config.cmake_target_triplet} \ + -DCMAKE_TOOLCHAIN_FILE={build_config.cmake_toolchain_file} \ + -DSCREENPLAY_STEAM={build_config.build_steam} \ + -DSCREENPLAY_TESTS={build_config.build_tests} \ + -DSCREENPLAY_RELEASE={build_config.build_release} \ + -DSCREENPLAY_INSTALLER={build_config.create_installer} \ + -DSCREENPLAY_IFW_ROOT:STRING={build_config.ifw_root_path} \ + -G "CodeBlocks - Ninja" \ -B.' + print(f"CMake configure:\n{cmake_configure_command}\n\n") + run(cmake_configure_command, cwd=build_config.build_folder) + print(f"\nCMake build:\n") + run("cmake --build . --target all", cwd=build_config.build_folder) - # Build - start_time = time.time() - print(cmake_configure_command) - run(cmake_configure_command, cwd=build_folder) - run("cmake --build . --target all", cwd=build_folder) + build_result.binary = Path(build_config.bin_dir) - bin_dir = build_folder.joinpath("bin") + return build_result - # Deploy dependencies - if deploy_command: # Only deploy if we have the dependencies + +def deploy(build_config: BuildConfig): + + if build_config.deploy_command: # Only deploy if we have the dependencies print("Executing deploy commands...") - run(deploy_command.format( - type=build_type, - prefix_path=qt_bin_path, + run(build_config.deploy_command.format( + type=build_config.build_type, + prefix_path=build_config.qt_bin_path, app="ScreenPlay", - executable_file_ending=executable_file_ending), cwd=bin_dir) + executable_file_ending=build_config.executable_file_ending), cwd=build_config.bin_dir) - run(deploy_command.format( - type=build_type, - prefix_path=qt_bin_path, + run(build_config.deploy_command.format( + type=build_config.build_type, + prefix_path=build_config.qt_bin_path, app="ScreenPlayWidget", - executable_file_ending=executable_file_ending), cwd=bin_dir) + executable_file_ending=build_config.executable_file_ending), cwd=build_config.bin_dir) - run(deploy_command.format( - type=build_type, - prefix_path=qt_bin_path, + run(build_config.deploy_command.format( + type=build_config.build_type, + prefix_path=build_config.qt_bin_path, app="ScreenPlayWallpaper", - executable_file_ending=executable_file_ending), cwd=bin_dir) + executable_file_ending=build_config.executable_file_ending), cwd=build_config.bin_dir) else: # just copy the folders and be done with it if platform.system() == "Linux": # Copy all .so files from the qt_bin_path lib folder into bin_dir - qt_lib_path = qt_bin_path + qt_lib_path = build_config.qt_bin_path for file in qt_lib_path.joinpath("lib").glob("*.so"): - shutil.copy(str(file), str(bin_dir)) + shutil.copy(str(file), str(build_config.bin_dir)) # Copy qt_qml_path folder content into bin_dir - qt_qml_path = qt_bin_path + qt_qml_path = build_config.qt_bin_path for folder in qt_qml_path.joinpath("qml").iterdir(): if not folder.is_file(): shutil.copytree(str(folder), str( - bin_dir.joinpath(folder.name))) + build_config.bin_dir.joinpath(folder.name))) print("Copied %s" % folder) # Copy all plugin folder from qt_bin_path plugins subfolder into bin_dir - qt_plugins_path = qt_bin_path - for folder in qt_bin_path.joinpath("plugins").iterdir(): + qt_plugins_path = build_config.qt_bin_path + for folder in build_config.qt_bin_path.joinpath("plugins").iterdir(): if not folder.is_file(): shutil.copytree(str(folder), str( - bin_dir.joinpath(folder.name))) + build_config.bin_dir.joinpath(folder.name))) print("Copied %s" % folder) # Copy all folder from qt_bin_path translation files into bin_dir translation folder - qt_translations_path = qt_bin_path + qt_translations_path = build_config.qt_bin_path for folder in qt_translations_path.joinpath("translations").iterdir(): if not folder.is_file(): shutil.copytree(str(folder), str( - bin_dir.joinpath("translations").joinpath(folder.name))) + build_config.bin_dir.joinpath("translations").joinpath(folder.name))) print("Copied %s" % folder) # Copy all filesfrom qt_bin_path resources folder into bin_dir folder - qt_resources_path = qt_bin_path - for file in qt_bin_path.joinpath("resources").glob("*"): - shutil.copy(str(file), str(bin_dir)) + qt_resources_path = build_config.qt_bin_path + for file in build_config.qt_bin_path.joinpath("resources").glob("*"): + shutil.copy(str(file), str(build_config.bin_dir)) print("Copied %s" % file) else: print("Not executing deploy commands due to missing dependencies.") - # Post-build - if platform.system() == "Darwin" and sign_build == "ON": - - run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --entitlements \"../../ScreenPlay/entitlements.plist\" --deep \"ScreenPlay.app/\"", cwd=bin_dir) - run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --deep \"ScreenPlayWallpaper.app/\"", cwd=bin_dir) - run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --deep \"ScreenPlayWidget.app/\"", cwd=bin_dir) - - run("codesign --verify --verbose=4 \"ScreenPlay.app/\"", cwd=bin_dir) - run("codesign --verify --verbose=4 \"ScreenPlayWallpaper.app/\"", cwd=bin_dir) - run("codesign --verify --verbose=4 \"ScreenPlayWidget.app/\"", cwd=bin_dir) - - run("xcnotary notarize ScreenPlay.app -d kelteseth@gmail.com -k ScreenPlay", cwd=bin_dir), - run("xcnotary notarize ScreenPlayWallpaper.app -d kelteseth@gmail.com -k ScreenPlay", cwd=bin_dir), - run("xcnotary notarize ScreenPlayWidget.app -d kelteseth@gmail.com -k ScreenPlay", cwd=bin_dir) - - run("spctl --assess --verbose \"ScreenPlay.app/\"", cwd=bin_dir) - run("spctl --assess --verbose \"ScreenPlayWallpaper.app/\"", cwd=bin_dir) - run("spctl --assess --verbose \"ScreenPlayWidget.app/\"", cwd=bin_dir) - - # Copy qml dir into all .app/Contents/MacOS/ + # Copy qml dir into all .app/Contents/MacOS/ if platform.system() == "Darwin": - copytree(Path.joinpath(bin_dir, "qml"), Path.joinpath(bin_dir, "ScreenPlay.app/Contents/MacOS/qml")) - copytree(Path.joinpath(bin_dir, "qml"), Path.joinpath(bin_dir, "ScreenPlayWallpaper.app/Contents/MacOS/qml")) - copytree(Path.joinpath(bin_dir, "qml"), Path.joinpath(bin_dir, "ScreenPlayWidget.app/Contents/MacOS/qml")) + copytree(Path.joinpath(build_config.bin_dir, "qml"), Path.joinpath( + build_config.bin_dir, "ScreenPlay.app/Contents/MacOS/qml")) + copytree(Path.joinpath(build_config.bin_dir, "qml"), Path.joinpath( + build_config.bin_dir, "ScreenPlayWallpaper.app/Contents/MacOS/qml")) + copytree(Path.joinpath(build_config.bin_dir, "qml"), Path.joinpath( + build_config.bin_dir, "ScreenPlayWidget.app/Contents/MacOS/qml")) # Some dlls like openssl do no longer get copied automatically. # Lets just copy all of them into bin. if platform.system() == "Windows": vcpkg_bin_path = Path( - f"{root_path}/../ScreenPlay-vcpkg/installed/x64-windows/bin").resolve() - print(vcpkg_bin_path) + f"{build_config.root_path}/../ScreenPlay-vcpkg/installed/x64-windows/bin").resolve() + print(f"Copy dlls from vcpkg bin path: {vcpkg_bin_path}") for file in vcpkg_bin_path.iterdir(): if file.suffix == ".dll" and file.is_file(): - print(file, bin_dir) - shutil.copy2(file, bin_dir) + print(file, build_config.bin_dir) + shutil.copy2(file, build_config.bin_dir) if not platform.system() == "Darwin": + file_endings = [".ninja_deps", ".ninja", ".ninja_log", ".lib", ".a", ".exp", + ".manifest", ".cmake", ".cbp", "CMakeCache.txt"] for file_ending in file_endings: - for file in bin_dir.rglob("*" + file_ending): + for file in build_config.bin_dir.rglob("*" + file_ending): if file.is_file(): print("Remove: %s" % file.resolve()) file.unlink() - if create_installer == "ON": - os.chdir(build_folder) - print("Running cpack at: ", os.getcwd()) - run("cpack", cwd=build_folder) - # We also need to sign the installer in osx: - if platform.system() == "Darwin" and sign_build == "ON": - run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --deep \"ScreenPlay-Installer.dmg/ScreenPlay-Installer.app/Contents/MacOS/ScreenPlay-Installer\"", cwd=build_folder) - run("codesign --verify --verbose=4 \"ScreenPlay-Installer.dmg/ScreenPlay-Installer.app/Contents/MacOS/ScreenPlay-Installer\"", cwd=build_folder) - run("xcnotary notarize ScreenPlay-Installer.dmg/ScreenPlay-Installer.app -d kelteseth@gmail.com -k ScreenPlay", cwd=build_folder) - run("spctl --assess --verbose \"ScreenPlay-Installer.dmg/ScreenPlay-Installer.app/\"", cwd=build_folder) - run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --deep \"ScreenPlay-Installer.dmg/\"", cwd=build_folder) - run("codesign --verify --verbose=4 \"ScreenPlay-Installer.dmg/\"", - cwd=build_folder) - run("xcnotary notarize ScreenPlay-Installer.dmg -d kelteseth@gmail.com -k ScreenPlay", cwd=build_folder) - run("spctl --assess --verbose \"ScreenPlay-Installer.dmg/\"", - cwd=build_folder) +def create_installer(build_config: BuildConfig, build_result: BuildResult): + os.chdir(build_result.build) + print("Running cpack at: ", os.getcwd()) + run("cpack", cwd=build_config.build_folder) - # Create a zip file for scoop - if platform.system() == "Windows": - zipName = f"ScreenPlay-{SCREENPLAY_VERSION}-{cmake_target_triplet}-{build_type}.zip" - print(f"Creating scoop zip file: \n{bin_dir} \n{zipName}") - os.chdir(build_folder) - with zipfile.ZipFile(zipName, 'w', zipfile.ZIP_DEFLATED) as zipf: - zipdir(bin_dir, zipf) - print("Time taken: {}s".format(time.time() - start_time)) +def sign(build_config: BuildConfig): + + run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --entitlements \"../../ScreenPlay/entitlements.plist\" --deep \"ScreenPlay.app/\"", cwd=build_config.bin_dir) + run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --deep \"ScreenPlayWallpaper.app/\"", cwd=build_config.bin_dir) + run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --deep \"ScreenPlayWidget.app/\"", cwd=build_config.bin_dir) + + run("codesign --verify --verbose=4 \"ScreenPlay.app/\"", cwd=build_config.bin_dir) + run("codesign --verify --verbose=4 \"ScreenPlayWallpaper.app/\"", + cwd=build_config.bin_dir) + run("codesign --verify --verbose=4 \"ScreenPlayWidget.app/\"", + cwd=build_config.bin_dir) + + run("xcnotary notarize ScreenPlay.app -d kelteseth@gmail.com -k ScreenPlay", + cwd=build_config.bin_dir), + run("xcnotary notarize ScreenPlayWallpaper.app -d kelteseth@gmail.com -k ScreenPlay", + cwd=build_config.bin_dir), + run("xcnotary notarize ScreenPlayWidget.app -d kelteseth@gmail.com -k ScreenPlay", + cwd=build_config.bin_dir) + + run("spctl --assess --verbose \"ScreenPlay.app/\"", cwd=build_config.bin_dir) + run("spctl --assess --verbose \"ScreenPlayWallpaper.app/\"", + cwd=build_config.bin_dir) + run("spctl --assess --verbose \"ScreenPlayWidget.app/\"", + cwd=build_config.bin_dir) + + # We also need to sign the installer in osx: + if build_config.create_installer == "ON": + run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --deep \"ScreenPlay-Installer.dmg/ScreenPlay-Installer.app/Contents/MacOS/ScreenPlay-Installer\"", cwd=build_config.build_folder) + run("codesign --verify --verbose=4 \"ScreenPlay-Installer.dmg/ScreenPlay-Installer.app/Contents/MacOS/ScreenPlay-Installer\"", + cwd=build_config.build_folder) + run("xcnotary notarize ScreenPlay-Installer.dmg/ScreenPlay-Installer.app -d kelteseth@gmail.com -k ScreenPlay", + cwd=build_config.build_folder) + run("spctl --assess --verbose \"ScreenPlay-Installer.dmg/ScreenPlay-Installer.app/\"", + cwd=build_config.build_folder) + + run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --deep \"ScreenPlay-Installer.dmg/\"", cwd=build_config.build_folder) + run("codesign --verify --verbose=4 \"ScreenPlay-Installer.dmg/\"", + cwd=build_config.build_folder) + run("xcnotary notarize ScreenPlay-Installer.dmg -d kelteseth@gmail.com -k ScreenPlay", + cwd=build_config.build_folder) + run("spctl --assess --verbose \"ScreenPlay-Installer.dmg/\"", + cwd=build_config.build_folder) + + +def zip(build_config: BuildConfig, build_result: BuildResult) -> BuildResult: + zipName = f"ScreenPlay-{build_config.screenplay_version}-{build_config.cmake_target_triplet}-{build_config.build_type}.zip" + build_result.build_zip = Path(build_config.bin_dir).joinpath(zipName) + print(f"Creating scoop zip file") + os.chdir(build_config.build_folder) + with zipfile.ZipFile(zipName, 'w', zipfile.ZIP_DEFLATED) as zipf: + zipdir(build_config.bin_dir, zipf) + + # Create sha256 hash from zip file + zip_file_path = os.path.join(build_result.build, zipName) + build_hash = sha256(zip_file_path) + build_result.build_hash = Path( + build_result.build).joinpath(zipName + ".sha256.txt") + f = open(build_result.build_hash, "a") + f.write(build_hash) + f.close() + + # Some weird company firewalls do not allow direct .exe downloads + # lets just zip the installer lol + installer_zip = build_result.installer.stem + ".zip" + build_result.installer_zip = Path(build_result.build).joinpath(build_result.installer.stem + ".zip") + zipfile.ZipFile(installer_zip, mode='w').write(build_result.installer) + + return build_result if __name__ == "__main__": @@ -381,7 +483,7 @@ if __name__ == "__main__": qt_version = "6.3.1" qt_ifw_version = "4.4" # Not yet used. - qt_version_overwrite = "" + qt_version_overwrite: str use_aqt = False if args.qt_version_overwrite: @@ -393,7 +495,7 @@ if __name__ == "__main__": print("Using Qt installer framework version {qt_ifw_version}") if not args.build_type: - print("Build type argument is missing (release, debug). E.g: python build.py -type release -steam") + print("Build type argument is missing (release, debug). E.g: python build_config.py -type release -steam") print( f"Defaulting to Qt version: {qt_version}. This can be overwritten via -qt-version 6.5.0") exit(1) @@ -415,19 +517,20 @@ if __name__ == "__main__": if args.create_installer: create_installer = "ON" - sign_build = "OFF" + sign_build = False if args.sign_build: - sign_build = "ON" + sign_build = True - build( - qt_version, - qt_ifw_version, - build_steam, - build_tests, - build_release, - create_installer, - build_type, - sign_build, - args.use_aqt, - args.build_architecture - ) + build_config = BuildConfig() + build_config.qt_version = qt_version + build_config.qt_ifw_version = qt_ifw_version + build_config.build_steam = build_steam + build_config.build_tests = build_tests + build_config.build_release = build_release + build_config.create_installer = create_installer + build_config.build_type = build_type + build_config.sign_build = args.sign_build + build_config.use_aqt = use_aqt + build_config.build_architecture = args.build_architecture + + execute(build_config) diff --git a/Tools/build_and_publish.py b/Tools/build_and_publish.py index 52102707..e1216511 100644 --- a/Tools/build_and_publish.py +++ b/Tools/build_and_publish.py @@ -1,14 +1,17 @@ -from build import build import steam_publish import argparse import os +import build from pathlib import Path from macos_lipo import run_lipo, check_fat_binary import platform +import ftplib + if __name__ == "__main__": parser = argparse.ArgumentParser(description='Build and Package ScreenPlay') parser.add_argument('-steam_password', action="store", dest="steam_password", required=True, help="Steam password") + parser.add_argument('-hosting_password', action="store", dest="hosting_password", required=True, help="FTPS password") args = parser.parse_args() # Script needs to run in the tools folder @@ -17,54 +20,52 @@ if __name__ == "__main__": root_path = tools_path.parent print(f"Set root directory to: {root_path}") + build_result = build.BuildResult() + + build_config = build.BuildConfig() + build_config.screenplay_version = "0.15.0-RC1" + build_config.qt_version = "6.3.1" + build_config.qt_ifw_version = "4.4" + build_config.build_steam = "ON" + build_config.build_tests = "OFF" + build_config.build_release = "ON" + build_config.create_installer = "ON" + build_config.build_type = "release" + build_config.sign_build = True + build_config.use_aqt = False + + if platform.system() == "Darwin": # OSX builds needs to build for x86 and arm # and also be signed! - build( - qt_version="6.4.0", - qt_ifw_version="4.4", - build_steam="ON", - build_tests="OFF", - build_release="ON", - create_installer="ON", - build_type="release", - sign_build=True, - use_aqt=False, - build_architecture = "arm64" - ) - build( - qt_version="6.4.0", - qt_ifw_version="4.4", - build_steam="ON", - build_tests="OFF", - build_release="ON", - create_installer="OFF", - build_type="release", - sign_build=True, - use_aqt=False, - build_architecture = "x64" - ) + + build_config.build_architecture = "arm64" + build_result = build.execute(build_config) + + build_config.build_architecture = "x64" + build_result = build.execute(build_config) + # Make sure to reset to tools path os.chdir(root_path) # Create universal (fat) binary run_lipo() check_fat_binary() else: - build( - qt_version="6.4.0", - qt_ifw_version="4.4", - build_steam="ON", - build_tests="OFF", - build_release="ON", - create_installer="OFF", - build_type="release", - sign_build=False, - use_aqt=False - ) + + build_config.build_architecture = "x64" + build_result = build.execute(build_config) + # Make sure to reset to tools path - os.chdir(tools_path) - #steam_publish( + #os.chdir(tools_path) + #steam_publish.publish( # steam_username="tachiom", # steam_password=args.steam_password, # set_live_branch_name="internal" #) + + #session = ftplib.FTP('wallpaper.software','screenplay_releases',args.hosting_password) + + #file = open('kitten.jpg','rb') # file to send + #session.storbinary('STOR kitten.jpg', file) # send the file + #file.close() # close file and FTP + #session.quit() diff --git a/Tools/steam_publish.py b/Tools/steam_publish.py index 155e6eb2..3f217bed 100644 --- a/Tools/steam_publish.py +++ b/Tools/steam_publish.py @@ -21,7 +21,7 @@ def get_git_revision_short_hash(): def get_git_commit_text(): return subprocess.check_output(['git', 'log', '-1', '--pretty=%B']) -def steam_publish( +def publish( steam_username, steam_password, set_live_branch_name @@ -102,7 +102,7 @@ if __name__ == "__main__": else: set_live_branch_name = "internal" - steam_publish( + publish( steam_username=args.steam_username, steam_password=args.steam_password, set_live_branch_name=set_live_branch_name diff --git a/Tools/util.py b/Tools/util.py index 41b7ba26..fe3b4f6b 100644 --- a/Tools/util.py +++ b/Tools/util.py @@ -1,17 +1,17 @@ - - +import hashlib from pathlib import Path from pathlib import Path from os import chdir +from concurrent.futures import ThreadPoolExecutor +import os import subprocess +import zipfile def run(cmd, cwd=Path.cwd()): result = subprocess.run(cmd, shell=True, cwd=cwd) if result.returncode != 0: raise RuntimeError(f"Failed to execute {cmd}") - - def cd_repo_root_path() -> str: # Make sure the script is always started from the same # ScreenPlay root folder @@ -21,3 +21,27 @@ def cd_repo_root_path() -> str: print(f"Change root directory to: {root_path}") chdir(root_path) return root_path + +def sha256(fname) -> str: + hash_sha256 = hashlib.sha256() + with open(fname, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + hash_sha256.update(chunk) + return hash_sha256.hexdigest() + + + +def zipdir(path, ziph): + # ziph is zipfile handle + for root, dirs, files in os.walk(path): + for file in files: + ziph.write(os.path.join(root, file), + os.path.relpath(os.path.join(root, file), + os.path.join(path, '..'))) + + +def run_io_tasks_in_parallel(tasks): + with ThreadPoolExecutor() as executor: + running_tasks = [executor.submit(task) for task in tasks] + for running_task in running_tasks: + running_task.result() \ No newline at end of file