Build universal binaries on macOS

This commit is contained in:
Kovid Goyal 2021-12-18 13:51:44 +05:30
parent e5cab43321
commit e195b3a1e2
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 28 additions and 6 deletions

View File

@ -25,7 +25,9 @@ from bypy.constants import (
from bypy.freeze import (
extract_extension_modules, fix_pycryptodome, freeze_python, path_to_freeze_dir
)
from bypy.utils import current_dir, mkdtemp, py_compile, timeit, walk
from bypy.utils import (
current_dir, get_arches_in_binary, mkdtemp, py_compile, timeit, walk
)
abspath, join, basename, dirname = os.path.abspath, os.path.join, os.path.basename, os.path.dirname
iv = globals()['init_env']
@ -35,7 +37,7 @@ py_ver = '.'.join(map(str, python_major_minor_version()))
sign_app = runpy.run_path(join(dirname(abspath(__file__)), 'sign.py'))['sign_app']
QT_PREFIX = join(PREFIX, 'qt')
QT_FRAMEWORKS = [x.replace('5', '') for x in QT_DLLS]
QT_FRAMEWORKS = [x.replace('6', '') for x in QT_DLLS]
ENV = dict(
FONTCONFIG_PATH='@executable_path/../Resources/fonts',
@ -56,7 +58,7 @@ def compile_launcher_lib(contents_dir, gcc, base, pyver, inc_dir):
dest = join(contents_dir, 'Frameworks', 'calibre-launcher.dylib')
src = join(base, 'util.c')
cmd = [gcc] + '-Wall -dynamiclib -std=gnu99'.split() + [src] + \
cmd = [gcc] + '-arch x86_64 -arch arm64 -Wall -dynamiclib -std=gnu99'.split() + [src] + \
['-I' + base] + '-DPY_VERSION_MAJOR={} -DPY_VERSION_MINOR={}'.format(*pyver.split('.')).split() + \
[f'-I{path_to_freeze_dir()}', f'-I{inc_dir}'] + \
[f'-DENV_VARS={env}', f'-DENV_VAR_VALS={env_vals}'] + \
@ -87,7 +89,8 @@ def compile_launchers(contents_dir, inc_dir, xprograms, pyver):
programs.append(out)
is_gui = 'true' if ptype == 'gui' else 'false'
cmd = [
gcc, '-Wall', f'-DPROGRAM=L"{program}"', f'-DMODULE=L"{module}"', f'-DFUNCTION=L"{func}"', f'-DIS_GUI={is_gui}',
gcc, '-Wall', '-arch', 'x86_64', '-arch', 'arm64',
f'-DPROGRAM=L"{program}"', f'-DMODULE=L"{module}"', f'-DFUNCTION=L"{func}"', f'-DIS_GUI={is_gui}',
'-I' + base, src, lib, '-o', out, '-headerpad_max_install_names'
]
# print('\t'+' '.join(cmd))
@ -108,7 +111,14 @@ def flipwritable(fn, mode=None):
return old_mode
def check_universal(path):
arches = get_arches_in_binary(path)
if arches != EXPECTED_ARCHES:
raise SystemExit(f'The file {path} is not a universal binary, it only has arches: {", ".join(arches)}')
STRIPCMD = ['/usr/bin/strip', '-x', '-S', '-']
EXPECTED_ARCHES = {'x86_64', 'arm64'}
def strip_files(files, argv_max=(256 * 1024)):
@ -272,6 +282,7 @@ class Freeze:
@flush
def fix_dependencies_in_lib(self, path_to_lib):
check_universal(path_to_lib)
self.to_strip.append(path_to_lib)
old_mode = flipwritable(path_to_lib)
for dep, bname, is_id in self.get_local_dependencies(path_to_lib):

View File

@ -178,6 +178,9 @@ def get_python_include_paths():
return sorted(frozenset(filter(None, map(gp, sorted(ans)))))
is_macos_universal_build = ismacos and 'universal2' in sysconfig.get_platform()
def init_env(debug=False, sanitize=False):
from setup.build_environment import win_ld, is64bit, win_inc, win_lib, NMAKE, win_cc
linker = None
@ -222,6 +225,9 @@ def init_env(debug=False, sanitize=False):
ldflags += (sysconfig.get_config_var('LINKFORSHARED') or '').split()
if ismacos:
if is_macos_universal_build:
cflags.extend(['-arch', 'x86_64', '-arch', 'arm64'])
ldflags.extend(['-arch', 'x86_64', '-arch', 'arm64'])
cflags.append('-D_OSX')
ldflags.extend('-bundle -undefined dynamic_lookup'.split())
cflags.extend(['-fno-common', '-dynamic'])
@ -443,7 +449,7 @@ class Build(Command):
def check_call(self, *args, **kwargs):
"""print cmdline if an error occurred
If something is missing (qmake e.g.) you get a non-informative error
If something is missing (cmake e.g.) you get a non-informative error
self.check_call(qmc + [ext.name+'.pro'])
so you would have to look at the source to see the actual command.
"""
@ -481,6 +487,8 @@ class Build(Command):
if os.path.exists(bdir):
shutil.rmtree(bdir)
cmd = [CMAKE]
if is_macos_universal_build:
cmd += ['-DCMAKE_OSX_ARCHITECTURES=x86_64;arm64']
if sw and os.path.exists(os.path.join(sw, 'qt')):
cmd += ['-DCMAKE_SYSTEM_PREFIX_PATH=' + os.path.join(sw, 'qt').replace(os.sep, '/')]
os.makedirs(bdir)
@ -491,7 +499,7 @@ class Build(Command):
self.check_call([self.env.make] + ['-j%d'%(cpu_count or 1)])
finally:
os.chdir(cwd)
os.rename(self.j(bdir, 'libheadless.' + ('dylib' if ismacos else 'so')), target)
os.rename(self.j(bdir, 'libheadless.so'), target)
def create_sip_build_skeleton(self, src_dir, ext):
from setup.build_environment import pyqt_sip_abi_version

View File

@ -159,6 +159,7 @@ elif ismacos:
sw = os.environ.get('SW', os.path.expanduser('~/sw'))
sw_inc_dir = os.path.join(sw, 'include')
sw_lib_dir = os.path.join(sw, 'lib')
sw_bin_dir = os.path.join(sw, 'bin')
podofo_inc = os.path.join(sw_inc_dir, 'podofo')
hunspell_inc_dirs = [os.path.join(sw_inc_dir, 'hunspell')]
podofo_lib = sw_lib_dir
@ -167,6 +168,8 @@ elif ismacos:
SSL = os.environ.get('OPENSSL_DIR', os.path.join(sw, 'private', 'ssl'))
openssl_inc_dirs = [os.path.join(SSL, 'include')]
openssl_lib_dirs = [os.path.join(SSL, 'lib')]
if os.path.exists(os.path.join(sw_bin_dir, 'cmake')):
CMAKE = os.path.join(sw_bin_dir, 'cmake')
else:
ft_inc_dirs = pkgconfig_include_dirs('freetype2', 'FT_INC_DIR',
'/usr/include/freetype2')