openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #33291
[Merge] lp:~raoul-snyman/openlp/fix-macos-codesign into lp:openlp/packaging
Raoul Snyman has proposed merging lp:~raoul-snyman/openlp/fix-macos-codesign into lp:openlp/packaging.
Commit message:
macOS codesigning fails on Apps with periods in file names. Incorporated fixes from PyInstaller's wiki and also updated the version number to match our new versioning scheme.
Requested reviews:
OpenLP Core (openlp-core)
For more details, see:
https://code.launchpad.net/~raoul-snyman/openlp/fix-macos-codesign/+merge/359972
macOS codesigning fails on Apps with periods in file names. Incorporated fixes from PyInstaller's wiki and also updated the version number to match our new versioning scheme.
--
Your team OpenLP Core is requested to review the proposed merge of lp:~raoul-snyman/openlp/fix-macos-codesign into lp:openlp/packaging.
=== modified file 'builders/builder.py'
--- builders/builder.py 2018-10-27 06:08:24 +0000
+++ builders/builder.py 2018-12-02 06:11:42 +0000
@@ -311,7 +311,7 @@
tag, revision = lines[-1].split()
output = self._bzr('log', self.branch_path, ['--line', '-r', '-1'], 'Error running bzr log')
revision = output.split(':')[0]
- self.version = '{tag}-bzr{revision}'.format(tag=tag, revision=revision)
+ self.version = '{tag}.dev{revision}'.format(tag=tag, revision=revision)
# Write the version to the version file
with open(os.path.join(self.dist_path, '.version'), 'w') as version_file:
version_file.write(str(self.version))
=== modified file 'builders/macosx-builder.py'
--- builders/macosx-builder.py 2016-12-06 20:51:27 +0000
+++ builders/macosx-builder.py 2018-12-02 06:11:42 +0000
@@ -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 @@
"""
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 @@
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 @@
"""
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 @@
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 @@
"""
Build the actual DMG
"""
+ self._fix_qt_paths()
self._code_sign()
self._create_dmg()
Follow ups