Merge lp:~raoul-snyman/openlp/fix-macos-codesign into lp:openlp/packaging

Proposed by Raoul Snyman
Status: Merged
Merged at revision: 40
Proposed branch: lp:~raoul-snyman/openlp/fix-macos-codesign
Merge into: lp:openlp/packaging
Diff against target: 174 lines (+106/-10)
2 files modified
builders/builder.py (+1/-1)
builders/macosx-builder.py (+105/-9)
To merge this branch: bzr merge lp:~raoul-snyman/openlp/fix-macos-codesign
Reviewer Review Type Date Requested Status
Tim Bentley Approve
Review via email: mp+359972@code.launchpad.net

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.

Description of the change

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.

To post a comment you must log in.
Revision history for this message
Tim Bentley (trb143) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'builders/builder.py'
2--- builders/builder.py 2018-10-27 06:08:24 +0000
3+++ builders/builder.py 2018-12-02 06:11:42 +0000
4@@ -311,7 +311,7 @@
5 tag, revision = lines[-1].split()
6 output = self._bzr('log', self.branch_path, ['--line', '-r', '-1'], 'Error running bzr log')
7 revision = output.split(':')[0]
8- self.version = '{tag}-bzr{revision}'.format(tag=tag, revision=revision)
9+ self.version = '{tag}.dev{revision}'.format(tag=tag, revision=revision)
10 # Write the version to the version file
11 with open(os.path.join(self.dist_path, '.version'), 'w') as version_file:
12 version_file.write(str(self.version))
13
14=== modified file 'builders/macosx-builder.py'
15--- builders/macosx-builder.py 2016-12-06 20:51:27 +0000
16+++ builders/macosx-builder.py 2018-12-02 06:11:42 +0000
17@@ -1,5 +1,5 @@
18 # -*- coding: utf-8 -*-
19-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
20+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
21
22 ###############################################################################
23 # OpenLP - Open Source Lyrics Projection #
24@@ -94,15 +94,15 @@
25 """
26
27 import os
28-import plistlib
29-import signal
30-from shutil import copy, copytree
31+from pathlib import Path
32+from shutil import copy, copytree, move, rmtree
33
34 from macholib.MachO import MachO
35-from macholib.util import flipwritable, in_system_path
36+from macholib.util import in_system_path
37
38 from builder import Builder
39
40+
41 class MacOSXBuilder(Builder):
42 """
43 The :class:`MacosxBuilder` class encapsulates everything that is needed
44@@ -119,6 +119,99 @@
45 dir_size += os.path.getsize(filename)
46 return dir_size
47
48+ def _create_symlink(self, folder):
49+ """
50+ Create the appropriate symlink in the MacOS folder pointing to the Resources folder.
51+ """
52+ sibling = Path(str(folder).replace('MacOS', ''))
53+
54+ # PyQt5/Qt/qml/QtQml/Models.2
55+ root = str(sibling).partition('Contents')[2].lstrip('/')
56+ # ../../../../
57+ backward = '../' * len(root.split('/'))
58+ # ../../../../Resources/PyQt5/Qt/qml/QtQml/Models.2
59+ good_path = f'{backward}Resources/{root}'
60+
61+ folder.symlink_to(good_path)
62+
63+ def _fix_qt_dll(self, dll):
64+ """
65+ Fix the DLL lookup paths to use relative ones for Qt dependencies.
66+ Inspiration: PyInstaller/depend/dylib.py:mac_set_relative_dylib_deps()
67+ Currently one header is pointing to (we are in the Resources folder):
68+ @loader_path/../../../../QtCore (it is referencing to the old MacOS folder)
69+ It will be converted to:
70+ @loader_path/../../../../../../MacOS/QtCore
71+ """
72+
73+ def match_func(pth):
74+ """
75+ Callback function for MachO.rewriteLoadCommands() that is
76+ called on every lookup path setted in the DLL headers.
77+ By returning None for system libraries, it changes nothing.
78+ Else we return a relative path pointing to the good file
79+ in the MacOS folder.
80+ """
81+ basename = os.path.basename(pth)
82+ if not basename.startswith('Qt'):
83+ return None
84+ return f'@loader_path{good_path}/{basename}'
85+
86+ # Resources/PyQt5/Qt/qml/QtQuick/Controls.2/Fusion
87+ root = str(dll.parent).partition('Contents')[2][1:]
88+ # /../../../../../../..
89+ backward = '/..' * len(root.split('/'))
90+ # /../../../../../../../MacOS
91+ good_path = f'{backward}/MacOS'
92+
93+ # Rewrite Mach headers with corrected @loader_path
94+ dll = MachO(dll)
95+ dll.rewriteLoadCommands(match_func)
96+ with open(dll.filename, 'rb+') as f:
97+ for header in dll.headers:
98+ f.seek(0)
99+ dll.write(f)
100+ f.seek(0, 2)
101+ f.flush()
102+
103+ def _find_problematic_qt_folders(self, folder):
104+ """
105+ Recursively yields problematic folders (containing a dot in their name).
106+ """
107+ for path in folder.iterdir():
108+ if not path.is_dir() or path.is_symlink():
109+ # Skip simlinks as they are allowed (even with a dot)
110+ continue
111+ if '.' in path.name:
112+ yield path
113+ else:
114+ yield from self._find_problematic_qt_folders(path)
115+
116+ def _move_contents_to_resources(self, folder):
117+ """
118+ Recursively move any non symlink file from a problematic folder to the sibling one in Resources.
119+ """
120+ for path in folder.iterdir():
121+ if path.is_symlink():
122+ continue
123+ if path.is_dir():
124+ yield from self._move_contents_to_resources(path)
125+ else:
126+ sibling = Path(str(path).replace('MacOS', 'Resources'))
127+ move(path, sibling)
128+ yield sibling
129+
130+ def _fix_qt_paths(self):
131+ """
132+ Fix the Qt paths
133+ """
134+ app_path = Path(self.dist_app_path) / 'Contents' / 'MacOS'
135+ for folder in self._find_problematic_qt_folders(app_path):
136+ for problematic_file in self._move_contents_to_resources(folder):
137+ self._fix_qt_dll(problematic_file)
138+ rmtree(folder)
139+ self._create_symlink(folder)
140+
141 def _relink_mupdf(self, bin_name):
142 """
143 Relink mupdf to bundled libraries
144@@ -181,7 +274,8 @@
145 """
146 Copy Info.plist and OpenLP.icns to app bundle.
147 """
148- copy(self.icon_path, os.path.join(self.dist_app_path, 'Contents', 'Resources', os.path.basename(self.icon_path)))
149+ copy(self.icon_path, os.path.join(self.dist_app_path, 'Contents', 'Resources',
150+ os.path.basename(self.icon_path)))
151 # Add OpenLP version to Info.plist and put it to app bundle.
152 fr = open(self.bundle_info_path, 'r')
153 fw = open(os.path.join(self.dist_app_path, 'Contents', os.path.basename(self.bundle_info_path)), 'w')
154@@ -237,9 +331,10 @@
155
156 os.chdir(os.path.dirname(self.dmg_settings_path))
157 self._run_command([self.dmgbuild_exe, '-s', self.dmg_settings_path, '-D', 'size={size}M'.format(size=size),
158- '-D', 'icon={icon_path}'.format(icon_path=self.icon_path),
159- '-D', 'app={dist_app_path}'.format(dist_app_path=self.dist_app_path), dmg_title, self.dmg_file],
160- 'Unable to run dmgbuild')
161+ '-D', 'icon={icon_path}'.format(icon_path=self.icon_path),
162+ '-D', 'app={dist_app_path}'.format(dist_app_path=self.dist_app_path), dmg_title,
163+ self.dmg_file],
164+ 'Unable to run dmgbuild')
165
166 # Dmg done.
167 self._print('Finished creating dmg file, resulting file: %s' % self.dmg_file)
168@@ -299,6 +394,7 @@
169 """
170 Build the actual DMG
171 """
172+ self._fix_qt_paths()
173 self._code_sign()
174 self._create_dmg()
175

Subscribers

People subscribed via source and target branches