From 6d5597e93e15448a10d1938fd9c124b1a3a853fd Mon Sep 17 00:00:00 2001 From: Felix Weilbach Date: Fri, 5 Nov 2021 13:52:54 +0100 Subject: Add a script to create a universal app package Signed-off-by: Felix Weilbach --- admin/osx/make_universal.py | 82 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100755 admin/osx/make_universal.py diff --git a/admin/osx/make_universal.py b/admin/osx/make_universal.py new file mode 100755 index 000000000..abc6e671f --- /dev/null +++ b/admin/osx/make_universal.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python + +import sys +import os +import subprocess + + +# A general note: We first produce a x86_64 and a arm64 app package +# and then merge them together instead of compiling the desktop client +# with the CMake option CMAKE_OSX_ARCHITECTURES="x86_64;arm64" because +# macdeployqt can not handle universal binaries well. In the future +# with Qt6 this might change and this script will become obsolete. + + +def usage(program_name): + print("Creates a universal app package from a x86_64 and a arm64 app package.") + print("Usage: {} x86_64_app_file arm64_app_file output_directory".format(program_name)) + print("Example: {} some_dir/Nextcloud.app some_other_dir/Nextcloud.app output_dir".format(program_name)) + + +def execute(command): + return subprocess.check_output(command) + + +def path_relative_to_package(app_package_file_path, file_path): + if file_path.startswith(app_package_file_path): + relative_path = file_path[len(app_package_file_path):] + if relative_path.startswith("/"): + return relative_path[1:] + return relative_path + return file_path + + +def is_executable(file_path): + output = str(execute(["file", file_path])) + if (("Mach-O 64-bit dynamically linked shared library" in output) + or ("Mach-O 64-bit executable" in output)): + return True + return False + + +if __name__ == "__main__": + if len(sys.argv) != 4: + usage(sys.argv[0]) + sys.exit(1) + + x86_64_app_file = sys.argv[1] + if not os.path.exists(x86_64_app_file): + print("Can't create universal: Path {} already exists".format(x86_64_app_file)) + sys.exit(1) + arm64_app_file = sys.argv[2] + if not os.path.exists(arm64_app_file): + print("Can't create universal: Path {} already exists".format(arm64_app_file)) + sys.exit(1) + output_dir = sys.argv[3] + + # Copy the Arm64 variant to the output location if possible + if not os.path.exists(output_dir): + os.makedirs(output_dir) + app_file_name = os.path.basename(arm64_app_file) + universal_app_file = os.path.join(output_dir, app_file_name) + if os.path.exists(universal_app_file): + print("Can't create universal: Path {} already exists".format(universal_app_file)) + sys.exit(1) + + execute(["cp", "-a", arm64_app_file, output_dir]) + + # Now walk through the copied arm64 version and replace the binaries + for root, dirs, files in os.walk(universal_app_file): + for f in files: + absoulte_file_path = os.path.join(root, f) + root_relative = path_relative_to_package(universal_app_file, root) + x86_64_absolute_path = os.path.join(x86_64_app_file, root_relative, f) + arm64_absolute_path = os.path.join(arm64_app_file, root_relative, f) + if os.path.islink(absoulte_file_path) or not is_executable(absoulte_file_path): + continue + try: + execute(["lipo", "-create", "-output", absoulte_file_path, arm64_absolute_path, x86_64_absolute_path]) + except: + print("Could not merge {} with {} into {}!".format(arm64_absolute_path, x86_64_absolute_path, absoulte_file_path)) + + print("Finished :)") -- cgit v1.2.3