Fix Qt paths so that code signing works

This commit is contained in:
Raoul Snyman 2018-10-27 10:35:03 -07:00
parent e17dfb2dad
commit 499847bbcd
1 changed files with 105 additions and 9 deletions

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
@ -94,15 +94,15 @@ You may need to install chardet via pip::
"""
import os
import plistlib
import signal
from shutil import copy, copytree
from pathlib import Path
from shutil import copy, copytree, move, rmtree
from macholib.MachO import MachO
from macholib.util import flipwritable, in_system_path
from macholib.util import in_system_path
from builder import Builder
class MacOSXBuilder(Builder):
"""
The :class:`MacosxBuilder` class encapsulates everything that is needed
@ -119,6 +119,99 @@ class MacOSXBuilder(Builder):
dir_size += os.path.getsize(filename)
return dir_size
def _create_symlink(self, folder):
"""
Create the appropriate symlink in the MacOS folder pointing to the Resources folder.
"""
sibling = Path(str(folder).replace('MacOS', ''))
# PyQt5/Qt/qml/QtQml/Models.2
root = str(sibling).partition('Contents')[2].lstrip('/')
# ../../../../
backward = '../' * len(root.split('/'))
# ../../../../Resources/PyQt5/Qt/qml/QtQml/Models.2
good_path = f'{backward}Resources/{root}'
folder.symlink_to(good_path)
def _fix_qt_dll(self, dll):
"""
Fix the DLL lookup paths to use relative ones for Qt dependencies.
Inspiration: PyInstaller/depend/dylib.py:mac_set_relative_dylib_deps()
Currently one header is pointing to (we are in the Resources folder):
@loader_path/../../../../QtCore (it is referencing to the old MacOS folder)
It will be converted to:
@loader_path/../../../../../../MacOS/QtCore
"""
def match_func(pth):
"""
Callback function for MachO.rewriteLoadCommands() that is
called on every lookup path setted in the DLL headers.
By returning None for system libraries, it changes nothing.
Else we return a relative path pointing to the good file
in the MacOS folder.
"""
basename = os.path.basename(pth)
if not basename.startswith('Qt'):
return None
return f'@loader_path{good_path}/{basename}'
# Resources/PyQt5/Qt/qml/QtQuick/Controls.2/Fusion
root = str(dll.parent).partition('Contents')[2][1:]
# /../../../../../../..
backward = '/..' * len(root.split('/'))
# /../../../../../../../MacOS
good_path = f'{backward}/MacOS'
# Rewrite Mach headers with corrected @loader_path
dll = MachO(dll)
dll.rewriteLoadCommands(match_func)
with open(dll.filename, 'rb+') as f:
for header in dll.headers:
f.seek(0)
dll.write(f)
f.seek(0, 2)
f.flush()
def _find_problematic_qt_folders(self, folder):
"""
Recursively yields problematic folders (containing a dot in their name).
"""
for path in folder.iterdir():
if not path.is_dir() or path.is_symlink():
# Skip simlinks as they are allowed (even with a dot)
continue
if '.' in path.name:
yield path
else:
yield from self._find_problematic_qt_folders(path)
def _move_contents_to_resources(self, folder):
"""
Recursively move any non symlink file from a problematic folder to the sibling one in Resources.
"""
for path in folder.iterdir():
if path.is_symlink():
continue
if path.is_dir():
yield from self._move_contents_to_resources(path)
else:
sibling = Path(str(path).replace('MacOS', 'Resources'))
move(path, sibling)
yield sibling
def _fix_qt_paths(self):
"""
Fix the Qt paths
"""
app_path = Path(self.dist_app_path) / 'Contents' / 'MacOS'
for folder in self._find_problematic_qt_folders(app_path):
for problematic_file in self._move_contents_to_resources(folder):
self._fix_qt_dll(problematic_file)
rmtree(folder)
self._create_symlink(folder)
def _relink_mupdf(self, bin_name):
"""
Relink mupdf to bundled libraries
@ -181,7 +274,8 @@ class MacOSXBuilder(Builder):
"""
Copy Info.plist and OpenLP.icns to app bundle.
"""
copy(self.icon_path, os.path.join(self.dist_app_path, 'Contents', 'Resources', os.path.basename(self.icon_path)))
copy(self.icon_path, os.path.join(self.dist_app_path, 'Contents', 'Resources',
os.path.basename(self.icon_path)))
# Add OpenLP version to Info.plist and put it to app bundle.
fr = open(self.bundle_info_path, 'r')
fw = open(os.path.join(self.dist_app_path, 'Contents', os.path.basename(self.bundle_info_path)), 'w')
@ -237,9 +331,10 @@ class MacOSXBuilder(Builder):
os.chdir(os.path.dirname(self.dmg_settings_path))
self._run_command([self.dmgbuild_exe, '-s', self.dmg_settings_path, '-D', 'size={size}M'.format(size=size),
'-D', 'icon={icon_path}'.format(icon_path=self.icon_path),
'-D', 'app={dist_app_path}'.format(dist_app_path=self.dist_app_path), dmg_title, self.dmg_file],
'Unable to run dmgbuild')
'-D', 'icon={icon_path}'.format(icon_path=self.icon_path),
'-D', 'app={dist_app_path}'.format(dist_app_path=self.dist_app_path), dmg_title,
self.dmg_file],
'Unable to run dmgbuild')
# Dmg done.
self._print('Finished creating dmg file, resulting file: %s' % self.dmg_file)
@ -299,6 +394,7 @@ class MacOSXBuilder(Builder):
"""
Build the actual DMG
"""
self._fix_qt_paths()
self._code_sign()
self._create_dmg()