From 817b89767aa31b3b2847203b27151044f5d323f6 Mon Sep 17 00:00:00 2001 From: Mary-nyan Date: Thu, 1 Dec 2022 14:08:43 +0100 Subject: [PATCH] infra: Add distribution files for macOS (#3934) This upstream macOS packing and distribution files --- distribution/macos/Info.plist | 46 ++ distribution/macos/Ryujinx.icns | Bin 0 -> 108982 bytes distribution/macos/bundle_fix_up.py | 609 ++++++++++++++++++ .../macos/construct_universal_dylib.py | 95 +++ distribution/macos/create_app_bundle.sh | 51 ++ distribution/macos/create_macos_release.sh | 105 +++ distribution/macos/entitlements.xml | 23 + distribution/misc/add_tar_exec.py | 24 + 8 files changed, 953 insertions(+) create mode 100644 distribution/macos/Info.plist create mode 100644 distribution/macos/Ryujinx.icns create mode 100644 distribution/macos/bundle_fix_up.py create mode 100644 distribution/macos/construct_universal_dylib.py create mode 100755 distribution/macos/create_app_bundle.sh create mode 100755 distribution/macos/create_macos_release.sh create mode 100644 distribution/macos/entitlements.xml create mode 100644 distribution/misc/add_tar_exec.py diff --git a/distribution/macos/Info.plist b/distribution/macos/Info.plist new file mode 100644 index 000000000..c3e1e08e4 --- /dev/null +++ b/distribution/macos/Info.plist @@ -0,0 +1,46 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + Ryujinx + CFBundleGetInfoString + Ryujinx + CFBundleIconFile + Ryujinx.icns + CFBundleTypeExtensions + + nca + nro + nso + nsp + xci + + CFBundleIdentifier + org.ryujinx.Ryujinx + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + %%RYUJINX_BUILD_VERSION%%-%%RYUJINX_BUILD_GIT_HASH%%" + CFBundleName + Ryujinx + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.1 + CFBundleSignature + ???? + CFBundleVersion + 1.1.0 + NSHighResolutionCapable + + CSResourcesFileMapped + + NSHumanReadableCopyright + Copyright © 2018 - 2022 Ryujinx Team and Contributors. + LSMinimumSystemVersion + 11.0 + + diff --git a/distribution/macos/Ryujinx.icns b/distribution/macos/Ryujinx.icns new file mode 100644 index 0000000000000000000000000000000000000000..f54a9aeb7e4780b09c1bddb1ad8b7fcf4f9a0d3c GIT binary patch literal 108982 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 str: + res = subprocess.check_output([OTOOL, "-D", str(dylib_path.absolute())]).decode( + "utf-8" + ) + + return res.split("\n")[1] + + +def get_dylib_dependencies(dylib_path: Path) -> List[str]: + output = ( + subprocess.check_output([OTOOL, "-L", str(dylib_path.absolute())]) + .decode("utf-8") + .split("\n")[1:] + ) + + res = [] + + for line in output: + line = line.strip() + index = line.find(" (compatibility version ") + if index == -1: + continue + + line = line[:index] + + res.append(line) + + return res + + +def replace_dylib_id(dylib_path: Path, new_id: str): + subprocess.check_call( + [INSTALL_NAME_TOOL, "-id", new_id, str(dylib_path.absolute())] + ) + + +def change_dylib_link(dylib_path: Path, old: str, new: str): + subprocess.check_call( + [INSTALL_NAME_TOOL, "-change", old, new, str(dylib_path.absolute())] + ) + + +def add_dylib_rpath(dylib_path: Path, rpath: str): + subprocess.check_call( + [INSTALL_NAME_TOOL, "-add_rpath", rpath, str(dylib_path.absolute())] + ) + + +def fixup_dylib( + dylib_path: Path, + replacement_path: str, + search_path: List[str], + content_directory: Path, +): + dylib_id = get_dylib_id(dylib_path) + new_dylib_id = replacement_path + "/" + os.path.basename(dylib_id) + replace_dylib_id(dylib_path, new_dylib_id) + + dylib_dependencies = get_dylib_dependencies(dylib_path) + dylib_new_mapping = {} + + for dylib_dependency in dylib_dependencies: + if ( + not dylib_dependency.startswith("@executable_path") + and not dylib_dependency.startswith("/usr/lib") + and not dylib_dependency.startswith("/System/Library") + ): + dylib_dependency_name = os.path.basename(dylib_dependency) + library_found = False + for library_base_path in search_path: + lib_path = Path(os.path.join(library_base_path, dylib_dependency_name)) + + if lib_path.exists(): + target_replacement_path = get_path_related_to_target_exec( + content_directory, lib_path + ) + + dylib_new_mapping[dylib_dependency] = ( + target_replacement_path + + "/" + + os.path.basename(dylib_dependency) + ) + library_found = True + + if not library_found: + raise Exception( + f"{dylib_id}: Cannot find dependency {dylib_dependency_name} for fixup" + ) + + for key in dylib_new_mapping: + change_dylib_link(dylib_path, key, dylib_new_mapping[key]) + + +FILE_TYPE_ASSEMBLY = 1 + +ALIGN_REQUIREMENTS = 4096 + + +def parse_embedded_string(data: bytes) -> Tuple[bytes, str]: + first_byte = data[0] + + if (first_byte & 0x80) == 0: + size = first_byte + data = data[1:] + else: + second_byte = data[1] + + assert (second_byte & 0x80) == 0 + + size = (second_byte << 7) | (first_byte & 0x7F) + + data = data[2:] + + res = data[:size].decode("utf-8") + data = data[size:] + + return (data, res) + + +def write_embedded_string(file, string: str): + raw_str = string.encode("utf-8") + raw_str_len = len(raw_str) + + assert raw_str_len < 0x7FFF + + if raw_str_len > 0x7F: + file.write(struct.pack("b", raw_str_len & 0x7F | 0x80)) + file.write(struct.pack("b", raw_str_len >> 7)) + else: + file.write(struct.pack("b", raw_str_len)) + + file.write(raw_str) + + +class BundleFileEntry(object): + offset: int + size: int + compressed_size: int + file_type: int + relative_path: str + data: bytes + + def __init__( + self, + offset: int, + size: int, + compressed_size: int, + file_type: int, + relative_path: str, + data: bytes, + ) -> None: + self.offset = offset + self.size = size + self.compressed_size = compressed_size + self.file_type = file_type + self.relative_path = relative_path + self.data = data + + def write(self, file): + self.offset = file.tell() + + if ( + self.file_type == FILE_TYPE_ASSEMBLY + and (self.offset % ALIGN_REQUIREMENTS) != 0 + ): + padding_size = ALIGN_REQUIREMENTS - (self.offset % ALIGN_REQUIREMENTS) + file.write(b"\0" * padding_size) + self.offset += padding_size + + file.write(self.data) + + def write_header(self, file): + file.write( + struct.pack( + "QQQb", self.offset, self.size, self.compressed_size, self.file_type + ) + ) + write_embedded_string(file, self.relative_path) + + +class BundleManifest(object): + major: int + minor: int + bundle_id: str + deps_json: BundleFileEntry + runtimeconfig_json: BundleFileEntry + flags: int + files: List[BundleFileEntry] + + def __init__( + self, + major: int, + minor: int, + bundle_id: str, + deps_json: BundleFileEntry, + runtimeconfig_json: BundleFileEntry, + flags: int, + files: List[BundleFileEntry], + ) -> None: + self.major = major + self.minor = minor + self.bundle_id = bundle_id + self.deps_json = deps_json + self.runtimeconfig_json = runtimeconfig_json + self.flags = flags + self.files = files + + def write(self, file) -> int: + for bundle_file in self.files: + bundle_file.write(file) + + bundle_header_offset = file.tell() + file.write(struct.pack("iiI", self.major, self.minor, len(self.files))) + write_embedded_string(file, self.bundle_id) + + if self.deps_json is not None: + deps_json_location_offset = self.deps_json.offset + deps_json_location_size = self.deps_json.size + else: + deps_json_location_offset = 0 + deps_json_location_size = 0 + + if self.runtimeconfig_json is not None: + runtimeconfig_json_location_offset = self.runtimeconfig_json.offset + runtimeconfig_json_location_size = self.runtimeconfig_json.size + else: + runtimeconfig_json_location_offset = 0 + runtimeconfig_json_location_size = 0 + + file.write( + struct.pack("qq", deps_json_location_offset, deps_json_location_size) + ) + file.write( + struct.pack( + "qq", + runtimeconfig_json_location_offset, + runtimeconfig_json_location_size, + ) + ) + file.write(struct.pack("q", self.flags)) + + for bundle_file in self.files: + bundle_file.write_header(file) + + return bundle_header_offset + + +def read_file_entry( + raw_data: bytes, header_bytes: bytes +) -> Tuple[bytes, BundleFileEntry]: + ( + offset, + size, + compressed_size, + file_type, + ) = struct.unpack("QQQb", header_bytes[:0x19]) + (header_bytes, relative_path) = parse_embedded_string(header_bytes[0x19:]) + + target_size = compressed_size + + if target_size == 0: + target_size = size + + return ( + header_bytes, + BundleFileEntry( + offset, + size, + compressed_size, + file_type, + relative_path, + raw_data[offset : offset + target_size], + ), + ) + + +def get_dotnet_bundle_data(data: bytes) -> Optional[Tuple[int, int, BundleManifest]]: + offset = data.find(hashlib.sha256(b".net core bundle\n").digest()) + + if offset == -1: + return None + + raw_header_offset = data[offset - 8 : offset] + (header_offset,) = struct.unpack("q", raw_header_offset) + header_bytes = data[header_offset:] + + ( + major, + minor, + files_count, + ) = struct.unpack("iiI", header_bytes[:0xC]) + header_bytes = header_bytes[0xC:] + + (header_bytes, bundle_id) = parse_embedded_string(header_bytes) + + # v2 header + ( + deps_json_location_offset, + deps_json_location_size, + ) = struct.unpack("qq", header_bytes[:0x10]) + ( + runtimeconfig_json_location_offset, + runtimeconfig_json_location_size, + ) = struct.unpack("qq", header_bytes[0x10:0x20]) + (flags,) = struct.unpack("q", header_bytes[0x20:0x28]) + header_bytes = header_bytes[0x28:] + + files = [] + + deps_json = None + runtimeconfig_json = None + + for _ in range(files_count): + (header_bytes, file_entry) = read_file_entry(data, header_bytes) + + files.append(file_entry) + + if file_entry.offset == deps_json_location_offset: + deps_json = file_entry + elif file_entry.offset == runtimeconfig_json_location_offset: + runtimeconfig_json = file_entry + + file_entry = files[0] + + return ( + file_entry.offset, + header_offset, + BundleManifest( + major, minor, bundle_id, deps_json, runtimeconfig_json, flags, files + ), + ) + + +LC_SYMTAB = 0x2 +LC_SEGMENT_64 = 0x19 +LC_CODE_SIGNATURE = 0x1D + + +def fixup_linkedit(file, data: bytes, new_size: int): + offset = 0 + + ( + macho_magic, + macho_cputype, + macho_cpusubtype, + macho_filetype, + macho_ncmds, + macho_sizeofcmds, + macho_flags, + macho_reserved, + ) = struct.unpack("IiiIIIII", data[offset : offset + 0x20]) + + offset += 0x20 + + linkedit_offset = None + symtab_offset = None + codesign_offset = None + + for _ in range(macho_ncmds): + (cmd, cmdsize) = struct.unpack("II", data[offset : offset + 8]) + + if cmd == LC_SEGMENT_64: + ( + cmd, + cmdsize, + segname_raw, + vmaddr, + vmsize, + fileoff, + filesize, + maxprot, + initprot, + nsects, + flags, + ) = struct.unpack("II16sQQQQiiII", data[offset : offset + 72]) + segname = segname_raw.decode("utf-8").split("\0")[0] + + if segname == "__LINKEDIT": + linkedit_offset = offset + elif cmd == LC_SYMTAB: + symtab_offset = offset + elif cmd == LC_CODE_SIGNATURE: + codesign_offset = offset + + offset += cmdsize + pass + + assert linkedit_offset is not None and symtab_offset is not None + + # If there is a codesign section, clean it up. + if codesign_offset is not None: + ( + codesign_cmd, + codesign_cmdsize, + codesign_dataoff, + codesign_datasize, + ) = struct.unpack("IIII", data[codesign_offset : codesign_offset + 16]) + file.seek(codesign_offset) + file.write(b"\0" * codesign_cmdsize) + + macho_ncmds -= 1 + macho_sizeofcmds -= codesign_cmdsize + file.seek(0) + file.write( + struct.pack( + "IiiIIIII", + macho_magic, + macho_cputype, + macho_cpusubtype, + macho_filetype, + macho_ncmds, + macho_sizeofcmds, + macho_flags, + macho_reserved, + ) + ) + + file.seek(codesign_dataoff) + file.write(b"\0" * codesign_datasize) + + ( + symtab_cmd, + symtab_cmdsize, + symtab_symoff, + symtab_nsyms, + symtab_stroff, + symtab_strsize, + ) = struct.unpack("IIIIII", data[symtab_offset : symtab_offset + 24]) + + symtab_strsize = new_size - symtab_stroff + + new_symtab = struct.pack( + "IIIIII", + symtab_cmd, + symtab_cmdsize, + symtab_symoff, + symtab_nsyms, + symtab_stroff, + symtab_strsize, + ) + + file.seek(symtab_offset) + file.write(new_symtab) + + ( + linkedit_cmd, + linkedit_cmdsize, + linkedit_segname_raw, + linkedit_vmaddr, + linkedit_vmsize, + linkedit_fileoff, + linkedit_filesize, + linkedit_maxprot, + linkedit_initprot, + linkedit_nsects, + linkedit_flags, + ) = struct.unpack("II16sQQQQiiII", data[linkedit_offset : linkedit_offset + 72]) + + linkedit_filesize = new_size - linkedit_fileoff + linkedit_vmsize = linkedit_filesize + + new_linkedit = struct.pack( + "II16sQQQQiiII", + linkedit_cmd, + linkedit_cmdsize, + linkedit_segname_raw, + linkedit_vmaddr, + linkedit_vmsize, + linkedit_fileoff, + linkedit_filesize, + linkedit_maxprot, + linkedit_initprot, + linkedit_nsects, + linkedit_flags, + ) + file.seek(linkedit_offset) + file.write(new_linkedit) + + +def write_bundle_data( + output, + old_bundle_base_offset: int, + new_bundle_base_offset: int, + bundle: BundleManifest, +) -> int: + # Write bundle data + bundle_header_offset = bundle.write(output) + total_size = output.tell() + + # Patch the header position + offset = file_data.find(hashlib.sha256(b".net core bundle\n").digest()) + output.seek(offset - 8) + output.write(struct.pack("q", bundle_header_offset)) + + return total_size - new_bundle_base_offset + + +input_directory: Path = Path(args.input_directory) +content_directory: Path = Path(os.path.join(args.input_directory, "Contents")) +executable_path: Path = Path(os.path.join(content_directory, args.executable_sub_path)) + + +def get_path_related_to_other_path(a: Path, b: Path) -> str: + temp = b + + parts = [] + + while temp != a: + temp = temp.parent + parts.append(temp.name) + + parts.remove(parts[-1]) + parts.reverse() + + return "/".join(parts) + + +def get_path_related_to_target_exec(input_directory: Path, path: Path): + return "@executable_path/../" + get_path_related_to_other_path( + input_directory, path + ) + + +search_path = [ + Path(os.path.join(content_directory, "Frameworks")), + Path(os.path.join(content_directory, "Resources/lib")), +] + + +for path in content_directory.rglob("**/*.dylib"): + current_search_path = [path.parent] + current_search_path.extend(search_path) + + fixup_dylib( + path, + get_path_related_to_target_exec(content_directory, path), + current_search_path, + content_directory, + ) + +for path in content_directory.rglob("**/*.so"): + current_search_path = [path.parent] + current_search_path.extend(search_path) + + fixup_dylib( + path, + get_path_related_to_target_exec(content_directory, path), + current_search_path, + content_directory, + ) + + +with open(executable_path, "rb") as input: + file_data = input.read() + + +(bundle_base_offset, bundle_header_offset, bundle) = get_dotnet_bundle_data(file_data) + +add_dylib_rpath(executable_path, "@executable_path/../Frameworks/") + +# Recent "vanilla" version of LLVM (LLVM 13 and upper) seems to really dislike how .NET package its assemblies. +# As a result, after execution of install_name_tool it will have "fixed" the symtab resulting in a missing .NET bundle... +# To mitigate that, we check if the bundle offset inside the binary is valid after install_name_tool and readd .NET bundle if not. +output_file_size = os.stat(executable_path).st_size +if output_file_size < bundle_header_offset: + print("LLVM broke the .NET bundle, readding bundle data...") + with open(executable_path, "r+b") as output: + file_data = output.read() + bundle_data_size = write_bundle_data( + output, bundle_base_offset, output_file_size, bundle + ) + + # Now patch the __LINKEDIT section + new_size = output_file_size + bundle_data_size + fixup_linkedit(output, file_data, new_size) diff --git a/distribution/macos/construct_universal_dylib.py b/distribution/macos/construct_universal_dylib.py new file mode 100644 index 000000000..b6c3770c6 --- /dev/null +++ b/distribution/macos/construct_universal_dylib.py @@ -0,0 +1,95 @@ +import argparse +import os +from pathlib import Path +import platform +import shutil +import subprocess + +parser = argparse.ArgumentParser( + description="Construct Universal dylibs for nuget package" +) +parser.add_argument( + "arm64_input_directory", help="ARM64 Input directory containing dylibs" +) +parser.add_argument( + "x86_64_input_directory", help="x86_64 Input directory containing dylibs" +) +parser.add_argument("output_directory", help="Output directory") +parser.add_argument("rglob", help="rglob") + +args = parser.parse_args() + +# Use Apple LLVM on Darwin, otherwise standard LLVM. +if platform.system() == "Darwin": + LIPO = "lipo" +else: + LIPO = shutil.which("llvm-lipo") + + if LIPO is None: + for llvm_ver in [15, 14, 13]: + lipo_path = shutil.which(f"llvm-lipo-{llvm_ver}") + if lipo_path is not None: + LIPO = lipo_path + break + +if LIPO is None: + raise Exception("Cannot find a valid location for LLVM lipo!") + +arm64_input_directory: Path = Path(args.arm64_input_directory) +x86_64_input_directory: Path = Path(args.x86_64_input_directory) +output_directory: Path = Path(args.output_directory) +rglob = args.rglob + + +def get_new_name( + input_directory: Path, output_directory: str, input_dylib_path: Path +) -> Path: + input_component = str(input_dylib_path).replace(str(input_directory), "")[1:] + return Path(os.path.join(output_directory, input_component)) + + +def is_fat_file(dylib_path: Path) -> str: + res = subprocess.check_output([LIPO, "-info", str(dylib_path.absolute())]).decode( + "utf-8" + ) + + return not res.split("\n")[0].startswith("Non-fat file") + + +def construct_universal_dylib( + arm64_input_dylib_path: Path, x86_64_input_dylib_path: Path, output_dylib_path: Path +): + if output_dylib_path.exists() or output_dylib_path.is_symlink(): + os.remove(output_dylib_path) + + os.makedirs(output_dylib_path.parent, exist_ok=True) + + if arm64_input_dylib_path.is_symlink(): + os.symlink( + os.path.basename(arm64_input_dylib_path.resolve()), output_dylib_path + ) + else: + if is_fat_file(arm64_input_dylib_path) or not x86_64_input_dylib_path.exists(): + with open(output_dylib_path, "wb") as dst: + with open(arm64_input_dylib_path, "rb") as src: + dst.write(src.read()) + else: + subprocess.check_call( + [ + LIPO, + str(arm64_input_dylib_path.absolute()), + str(x86_64_input_dylib_path.absolute()), + "-output", + str(output_dylib_path.absolute()), + "-create", + ] + ) + + +print(rglob) +for path in arm64_input_directory.rglob("**/*.dylib"): + construct_universal_dylib( + path, + get_new_name(arm64_input_directory, x86_64_input_directory, path), + get_new_name(arm64_input_directory, output_directory, path), + ) diff --git a/distribution/macos/create_app_bundle.sh b/distribution/macos/create_app_bundle.sh new file mode 100755 index 000000000..8076303cb --- /dev/null +++ b/distribution/macos/create_app_bundle.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +set -e + +PUBLISH_DIRECTORY=$1 +OUTPUT_DIRECTORY=$2 +ENTITLEMENTS_FILE_PATH=$3 + +APP_BUNDLE_DIRECTORY=$OUTPUT_DIRECTORY/Ryujinx.app + +rm -rf $APP_BUNDLE_DIRECTORY +mkdir -p $APP_BUNDLE_DIRECTORY/Contents +mkdir $APP_BUNDLE_DIRECTORY/Contents/Frameworks +mkdir $APP_BUNDLE_DIRECTORY/Contents/MacOS +mkdir $APP_BUNDLE_DIRECTORY/Contents/Resources + +# Copy executables first +cp $PUBLISH_DIRECTORY/Ryujinx.Ava $APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx +chmod u+x $APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx + +# Then all libraries +cp $PUBLISH_DIRECTORY/*.dylib $APP_BUNDLE_DIRECTORY/Contents/Frameworks + +# Then resources +cp Info.plist $APP_BUNDLE_DIRECTORY/Contents +cp Ryujinx.icns $APP_BUNDLE_DIRECTORY/Contents/Resources/Ryujinx.icns +cp -r $PUBLISH_DIRECTORY/THIRDPARTY.md $APP_BUNDLE_DIRECTORY/Contents/Resources + +echo -n "APPL????" > $APP_BUNDLE_DIRECTORY/Contents/PkgInfo + +# Fixup libraries and executable +python3 bundle_fix_up.py $APP_BUNDLE_DIRECTORY MacOS/Ryujinx + +# Now sign it +if ! [ -x "$(command -v codesign)" ]; +then + if ! [ -x "$(command -v rcodesign)" ]; + then + echo "Cannot find rcodesign on your system, please install rcodesign." + exit 1 + fi + + # NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes. + # cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign" + echo "Usign rcodesign for ad-hoc signing" + rcodesign sign --entitlements-xml-path $ENTITLEMENTS_FILE_PATH $APP_BUNDLE_DIRECTORY +else + echo "Usign codesign for ad-hoc signing" + codesign --entitlements $ENTITLEMENTS_FILE_PATH -f --deep -s - $APP_BUNDLE_DIRECTORY +fi + diff --git a/distribution/macos/create_macos_release.sh b/distribution/macos/create_macos_release.sh new file mode 100755 index 000000000..545baf20e --- /dev/null +++ b/distribution/macos/create_macos_release.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +set -e + +if [ "$#" -ne 6 ]; then + echo "usage " + exit 1 +fi + +mkdir -p $1 +mkdir -p $2 +mkdir -p $3 + +BASE_DIR=$(readlink -f $1) +TEMP_DIRECTORY=$(readlink -f $2) +OUTPUT_DIRECTORY=$(readlink -f $3) +ENTITLEMENTS_FILE_PATH=$(readlink -f $4) +VERSION=$5 +SOURCE_REVISION_ID=$6 + +RELEASE_TAR_FILE_NAME=Ryujinx-$VERSION-macos_universal.app.tar +ARM64_APP_BUNDLE=$TEMP_DIRECTORY/output_arm64/Ryujinx.app +X64_APP_BUNDLE=$TEMP_DIRECTORY/output_x64/Ryujinx.app +UNIVERSAL_APP_BUNDLE=$OUTPUT_DIRECTORY/Ryujinx.app +EXECUTABLE_SUB_PATH=Contents/MacOS/Ryujinx + +rm -rf $TEMP_DIRECTORY +mkdir -p $TEMP_DIRECTORY + +DOTNET_COMMON_ARGS="-p:DebugType=embedded -p:Version=$VERSION -p:SourceRevisionId=$SOURCE_REVISION_ID -p:ExtraDefineConstants=DISABLE_UPDATER --self-contained true" + +dotnet restore +dotnet build -c Release Ryujinx.Ava +dotnet publish -c Release -r osx-arm64 -o $TEMP_DIRECTORY/publish_arm64 $DOTNET_COMMON_ARGS Ryujinx.Ava +dotnet publish -c Release -r osx-x64 -o $TEMP_DIRECTORY/publish_x64 $DOTNET_COMMON_ARGS Ryujinx.Ava + +# Get ride of the support library for ARMeilleur for x64 (that's only for arm64) +rm -rf $TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib + +# Get ride of libsoundio from arm64 builds as we don't have a arm64 variant +# TODO: remove this once done +rm -rf $TEMP_DIRECTORY/publish_arm64/libsoundio.dylib + +pushd $BASE_DIR/distribution/macos +./create_app_bundle.sh $TEMP_DIRECTORY/publish_x64 $TEMP_DIRECTORY/output_x64 $ENTITLEMENTS_FILE_PATH +./create_app_bundle.sh $TEMP_DIRECTORY/publish_arm64 $TEMP_DIRECTORY/output_arm64 $ENTITLEMENTS_FILE_PATH +popd + +rm -rf $UNIVERSAL_APP_BUNDLE +mkdir -p $OUTPUT_DIRECTORY + +# Let's copy one of the two different app bundle and remove the executable +cp -R $ARM64_APP_BUNDLE $UNIVERSAL_APP_BUNDLE +rm $UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH + +# Make it libraries universal +python3 $BASE_DIR/distribution/macos/construct_universal_dylib.py $ARM64_APP_BUNDLE $X64_APP_BUNDLE $UNIVERSAL_APP_BUNDLE "**/*.dylib" + +if ! [ -x "$(command -v lipo)" ]; +then + if ! [ -x "$(command -v llvm-lipo-14)" ]; + then + LIPO=llvm-lipo + else + LIPO=llvm-lipo-14 + fi +else + LIPO=lipo +fi + +# Make it the executable universal +$LIPO $ARM64_APP_BUNDLE/$EXECUTABLE_SUB_PATH $X64_APP_BUNDLE/$EXECUTABLE_SUB_PATH -output $UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH -create + +# Patch up the Info.plist to have appropriate version +sed -r -i.bck "s/\%\%RYUJINX_BUILD_VERSION\%\%/$VERSION/g;" $UNIVERSAL_APP_BUNDLE/Contents/Info.plist +sed -r -i.bck "s/\%\%RYUJINX_BUILD_GIT_HASH\%\%/$SOURCE_REVISION_ID/g;" $UNIVERSAL_APP_BUNDLE/Contents/Info.plist +rm $UNIVERSAL_APP_BUNDLE/Contents/Info.plist.bck + +# Now sign it +if ! [ -x "$(command -v codesign)" ]; +then + if ! [ -x "$(command -v rcodesign)" ]; + then + echo "Cannot find rcodesign on your system, please install rcodesign." + exit 1 + fi + + # NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes. + # cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign" + echo "Usign rcodesign for ad-hoc signing" + rcodesign sign --entitlements-xml-path $ENTITLEMENTS_FILE_PATH $UNIVERSAL_APP_BUNDLE +else + echo "Usign codesign for ad-hoc signing" + codesign --entitlements $ENTITLEMENTS_FILE_PATH -f --deep -s - $UNIVERSAL_APP_BUNDLE +fi + +echo "Creating archive" +pushd $OUTPUT_DIRECTORY +tar --exclude "Ryujinx.app/Contents/MacOS/Ryujinx" -cvf $RELEASE_TAR_FILE_NAME Ryujinx.app 1> /dev/null +python3 $BASE_DIR/distribution/misc/add_tar_exec.py $RELEASE_TAR_FILE_NAME "Ryujinx.app/Contents/MacOS/Ryujinx" "Ryujinx.app/Contents/MacOS/Ryujinx" +gzip -9 < $RELEASE_TAR_FILE_NAME > $RELEASE_TAR_FILE_NAME.gz +rm $RELEASE_TAR_FILE_NAME +popd + +echo "Done" diff --git a/distribution/macos/entitlements.xml b/distribution/macos/entitlements.xml new file mode 100644 index 000000000..bf3185071 --- /dev/null +++ b/distribution/macos/entitlements.xml @@ -0,0 +1,23 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.cs.disable-library-validation + + com.apple.security.cs.disable-executable-page-protection + + com.apple.security.cs.debugger + + com.apple.security.get-task-allow + + com.apple.security.hypervisor + + + diff --git a/distribution/misc/add_tar_exec.py b/distribution/misc/add_tar_exec.py new file mode 100644 index 000000000..fe659c161 --- /dev/null +++ b/distribution/misc/add_tar_exec.py @@ -0,0 +1,24 @@ +import argparse +from io import BytesIO +import tarfile + +parser = argparse.ArgumentParser( + description="Add the main binary to a tar and force it to be executable" +) +parser.add_argument("input_tar_file", help="input tar file") +parser.add_argument("main_binary_path", help="Main executable path") +parser.add_argument("main_binary_tar_path", help="Main executable tar path") + +args = parser.parse_args() +input_tar_file = args.input_tar_file +main_binary_path = args.main_binary_path +main_binary_tar_path = args.main_binary_tar_path + +with open(main_binary_path, "rb") as f: + with tarfile.open(input_tar_file, "a") as tar: + data = f.read() + tar_info = tarfile.TarInfo(main_binary_tar_path) + tar_info.mode = 0o755 + tar_info.size = len(data) + + tar.addfile(tar_info, BytesIO(data))