This commit is contained in:
Tomas Groth 2019-02-15 21:19:31 +01:00
commit eb853ebff9
5 changed files with 129 additions and 47 deletions

View File

@ -160,6 +160,8 @@ class Builder(object):
parser.add_argument('--skip-translations', action='store_true', default=False,
help='Do NOT update the language translation files')
parser.add_argument('--debug', action='store_true', default=False, help='Create a debug build')
parser.add_argument('--tag-override', metavar='<tag>-bzr<revision>', default=None,
help='Override tag and revision, should be in format <tag>-bzr<revision>')
self.add_extra_args(parser)
self.args = parser.parse_args()
@ -216,7 +218,7 @@ class Builder(object):
else:
self.version = None
self.work_path = self.branch_path
self.openlp_script = os.path.abspath(os.path.join(self.work_path, 'openlp-run.py'))
self.openlp_script = os.path.abspath(os.path.join(self.work_path, 'run_openlp.py'))
self.source_path = os.path.join(self.work_path, 'openlp')
self.manual_path = os.path.join(self.documentation_path, 'manual')
self.manual_build_path = os.path.join(self.manual_path, 'build')
@ -273,7 +275,6 @@ class Builder(object):
Run PyInstaller on the branch to build an executable.
"""
self._print('Running PyInstaller...')
copy(os.path.join(self.work_path, 'openlp.py'), self.openlp_script)
os.chdir(self.work_path)
cmd = [self.python,
self.pyinstaller_exe,
@ -304,17 +305,20 @@ class Builder(object):
"""
self._print('Writing version file...')
if not self.args.release:
# This is a development build, get the tag and revision
output = self._bzr('tags', self.branch_path, err_msg='Error running bzr tags')
lines = output.splitlines()
if len(lines) == 0:
tag = '0.0.0'
revision = '0'
if self.args.tag_override:
self.version = self.args.tag_override
else:
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)
# This is a development build, get the tag and revision
output = self._bzr('tags', self.branch_path, err_msg='Error running bzr tags')
lines = output.splitlines()
if len(lines) == 0:
tag = '0.0.0'
revision = '0'
else:
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}.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))
@ -430,7 +434,8 @@ class Builder(object):
rmtree(self.manual_build_path)
os.chdir(self.manual_path)
sphinx_build = self.get_sphinx_build()
command = [self.sphinx_exe, '-b', sphinx_build, '-d', 'build/doctrees', 'source', 'build/{}'.format(sphinx_build)]
command = [self.sphinx_exe, '-b', sphinx_build, '-d', 'build/doctrees', 'source',
'build/{}'.format(sphinx_build)]
self._run_command(command, 'Error running Sphinx')
self.after_run_sphinx()
@ -479,5 +484,3 @@ class Builder(object):
self._print('Done.')
raise SystemExit()

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()

View File

@ -26,7 +26,7 @@ Windows Build Script
This script is used to build the Windows binary and the accompanying installer.
For this script to work out of the box, it depends on a number of things:
Python 3.6
Python 3.7
PyQt5
You should already have this installed, OpenLP doesn't work without it. The
@ -57,10 +57,6 @@ OpenLP
shared repository directory. This means your code should be in a directory
structure like this: "openlp\\branch-name".
Visual C++ 2008 Express Edition
This is to build pptviewlib.dll, the library for controlling the
PowerPointViewer.
windows-builder.py
This script, of course. It should be in the "windows-installer" directory
at the same level as OpenLP trunk.
@ -125,17 +121,6 @@ class WindowsBuilder(Builder):
The :class:`WindowsBuilder` class encapsulates everything that is needed
to build a Windows installer.
"""
def _build_pptviewlib(self):
"""
Build the PowerPoint Viewer DLL using Visual Studio.
"""
self._print('Building PPTVIEWLIB.DLL...')
if not os.path.exists(self.vcbuild_exe):
self._print('... WARNING: vcbuild.exe was not found, skipping building pptviewlib.dll')
return
self._run_command([self.vcbuild_exe, '/rebuild', os.path.join(self.pptviewlib_path, 'pptviewlib.vcproj'),
'Release|Win32'], 'Error building pptviewlib.dll')
copy(os.path.join(self.pptviewlib_path, 'Release', 'pptviewlib.dll'), self.pptviewlib_path)
def _create_innosetup_file(self):
"""
@ -345,7 +330,7 @@ class WindowsBuilder(Builder):
"""
parameters = []
# Finds the UCRT DDLs available from the Windows 10 SDK
for binary in glob.glob('C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\ucrt\\DLLs\\x86\\*.dll'):
for binary in glob.glob('C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\ucrt\\DLLs\\x64\\*.dll'):
parameters.append('--add-binary')
parameters.append(binary + ";.")
return parameters

View File

@ -2,17 +2,16 @@
innosetup = %(progfiles)s\Inno Setup 5\ISCC.exe
sphinx = %(pyroot)s\Scripts\sphinx-build.exe
pyinstaller = %(pyroot)s\Scripts\pyinstaller-script.py
vcbuild = %(progfiles)s\Microsoft Visual Studio 9.0\VC\vcpackages\vcbuild.exe
htmlhelp = %(progfiles)s\HTML Help Workshop\hhc.exe
psvince = %(here)s\psvince.dll
lrelease = C:\Qt\5.9\msvc2015\bin\lrelease.exe
lrelease = C:\Qt\5.12\msvc2017\bin\lrelease.exe
portablelauncher = %(here)s\..\..\PortableApps.comLauncher\PortableApps.comLauncherGenerator.exe
portableinstaller = %(here)s\..\..\PortableApps.comInstaller\PortableApps.comInstaller.exe
mutool = %(here)s\..\..\mupdf-1.9a-windows\mutool.exe
mediainfo = %(here)s\..\..\MediaInfo\MediaInfo.exe
mutool = %(project)s\openlp-branch\mutool.exe
mediainfo = %(project)s\openlp-branch\MediaInfo.exe
[paths]
branch = %(projects)s\trunk
branch = %(projects)s\openlp-branch
documentation = %(projects)s\documentation
icon = %(here)s\OpenLP.ico
hooks = %(here)s\..\pyinstaller-hooks

View File

@ -2,7 +2,6 @@
innosetup = %(progfiles)s\Inno Setup 5\ISCC.exe
sphinx = %(pyroot)s\Scripts\sphinx-build.exe
pyinstaller = %(here)s\..\pyinstaller\pyinstaller.py
vcbuild = %(progfiles)s\Microsoft Visual Studio 9.0\VC\vcpackages\vcbuild.exe
htmlhelp = %(progfiles)s\HTML Help Workshop\hhc.exe
psvince = %(here)s\psvince.dll
lrelease = %(sitepackages)s\PyQt5\bin\lrelease.exe