Apple M1: Build, Analytics, and Memory Management
Analytics: - Incorporated fix to allow the full set of analytics that was recommended by spotlightishere BuildMacOSUniversalBinary: - The x86_64 slice for a universal binary is now built for 10.12 - The universal binary build script now can be configured though command line options instead of modifying the script itself. - os.system calls were replaced with equivalent subprocess calls - Formatting was reworked to be more PEP 8 compliant - The script was refactored to make it more modular - The com.apple.security.cs.disable-library-validation entitlement was removed Memory Management: - Changed the JITPageWrite*Execute*() functions to incorporate support for nesting Other: - Fixed several small lint errors - Fixed doc and formatting mistakes - Several small refactors to make things clearer
This commit is contained in:
parent
4ecb3084b7
commit
948764d37b
|
@ -1,146 +0,0 @@
|
||||||
"""
|
|
||||||
The current tooling supported in CMake, Homebrew, and QT5 are insufficient for creating
|
|
||||||
MacOSX universal binaries automatically for applications like Dolphin which have more
|
|
||||||
complicated build requirements (like different libraries, build flags and source files
|
|
||||||
for each target architecture).
|
|
||||||
|
|
||||||
So instead, this script manages the conifiguration and compilation of distinct builds
|
|
||||||
and project files for each target architecture and then merges the two binaries into
|
|
||||||
a single universal binary.
|
|
||||||
|
|
||||||
Running this script will:
|
|
||||||
1) Generate Xcode project files for the ARM build (if project files don't already exist)
|
|
||||||
2) Generate Xcode project files for the x64 build (if project files don't already exist)
|
|
||||||
3) Build the ARM project for the selected build_target
|
|
||||||
4) Build the x64 project for the selected build_target
|
|
||||||
5) Generates universal .app packages combining the ARM and x64 packages
|
|
||||||
6) Utilizes the lipo tool to combine the binary objects inside each of the packages
|
|
||||||
into universal binaries
|
|
||||||
7) Code signs the final universal binaries using the specified codesign_identity
|
|
||||||
"""
|
|
||||||
|
|
||||||
##BEGIN CONFIG##
|
|
||||||
#Location of destination universal binary
|
|
||||||
dst_app = "universal/"
|
|
||||||
#Build Target (dolphin-emu to just build the emulator and skip the tests)
|
|
||||||
build_target = "ALL_BUILD"
|
|
||||||
|
|
||||||
#Locations to pkg config files for arm and x64 libraries
|
|
||||||
#The default values of these paths are taken from the default
|
|
||||||
#paths used for homebrew
|
|
||||||
arm_pkg_config_path='/opt/homebrew/lib/pkgconfig'
|
|
||||||
x64_pkg_config_path='/usr/local/lib/pkgconfig'
|
|
||||||
|
|
||||||
#Locations to qt5 directories for arm and x64 libraries
|
|
||||||
#The default values of these paths are taken from the default
|
|
||||||
#paths used for homebrew
|
|
||||||
arm_qt5_path='/opt/homebrew/opt/qt5'
|
|
||||||
x64_qt5_path='/usr/local/opt/qt5'
|
|
||||||
|
|
||||||
# Identity to use for code signing. "-" indicates that the app will not
|
|
||||||
# be cryptographically signed/notarized but will instead just use a
|
|
||||||
# SHA checksum to verify the integrity of the app. This doesn't
|
|
||||||
# protect against malicious actors, but it does protect against
|
|
||||||
# running corrupted binaries and allows for access to the extended
|
|
||||||
# permisions needed for ARM builds
|
|
||||||
|
|
||||||
codesign_identity ='"-"'
|
|
||||||
|
|
||||||
##END CONFIG##
|
|
||||||
|
|
||||||
import glob
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import filecmp
|
|
||||||
|
|
||||||
|
|
||||||
#Configure ARM project files if they don't exist
|
|
||||||
if not os.path.exists("arm"):
|
|
||||||
os.mkdir("arm");
|
|
||||||
os.chdir("arm");
|
|
||||||
|
|
||||||
os.system('PKG_CONFIG_PATH="'+arm_pkg_config_path+'" Qt5_DIR="'+arm_qt5_path+'" CMAKE_OSX_ARCHITECTURES=arm64 arch -arm64 cmake ../../ -G Xcode');
|
|
||||||
os.chdir("..");
|
|
||||||
|
|
||||||
#Configure x64 project files if they don't exist
|
|
||||||
if not os.path.exists("x64"):
|
|
||||||
os.mkdir("x64");
|
|
||||||
os.chdir("x64");
|
|
||||||
|
|
||||||
os.system('PKG_CONFIG_PATH="'+x64_pkg_config_path+'" Qt5_DIR="'+x64_qt5_path+'" CMAKE_OSX_ARCHITECTURES=x86_64 arch -x86_64 cmake ../../ -G Xcode')
|
|
||||||
os.chdir("..");
|
|
||||||
|
|
||||||
#Build ARM and x64 projects
|
|
||||||
os.system('xcodebuild -project arm/dolphin-emu.xcodeproj -target "'+build_target+'" -configuration Release');
|
|
||||||
os.system('xcodebuild -project x64/dolphin-emu.xcodeproj -target "'+build_target+'" -configuration Release');
|
|
||||||
|
|
||||||
#Merge ARM and x64 binaries into universal binaries
|
|
||||||
|
|
||||||
#Source binaries to merge together
|
|
||||||
src_app0 = "arm/Binaries/release"
|
|
||||||
src_app1 = "x64/Binaries/release"
|
|
||||||
|
|
||||||
|
|
||||||
if os.path.exists(dst_app): shutil.rmtree(dst_app)
|
|
||||||
os.mkdir(dst_app);
|
|
||||||
|
|
||||||
def lipo(path0,path1,dst):
|
|
||||||
cmd = 'lipo -create -output "'+dst + '" "' + path0 +'" "' + path1+'"'
|
|
||||||
print(cmd)
|
|
||||||
os.system(cmd)
|
|
||||||
def recursiveMergeBinaries(src0,src1,dst):
|
|
||||||
#loop over all files in src0
|
|
||||||
for newpath0 in glob.glob(src0+"/*"):
|
|
||||||
filename = os.path.basename(newpath0);
|
|
||||||
newpath1 = os.path.join(src1,filename);
|
|
||||||
new_dst_path = os.path.join(dst,filename);
|
|
||||||
if not os.path.islink(newpath0):
|
|
||||||
if os.path.exists(newpath1):
|
|
||||||
if os.path.isdir(newpath1):
|
|
||||||
os.mkdir(new_dst_path);
|
|
||||||
#recurse into directories
|
|
||||||
recursiveMergeBinaries(newpath0,newpath1,new_dst_path)
|
|
||||||
else:
|
|
||||||
if filecmp.cmp(newpath0,newpath1):
|
|
||||||
#copy files that are the same
|
|
||||||
shutil.copy(newpath0,new_dst_path);
|
|
||||||
else:
|
|
||||||
#lipo together files that are different
|
|
||||||
lipo(newpath0,newpath1,new_dst_path)
|
|
||||||
else:
|
|
||||||
#copy files that don't exist in path1
|
|
||||||
shutil.copy(newpath0,new_dst_path)
|
|
||||||
|
|
||||||
#loop over files in src1 and copy missing things over to dst
|
|
||||||
for newpath1 in glob.glob(src1+"/*"):
|
|
||||||
filename = os.path.basename(newpath0);
|
|
||||||
newpath0 = os.path.join(src0,filename);
|
|
||||||
new_dst_path = os.path.join(dst,filename);
|
|
||||||
if not os.path.exists(newpath0) and not os.path.islink(newpath1):
|
|
||||||
shutil.copytree(newpath1,new_dst_path);
|
|
||||||
|
|
||||||
#fix up symlinks for path0
|
|
||||||
for newpath0 in glob.glob(src0+"/*"):
|
|
||||||
filename = os.path.basename(newpath0);
|
|
||||||
new_dst_path = os.path.join(dst,filename);
|
|
||||||
if os.path.islink(newpath0):
|
|
||||||
relative_path = os.path.relpath(os.path.realpath(newpath0),src0)
|
|
||||||
print(relative_path,new_dst_path)
|
|
||||||
os.symlink(relative_path,new_dst_path);
|
|
||||||
#fix up symlinks for path1
|
|
||||||
for newpath1 in glob.glob(src1+"/*"):
|
|
||||||
filename = os.path.basename(newpath1);
|
|
||||||
new_dst_path = os.path.join(dst,filename);
|
|
||||||
newpath0 = os.path.join(src0,filename);
|
|
||||||
if os.path.islink(newpath1) and not os.path.exists(newpath0):
|
|
||||||
relative_path = os.path.relpath(os.path.realpath(newpath1),src1)
|
|
||||||
print(relative_path,new_dst_path)
|
|
||||||
os.symlink(relative_path,new_dst_path);
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
#create univeral binary
|
|
||||||
recursiveMergeBinaries(src_app0,src_app1,dst_app);
|
|
||||||
#codesign
|
|
||||||
os.system("codesign --deep --force -s "+codesign_identity+" " +dst_app +"/*");
|
|
|
@ -0,0 +1,278 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
The current tooling supported in CMake, Homebrew, and QT5 are insufficient for
|
||||||
|
creating macOS universal binaries automatically for applications like Dolphin
|
||||||
|
which have more complicated build requirements (like different libraries, build
|
||||||
|
flags and source files for each target architecture).
|
||||||
|
|
||||||
|
So instead, this script manages the configuration and compilation of distinct
|
||||||
|
builds and project files for each target architecture and then merges the two
|
||||||
|
binaries into a single universal binary.
|
||||||
|
|
||||||
|
Running this script will:
|
||||||
|
1) Generate Xcode project files for the ARM build (if project files don't
|
||||||
|
already exist)
|
||||||
|
2) Generate Xcode project files for the x64 build (if project files don't
|
||||||
|
already exist)
|
||||||
|
3) Build the ARM project for the selected build_target
|
||||||
|
4) Build the x64 project for the selected build_target
|
||||||
|
5) Generates universal .app packages combining the ARM and x64 packages
|
||||||
|
6) Utilizes the lipo tool to combine the binary objects inside each of the
|
||||||
|
packages into universal binaries
|
||||||
|
7) Code signs the final universal binaries using the specified
|
||||||
|
codesign_identity
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import copy
|
||||||
|
import filecmp
|
||||||
|
import glob
|
||||||
|
import json # Used to print config
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# #BEGIN CONFIG# #
|
||||||
|
|
||||||
|
# The config variables listed below are the defaults, but they can be
|
||||||
|
# overridden by command line arguments see parse_args(), or run:
|
||||||
|
# BuildMacOSUniversalBinary.py --help
|
||||||
|
DEFAULT_CONFIG = {
|
||||||
|
|
||||||
|
# Location of destination universal binary
|
||||||
|
"dst_app": "universal/",
|
||||||
|
# Build Target (dolphin-emu to just build the emulator and skip the tests)
|
||||||
|
"build_target": "ALL_BUILD",
|
||||||
|
|
||||||
|
# Locations to pkg config files for arm and x64 libraries
|
||||||
|
# The default values of these paths are taken from the default
|
||||||
|
# paths used for homebrew
|
||||||
|
"arm64_pkg_config_path": '/opt/homebrew/lib/pkgconfig',
|
||||||
|
"x86_64_pkg_config_path": '/usr/local/lib/pkgconfig',
|
||||||
|
|
||||||
|
# Locations to qt5 directories for arm and x64 libraries
|
||||||
|
# The default values of these paths are taken from the default
|
||||||
|
# paths used for homebrew
|
||||||
|
"arm64_qt5_path": '/opt/homebrew/opt/qt5',
|
||||||
|
"x86_64_qt5_path": '/usr/local/opt/qt5',
|
||||||
|
|
||||||
|
# Identity to use for code signing. "-" indicates that the app will not
|
||||||
|
# be cryptographically signed/notarized but will instead just use a
|
||||||
|
# SHA checksum to verify the integrity of the app. This doesn't
|
||||||
|
# protect against malicious actors, but it does protect against
|
||||||
|
# running corrupted binaries and allows for access to the extended
|
||||||
|
# permisions needed for ARM builds
|
||||||
|
"codesign_identity": '-',
|
||||||
|
# Etitlements file to use for code signing
|
||||||
|
"entitlements": "../Source/Core/DolphinQt/DolphinEmu.entitlements",
|
||||||
|
|
||||||
|
# Minimum macOS version for each architecture slice
|
||||||
|
"arm64_mac_os_deployment_target": "11.0.0",
|
||||||
|
"x86_64_mac_os_deployment_target": "10.12.0"
|
||||||
|
|
||||||
|
}
|
||||||
|
# # END CONFIG # #
|
||||||
|
|
||||||
|
# Architectures to build for. This is explicity left out of the command line
|
||||||
|
# config options for several reasons:
|
||||||
|
# 1) Adding new architectures will generally require more code changes
|
||||||
|
# 2) Single architecture builds should utilize the normal generated cmake
|
||||||
|
# project files rather than this wrapper script
|
||||||
|
|
||||||
|
ARCHITECTURES = ["x86_64", "arm64"]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args(default_conf=DEFAULT_CONFIG):
|
||||||
|
"""
|
||||||
|
Parses the command line arguments into a config dictionary.
|
||||||
|
"""
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--target',
|
||||||
|
help='Build target in generated project files',
|
||||||
|
default=default_conf["build_target"],
|
||||||
|
dest="build_target")
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--dst_app',
|
||||||
|
help='Directory where universal binary will be stored',
|
||||||
|
default=default_conf["dst_app"])
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--entitlements',
|
||||||
|
help='Path to .entitlements file for code signing',
|
||||||
|
default=default_conf["entitlements"])
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--codesign',
|
||||||
|
help='Code signing identity to use to sign the applications',
|
||||||
|
default=default_conf["codesign_identity"],
|
||||||
|
dest="codesign_identity")
|
||||||
|
|
||||||
|
for arch in ARCHITECTURES:
|
||||||
|
parser.add_argument(
|
||||||
|
'--{}_pkg_config'.format(arch),
|
||||||
|
help="Folder containing .pc files for {} libraries".format(arch),
|
||||||
|
default=default_conf[arch+"_pkg_config_path"],
|
||||||
|
dest=arch+"_pkg_config_path")
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--{}_qt5_path'.format(arch),
|
||||||
|
help="Install path for {} qt5 libraries".format(arch),
|
||||||
|
default=default_conf[arch+"_qt5_path"])
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--{}_mac_os_deployment_target'.format(arch),
|
||||||
|
help="Deployment architecture for {} slice".format(arch),
|
||||||
|
default=default_conf[arch+"_mac_os_deployment_target"])
|
||||||
|
|
||||||
|
return vars(parser.parse_args())
|
||||||
|
|
||||||
|
|
||||||
|
def lipo(path0, path1, dst):
|
||||||
|
if subprocess.call(['lipo', '-create', '-output', dst, path0, path1]) != 0:
|
||||||
|
print("WARNING: {} and {} can not be lipo'd, keeping {}"
|
||||||
|
.format(path0, path1, path0))
|
||||||
|
|
||||||
|
shutil.copy(path0, dst)
|
||||||
|
|
||||||
|
|
||||||
|
def recursiveMergeBinaries(src0, src1, dst):
|
||||||
|
"""
|
||||||
|
Merges two build trees together for different architectures into a single
|
||||||
|
universal binary.
|
||||||
|
|
||||||
|
The rules for merging are:
|
||||||
|
|
||||||
|
1) Files that exist in either src tree are copied into the dst tree
|
||||||
|
2) Files that exist in both trees and are identical are copied over
|
||||||
|
unmodified
|
||||||
|
3) Files that exist in both trees and are non-identical are lipo'd
|
||||||
|
4) Symlinks are created in the destination tree to mirror the hierarchy in
|
||||||
|
the source trees
|
||||||
|
"""
|
||||||
|
|
||||||
|
# loop over all files in src0
|
||||||
|
for newpath0 in glob.glob(src0+"/*"):
|
||||||
|
filename = os.path.basename(newpath0)
|
||||||
|
newpath1 = os.path.join(src1, filename)
|
||||||
|
new_dst_path = os.path.join(dst, filename)
|
||||||
|
if os.path.islink(newpath0):
|
||||||
|
# symlinks will be fixed after files are resolved
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not os.path.exists(newpath1):
|
||||||
|
# copy files that don't exist in path1
|
||||||
|
shutil.copy(newpath0, new_dst_path)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if os.path.isdir(newpath1):
|
||||||
|
os.mkdir(new_dst_path)
|
||||||
|
# recurse into directories
|
||||||
|
recursiveMergeBinaries(newpath0, newpath1, new_dst_path)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if filecmp.cmp(newpath0, newpath1):
|
||||||
|
# copy files that are the same
|
||||||
|
shutil.copy(newpath0, new_dst_path)
|
||||||
|
else:
|
||||||
|
# lipo together files that are different
|
||||||
|
lipo(newpath0, newpath1, new_dst_path)
|
||||||
|
|
||||||
|
# loop over files in src1 and copy missing things over to dst
|
||||||
|
for newpath1 in glob.glob(src1+"/*"):
|
||||||
|
filename = os.path.basename(newpath0)
|
||||||
|
newpath0 = os.path.join(src0, filename)
|
||||||
|
new_dst_path = os.path.join(dst, filename)
|
||||||
|
if not os.path.exists(newpath0) and not os.path.islink(newpath1):
|
||||||
|
shutil.copytree(newpath1, new_dst_path)
|
||||||
|
|
||||||
|
# fix up symlinks for path0
|
||||||
|
for newpath0 in glob.glob(src0+"/*"):
|
||||||
|
filename = os.path.basename(newpath0)
|
||||||
|
new_dst_path = os.path.join(dst, filename)
|
||||||
|
if os.path.islink(newpath0):
|
||||||
|
relative_path = os.path.relpath(os.path.realpath(newpath0), src0)
|
||||||
|
os.symlink(relative_path, new_dst_path)
|
||||||
|
# fix up symlinks for path1
|
||||||
|
for newpath1 in glob.glob(src1+"/*"):
|
||||||
|
filename = os.path.basename(newpath1)
|
||||||
|
new_dst_path = os.path.join(dst, filename)
|
||||||
|
newpath0 = os.path.join(src0, filename)
|
||||||
|
if os.path.islink(newpath1) and not os.path.exists(newpath0):
|
||||||
|
relative_path = os.path.relpath(os.path.realpath(newpath1), src1)
|
||||||
|
os.symlink(relative_path, new_dst_path)
|
||||||
|
|
||||||
|
|
||||||
|
def build(config):
|
||||||
|
"""
|
||||||
|
Builds the project with the parameters specified in config.
|
||||||
|
"""
|
||||||
|
|
||||||
|
print("Building config:")
|
||||||
|
print(json.dumps(config, indent=4))
|
||||||
|
|
||||||
|
dst_app = config["dst_app"]
|
||||||
|
# Configure and build single architecture builds for each architecture
|
||||||
|
for arch in ARCHITECTURES:
|
||||||
|
# Create build directory for architecture
|
||||||
|
if not os.path.exists(arch):
|
||||||
|
os.mkdir(arch)
|
||||||
|
# Setup environment variables for build
|
||||||
|
envs = os.environ.copy()
|
||||||
|
envs['PKG_CONFIG_PATH'] = config[arch+"_pkg_config_path"]
|
||||||
|
envs['Qt5_DIR'] = config[arch+"_qt5_path"]
|
||||||
|
envs['CMAKE_OSX_ARCHITECTURES'] = arch
|
||||||
|
|
||||||
|
subprocess.check_call([
|
||||||
|
'arch', '-'+arch,
|
||||||
|
'cmake', '../../', '-G', 'Xcode',
|
||||||
|
'-DCMAKE_OSX_DEPLOYMENT_TARGET='
|
||||||
|
+ config[arch+"_mac_os_deployment_target"],
|
||||||
|
'-DMACOS_CODE_SIGNING_IDENTITY='
|
||||||
|
+ config['codesign_identity'],
|
||||||
|
'-DMACOS_CODE_SIGNING_IDENTITY_UPDATER='
|
||||||
|
+ config['codesign_identity'],
|
||||||
|
'-DMACOS_CODE_SIGNING="ON"'
|
||||||
|
],
|
||||||
|
env=envs, cwd=arch)
|
||||||
|
|
||||||
|
# Build project
|
||||||
|
subprocess.check_call(['xcodebuild',
|
||||||
|
'-project', 'dolphin-emu.xcodeproj',
|
||||||
|
'-target', config["build_target"],
|
||||||
|
'-configuration', 'Release'], cwd=arch)
|
||||||
|
|
||||||
|
# Source binary trees to merge together
|
||||||
|
src_app0 = ARCHITECTURES[0]+"/Binaries/release"
|
||||||
|
src_app1 = ARCHITECTURES[1]+"/Binaries/release"
|
||||||
|
|
||||||
|
if os.path.exists(dst_app):
|
||||||
|
shutil.rmtree(dst_app)
|
||||||
|
|
||||||
|
os.mkdir(dst_app)
|
||||||
|
# create univeral binary
|
||||||
|
recursiveMergeBinaries(src_app0, src_app1, dst_app)
|
||||||
|
# codesign the universal binary
|
||||||
|
for path in glob.glob(dst_app+"/*"):
|
||||||
|
subprocess.check_call([
|
||||||
|
'codesign',
|
||||||
|
'-d',
|
||||||
|
'--force',
|
||||||
|
'-s',
|
||||||
|
config["codesign_identity"],
|
||||||
|
'--options', 'runtime',
|
||||||
|
'--entitlements', config["entitlements"],
|
||||||
|
'--deep',
|
||||||
|
'--verbose=2',
|
||||||
|
path])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
conf = parse_args()
|
||||||
|
build(conf)
|
||||||
|
print("Built Universal Binary successfully!")
|
|
@ -6,8 +6,12 @@ cmake_minimum_required(VERSION 3.10)
|
||||||
# Minimum OS X version.
|
# Minimum OS X version.
|
||||||
# This is inserted into the Info.plist as well.
|
# This is inserted into the Info.plist as well.
|
||||||
|
|
||||||
# MacOS prior to 10.14 did not fully support C++17, which is used to
|
# MacOS prior to 10.14 did not support aligned alloc which is used to implement
|
||||||
# handle configuration options and aligned alloc
|
# std::unique_ptr in the arm64 C++ standard library. x86_64 builds can override
|
||||||
|
# this to 10.12.0 using -DCMAKE_OSX_DEPLOYMENT_TARGET="10.12.0" without issue.
|
||||||
|
# This is done in the universal binary building script to build a binary that
|
||||||
|
# runs on 10.12 on x86_64 computers, while still containing an arm64 slice.
|
||||||
|
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14.0" CACHE STRING "")
|
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14.0" CACHE STRING "")
|
||||||
|
|
||||||
set(CMAKE_USER_MAKE_RULES_OVERRIDE "CMake/FlagsOverride.cmake")
|
set(CMAKE_USER_MAKE_RULES_OVERRIDE "CMake/FlagsOverride.cmake")
|
||||||
|
@ -68,8 +72,12 @@ else()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
option(OSX_USE_DEFAULT_SEARCH_PATH "Don't prioritize system library paths" OFF)
|
option(MACOS_USE_DEFAULT_SEARCH_PATH "Don't prioritize system library paths" OFF)
|
||||||
option(SKIP_POSTPROCESS_BUNDLE "Skip postprocessing bundle for redistributability" OFF)
|
option(SKIP_POSTPROCESS_BUNDLE "Skip postprocessing bundle for redistributability" OFF)
|
||||||
|
# Enable adhoc code signing by default (otherwise makefile builds on ARM will not work)
|
||||||
|
option(MACOS_CODE_SIGNING "Enable codesigning" ON)
|
||||||
|
set(MACOS_CODE_SIGNING_IDENTITY "-" CACHE STRING "The identity used for codesigning.")
|
||||||
|
set(MACOS_CODE_SIGNING_IDENTITY_UPDATER "-" CACHE STRING "The identity used for codesigning, for the updater.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
|
@ -290,7 +298,7 @@ else()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
||||||
if(NOT OSX_USE_DEFAULT_SEARCH_PATH)
|
if(NOT MACOS_USE_DEFAULT_SEARCH_PATH)
|
||||||
# Hack up the path to prioritize the path to built-in OS libraries to
|
# Hack up the path to prioritize the path to built-in OS libraries to
|
||||||
# increase the chance of not depending on a bunch of copies of them
|
# increase the chance of not depending on a bunch of copies of them
|
||||||
# installed by MacPorts, Fink, Homebrew, etc, and ending up copying
|
# installed by MacPorts, Fink, Homebrew, etc, and ending up copying
|
||||||
|
|
|
@ -73,7 +73,7 @@ application bundle using the following steps:
|
||||||
4. Universal binaries will be available in the `universal` folder
|
4. Universal binaries will be available in the `universal` folder
|
||||||
|
|
||||||
Doing this requires installation of library dependencies for both x64 and ARM (or universal library
|
Doing this requires installation of library dependencies for both x64 and ARM (or universal library
|
||||||
equilvalents) and may require modification of the config portion of the script to point to the
|
equivalents) and may require modification of the config portion of the script to point to the
|
||||||
library locations
|
library locations
|
||||||
|
|
||||||
A binary supporting a single architecture can be built as well using the following steps:
|
A binary supporting a single architecture can be built as well using the following steps:
|
||||||
|
|
|
@ -345,7 +345,7 @@ void ARM64XEmitter::FlushIcacheSection(u8* start, u8* end)
|
||||||
if (start == end)
|
if (start == end)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#if defined(IOS) ||defined(__APPLE__)
|
#if defined(IOS) || defined(__APPLE__)
|
||||||
// Header file says this is equivalent to: sys_icache_invalidate(start, end - start);
|
// Header file says this is equivalent to: sys_icache_invalidate(start, end - start);
|
||||||
sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start);
|
sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start);
|
||||||
#elif defined(WIN32)
|
#elif defined(WIN32)
|
||||||
|
|
|
@ -39,12 +39,11 @@ void* AllocateExecutableMemory(size_t size)
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||||
#else
|
#else
|
||||||
int map_flags =MAP_ANON | MAP_PRIVATE;
|
int map_flags = MAP_ANON | MAP_PRIVATE;
|
||||||
#if defined(_M_ARM_64) && defined(__APPLE__)
|
#if defined(_M_ARM_64) && defined(__APPLE__)
|
||||||
map_flags |= MAP_JIT;
|
map_flags |= MAP_JIT;
|
||||||
#endif
|
#endif
|
||||||
void* ptr =
|
void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, map_flags, -1, 0);
|
||||||
mmap(nullptr, size, PROT_READ | PROT_WRITE |PROT_EXEC , map_flags, -1, 0);
|
|
||||||
if (ptr == MAP_FAILED)
|
if (ptr == MAP_FAILED)
|
||||||
ptr = nullptr;
|
ptr = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
@ -54,7 +53,19 @@ void* AllocateExecutableMemory(size_t size)
|
||||||
|
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
// Certain platforms (Mac OS X on ARM) enforce that a single thread can only have write or
|
// This function is used to provide a counter for the JITPageWrite*Execute*
|
||||||
|
// functions to enable nesting. The static variable is wrapped in a a function
|
||||||
|
// to allow those functions to be called inside of the constructor of a static
|
||||||
|
// variable portably.
|
||||||
|
//
|
||||||
|
// The variable is thread_local as the W^X mode is specific to each running thread.
|
||||||
|
static int& JITPageWriteNestCounter()
|
||||||
|
{
|
||||||
|
static thread_local int nest_counter = 0;
|
||||||
|
return nest_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Certain platforms (Mac OS on ARM) enforce that a single thread can only have write or
|
||||||
// execute permissions to pages at any given point of time. The two below functions
|
// execute permissions to pages at any given point of time. The two below functions
|
||||||
// are used to toggle between having write permissions or execute permissions.
|
// are used to toggle between having write permissions or execute permissions.
|
||||||
//
|
//
|
||||||
|
@ -66,20 +77,52 @@ void* AllocateExecutableMemory(size_t size)
|
||||||
// PrepareInstructionStreamForJIT();
|
// PrepareInstructionStreamForJIT();
|
||||||
// JITPageWriteDisableExecuteEnable();
|
// JITPageWriteDisableExecuteEnable();
|
||||||
|
|
||||||
//Allows a thread to write to executable memory, but not execute the data.
|
// These functions can be nested, in which case execution will only be enabled
|
||||||
void JITPageWriteEnableExecuteDisable(){
|
// after the call to the JITPageWriteDisableExecuteEnable from the top most
|
||||||
|
// nesting level. Example:
|
||||||
|
|
||||||
|
// [JIT page is in execute mode for the thread]
|
||||||
|
// JITPageWriteEnableExecuteDisable();
|
||||||
|
// [JIT page is in write mode for the thread]
|
||||||
|
// JITPageWriteEnableExecuteDisable();
|
||||||
|
// [JIT page is in write mode for the thread]
|
||||||
|
// JITPageWriteDisableExecuteEnable();
|
||||||
|
// [JIT page is in write mode for the thread]
|
||||||
|
// JITPageWriteDisableExecuteEnable();
|
||||||
|
// [JIT page is in execute mode for the thread]
|
||||||
|
|
||||||
|
// Allows a thread to write to executable memory, but not execute the data.
|
||||||
|
void JITPageWriteEnableExecuteDisable()
|
||||||
|
{
|
||||||
#if defined(_M_ARM_64) && defined(__APPLE__)
|
#if defined(_M_ARM_64) && defined(__APPLE__)
|
||||||
if (__builtin_available(macOS 11.0, *)) {
|
if (JITPageWriteNestCounter() == 0)
|
||||||
pthread_jit_write_protect_np(0);
|
{
|
||||||
|
if (__builtin_available(macOS 11.0, *))
|
||||||
|
{
|
||||||
|
pthread_jit_write_protect_np(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
JITPageWriteNestCounter()++;
|
||||||
}
|
}
|
||||||
//Allows a thread to execute memory allocated for execution, but not write to it.
|
// Allows a thread to execute memory allocated for execution, but not write to it.
|
||||||
void JITPageWriteDisableExecuteEnable(){
|
void JITPageWriteDisableExecuteEnable()
|
||||||
|
{
|
||||||
|
JITPageWriteNestCounter()--;
|
||||||
|
|
||||||
|
// Sanity check the NestCounter to identify underflow
|
||||||
|
// This can indicate the calls to JITPageWriteDisableExecuteEnable()
|
||||||
|
// are not matched with previous calls to JITPageWriteEnableExecuteDisable()
|
||||||
|
if (JITPageWriteNestCounter() < 0)
|
||||||
|
PanicAlertFmt("JITPageWriteNestCounter() underflowed");
|
||||||
|
|
||||||
#if defined(_M_ARM_64) && defined(__APPLE__)
|
#if defined(_M_ARM_64) && defined(__APPLE__)
|
||||||
if (__builtin_available(macOS 11.0, *)) {
|
if (JITPageWriteNestCounter() == 0)
|
||||||
pthread_jit_write_protect_np(1);
|
{
|
||||||
|
if (__builtin_available(macOS 11.0, *))
|
||||||
|
{
|
||||||
|
pthread_jit_write_protect_np(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,9 @@
|
||||||
namespace Common
|
namespace Common
|
||||||
{
|
{
|
||||||
void* AllocateExecutableMemory(size_t size);
|
void* AllocateExecutableMemory(size_t size);
|
||||||
//Allows a thread to write to executable memory, but not execute the data.
|
// Allows a thread to write to executable memory, but not execute the data.
|
||||||
void JITPageWriteEnableExecuteDisable();
|
void JITPageWriteEnableExecuteDisable();
|
||||||
//Allows a thread to execute memory allocated for execution, but not write to it.
|
// Allows a thread to execute memory allocated for execution, but not write to it.
|
||||||
void JITPageWriteDisableExecuteEnable();
|
void JITPageWriteDisableExecuteEnable();
|
||||||
void* AllocateMemoryPages(size_t size);
|
void* AllocateMemoryPages(size_t size);
|
||||||
void FreeMemoryPages(void* ptr, size_t size);
|
void FreeMemoryPages(void* ptr, size_t size);
|
||||||
|
|
|
@ -277,8 +277,7 @@ void DolphinAnalytics::MakeBaseBuilder()
|
||||||
builder.AddData("android-version", s_get_val_func("DEVICE_OS"));
|
builder.AddData("android-version", s_get_val_func("DEVICE_OS"));
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
builder.AddData("os-type", "osx");
|
builder.AddData("os-type", "osx");
|
||||||
//objc_msgSend is only available on x86
|
|
||||||
#ifndef _M_ARM_64
|
|
||||||
// id processInfo = [NSProcessInfo processInfo]
|
// id processInfo = [NSProcessInfo processInfo]
|
||||||
id processInfo = reinterpret_cast<id (*)(Class, SEL)>(objc_msgSend)(
|
id processInfo = reinterpret_cast<id (*)(Class, SEL)>(objc_msgSend)(
|
||||||
objc_getClass("NSProcessInfo"), sel_getUid("processInfo"));
|
objc_getClass("NSProcessInfo"), sel_getUid("processInfo"));
|
||||||
|
@ -290,17 +289,21 @@ void DolphinAnalytics::MakeBaseBuilder()
|
||||||
s64 minor_version; // NSInteger minorVersion
|
s64 minor_version; // NSInteger minorVersion
|
||||||
s64 patch_version; // NSInteger patchVersion
|
s64 patch_version; // NSInteger patchVersion
|
||||||
};
|
};
|
||||||
|
// Under arm64, we need to call objc_msgSend to recieve a struct.
|
||||||
|
// On x86_64, we need to explicitly call objc_msgSend_stret for a struct.
|
||||||
|
#if _M_ARM_64
|
||||||
|
#define msgSend objc_msgSend
|
||||||
|
#else
|
||||||
|
#define msgSend objc_msgSend_stret
|
||||||
|
#endif
|
||||||
// NSOperatingSystemVersion version = [processInfo operatingSystemVersion]
|
// NSOperatingSystemVersion version = [processInfo operatingSystemVersion]
|
||||||
OSVersion version = reinterpret_cast<OSVersion (*)(id, SEL)>(objc_msgSend_stret)(
|
OSVersion version = reinterpret_cast<OSVersion (*)(id, SEL)>(msgSend)(
|
||||||
processInfo, sel_getUid("operatingSystemVersion"));
|
processInfo, sel_getUid("operatingSystemVersion"));
|
||||||
|
#undef msgSend
|
||||||
builder.AddData("osx-ver-major", version.major_version);
|
builder.AddData("osx-ver-major", version.major_version);
|
||||||
builder.AddData("osx-ver-minor", version.minor_version);
|
builder.AddData("osx-ver-minor", version.minor_version);
|
||||||
builder.AddData("osx-ver-bugfix", version.patch_version);
|
builder.AddData("osx-ver-bugfix", version.patch_version);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
builder.AddData("os-type", "linux");
|
builder.AddData("os-type", "linux");
|
||||||
#elif defined(__FreeBSD__)
|
#elif defined(__FreeBSD__)
|
||||||
|
|
|
@ -73,8 +73,8 @@ void JitArm64::Init()
|
||||||
|
|
||||||
bool JitArm64::HandleFault(uintptr_t access_address, SContext* ctx)
|
bool JitArm64::HandleFault(uintptr_t access_address, SContext* ctx)
|
||||||
{
|
{
|
||||||
//Ifdef this since the exception handler runs on a separate thread on Mac OS X (ARM)
|
// Ifdef this since the exception handler runs on a separate thread on macOS (ARM)
|
||||||
#if !defined(__APPLE__) && !defined(_M_ARM_64)
|
#if !(defined(__APPLE__) && defined(_M_ARM_64))
|
||||||
// We can't handle any fault from other threads.
|
// We can't handle any fault from other threads.
|
||||||
if (!Core::IsCPUThread())
|
if (!Core::IsCPUThread())
|
||||||
{
|
{
|
||||||
|
@ -631,7 +631,6 @@ void JitArm64::Jit(u32)
|
||||||
DoJit(em_address, b, nextPC);
|
DoJit(em_address, b, nextPC);
|
||||||
blocks.FinalizeBlock(*b, jo.enableBlocklink, code_block.m_physical_addresses);
|
blocks.FinalizeBlock(*b, jo.enableBlocklink, code_block.m_physical_addresses);
|
||||||
Common::JITPageWriteDisableExecuteEnable();
|
Common::JITPageWriteDisableExecuteEnable();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
void JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
||||||
|
|
|
@ -518,6 +518,22 @@ if(APPLE)
|
||||||
POST_BUILD COMMAND
|
POST_BUILD COMMAND
|
||||||
${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/"
|
${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/"
|
||||||
$<TARGET_FILE:dolphin-emu>)
|
$<TARGET_FILE:dolphin-emu>)
|
||||||
|
|
||||||
|
if(MACOS_CODE_SIGNING)
|
||||||
|
# Code sign make file builds
|
||||||
|
add_custom_command(TARGET dolphin-emu
|
||||||
|
POST_BUILD COMMAND
|
||||||
|
/usr/bin/codesign -f -s "${MACOS_CODE_SIGNING_IDENTITY}" --deep --options runtime --entitlements ${CMAKE_SOURCE_DIR}/Source/Core/DolphinQt/DolphinEmu.entitlements "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Dolphin.app" || true)
|
||||||
|
|
||||||
|
# Code sign builds for build systems that do have release/debug variants (Xcode)
|
||||||
|
add_custom_command(TARGET dolphin-emu
|
||||||
|
POST_BUILD COMMAND
|
||||||
|
/usr/bin/codesign -f -s "${MACOS_CODE_SIGNING_IDENTITY}" --deep --options runtime --entitlements ${CMAKE_SOURCE_DIR}/Source/Core/DolphinQt/DolphinEmu.entitlements "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG}/Dolphin.app" || true)
|
||||||
|
|
||||||
|
add_custom_command(TARGET dolphin-emu
|
||||||
|
POST_BUILD COMMAND
|
||||||
|
/usr/bin/codesign -f -s "${MACOS_CODE_SIGNING_IDENTITY}" --deep --options runtime --entitlements ${CMAKE_SOURCE_DIR}/Source/Core/DolphinQt/DolphinEmu.entitlements "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE}/Dolphin.app" || true)
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
install(TARGETS dolphin-emu RUNTIME DESTINATION ${bindir})
|
install(TARGETS dolphin-emu RUNTIME DESTINATION ${bindir})
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -4,7 +4,14 @@
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.cs.allow-jit</key>
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.cs.disable-library-validation</key>
|
<!-- Needed for GameCube microphone emulation -->
|
||||||
|
<key>com.apple.security.device.audio-input</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<!-- TODO: It is likely this requirement is coming from Qt, but should confirm -->
|
||||||
|
<key>com.apple.security.automation.apple-events</key>
|
||||||
|
<true/>
|
||||||
|
<!-- This is needed to use adhoc signed linked libraries -->
|
||||||
|
<key>com.apple.security.cs.disable-library-validation</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -13,6 +13,8 @@ set(SOURCES
|
||||||
add_executable(MacUpdater ${SOURCES})
|
add_executable(MacUpdater ${SOURCES})
|
||||||
|
|
||||||
set(MacUpdater_NAME "Dolphin Updater")
|
set(MacUpdater_NAME "Dolphin Updater")
|
||||||
|
set(MacUpdater_BIN_DIR ${CMAKE_BINARY_DIR}/Binaries)
|
||||||
|
set(MacUpdater_BUNDLE_PATH ${MacUpdater_BIN_DIR}/${MacUpdater_NAME}.app)
|
||||||
|
|
||||||
set_target_properties(MacUpdater PROPERTIES
|
set_target_properties(MacUpdater PROPERTIES
|
||||||
MACOSX_BUNDLE true
|
MACOSX_BUNDLE true
|
||||||
|
@ -53,8 +55,24 @@ foreach(sb ${STORYBOARDS})
|
||||||
|
|
||||||
add_custom_command(TARGET MacUpdater POST_BUILD
|
add_custom_command(TARGET MacUpdater POST_BUILD
|
||||||
COMMAND ${IBTOOL} --errors --warnings --notices --output-format human-readable-text
|
COMMAND ${IBTOOL} --errors --warnings --notices --output-format human-readable-text
|
||||||
--compile ${MacUpdater_BIN_DIR}/${MacUpdater_NAME}.app/Contents/Resources/${sb}c
|
--compile ${MacUpdater_BUNDLE_PATH}/Contents/Resources/${sb}c
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/${sb}
|
${CMAKE_CURRENT_SOURCE_DIR}/${sb}
|
||||||
COMMENT "Compiling Storyboard ${sb}...")
|
COMMENT "Compiling Storyboard ${sb}...")
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
if(MACOS_CODE_SIGNING)
|
||||||
|
if (MACOS_CODE_SIGNING_IDENTITY_UPDATER STREQUAL "")
|
||||||
|
set(MACOS_CODE_SIGNING_IDENTITY_UPDATER "${MACOS_CODE_SIGNING_IDENTITY}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Make file build code sign
|
||||||
|
add_custom_command(TARGET MacUpdater POST_BUILD
|
||||||
|
COMMAND test ${MacUpdater_BUNDLE_PATH} || /usr/bin/codesign -f -s "${MACOS_CODE_SIGNING_IDENTITY_UPDATER}" --deep --options runtime ${MacUpdater_BUNDLE_PATH})
|
||||||
|
|
||||||
|
# Xcode build code sign
|
||||||
|
add_custom_command(TARGET MacUpdater POST_BUILD
|
||||||
|
COMMAND test "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG}/${MacUpdater_NAME}.app" || /usr/bin/codesign -f -s "${MACOS_CODE_SIGNING_IDENTITY_UPDATER}" --deep --options runtime "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG}/${MacUpdater_NAME}.app")
|
||||||
|
|
||||||
|
add_custom_command(TARGET MacUpdater POST_BUILD
|
||||||
|
COMMAND test "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE}/${MacUpdater_NAME}.app" || /usr/bin/codesign -f -s "${MACOS_CODE_SIGNING_IDENTITY_UPDATER}" --deep --options runtime "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE}/${MacUpdater_NAME}.app")
|
||||||
|
endif()
|
||||||
|
|
Loading…
Reference in New Issue