1
0
mirror of https://gitlab.com/openlp/packaging.git synced 2024-12-22 04:52:50 +00:00

Update the macOS build system to use dmgbuild instead of manually running hdiutil and AppleScript.

bzr-revno: 31
This commit is contained in:
raoul@snyman.info 2016-11-12 13:58:52 +02:00 committed by Raoul Snyman
commit ea2cf8459f
15 changed files with 87 additions and 139 deletions

View File

@ -34,3 +34,4 @@ __pycache__
*.dll
*.DS_Store
config.ini
*.dmg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View File

@ -1,32 +0,0 @@
on run
-- wait for virus scanner
delay 2
tell application "Finder"
tell disk "%(dmg_name)s"
open
set current view of container window to icon view
set toolbar visible of container window to false
set statusbar visible of container window to false
set the bounds of container window to {400, 100, 1100, 500}
set theViewOptions to the icon view options of container window
set arrangement of theViewOptions to not arranged
set icon size of theViewOptions to 128
set background picture of theViewOptions to file ".background:installer-background.png"
set position of item "%(app_name)s" of container window to {160, 200}
set position of item "Applications" of container window to {550, 200}
set position of item ".background" of container window to {100, 500}
set position of item ".DS_Store" of container window to {200, 500}
set position of item ".fseventsd" of container window to {300, 500}
set position of item ".Trashes" of container window to {400, 500}
set position of item ".VolumeIcon.icns" of container window to {500, 500}
delay 5
close
open
update without registering applications
-- wait until the virus scan completes
delay 2
-- eject
end tell
end tell
end run

View File

@ -2,9 +2,7 @@
sphinx = sphinx-build-3.4
pyinstaller = %(projects)s/../pyinstaller/pyinstaller.py
lrelease = /opt/local/libexec/qt5/bin/lrelease
diskutil = diskutil
hdiutil = hdiutil
osascript = osascript
dmgbuild = dmgbuild
mudrawbin = mudraw
mutoolbin = mutool
@ -12,10 +10,9 @@ mutoolbin = mutool
branch = %(projects)s/trunk
documentation = %(projects)s/documentation
app_icon = %(here)s/OpenLP.icns
dmg_icon = %(here)s/openlp-dmg.icns
bundle_info = %(here)s/Info.plist
hooks = %(here)s/../pyinstaller-hooks
dmg_background = %(here)s/DmgImageInstallBackground.png
dmg_settings = %(here)s/dmg-settings.py
[transifex]
username =

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
osx/dmg-background-new.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
osx/dmg-background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

27
osx/dmg-settings-new.py Normal file
View File

@ -0,0 +1,27 @@
import os
# This is the settings file for building the DMG. Run dmgbuild like so:
# $ dmgbuild -s dmg-settings.py -D size=<size>,app=<path/to/OpenLP.app> "OpenLP" OpenLP-{version}.dmg
HERE = os.getcwd()
format = 'UDBZ'
size = '600M'
files = [defines.get('app', '/Applications/OpenLP.app')]
symlinks = { 'Applications': '/Applications' }
badge_icon = os.path.join(HERE, 'openlp-logo-new.icns')
icon_locations = {
'OpenLP.app': (160, 200),
'Applications': (550, 200)
}
background = os.path.join(HERE, 'dmg-background-new.png')
window_rect = ((100, 100), (700, 457))
default_view = 'icon-view'
show_icon_preview = False
arrange_by = None
scroll_position = (0, 0)
grid_offset = (0, 0)
grid_spacing = 100
label_pos = 'bottom' # or 'right'
text_size = 16
icon_size = 128

27
osx/dmg-settings.py Normal file
View File

@ -0,0 +1,27 @@
import os
# This is the settings file for building the DMG. Run dmgbuild like so:
# $ dmgbuild -s dmg-settings.py -D size=<size>,app=<path/to/OpenLP.app> "OpenLP" OpenLP-{version}.dmg
HERE = os.getcwd()
format = 'UDZO'
size = defines.get('size', '600M')
files = [defines.get('app', '/Applications/OpenLP.app')]
symlinks = { 'Applications': '/Applications' }
badge_icon = defines.get('icon', 'OpenLP.icns')
icon_locations = {
'OpenLP.app': (160, 200),
'Applications': (550, 200)
}
background = os.path.join(HERE, 'dmg-background.png')
window_rect = ((100, 100), (700, 460))
default_view = 'icon-view'
show_icon_preview = False
arrange_by = None
scroll_position = (0, 0)
grid_offset = (0, 0)
grid_spacing = 100
label_pos = 'bottom' # or 'right'
text_size = 16
icon_size = 128

View File

@ -27,9 +27,9 @@ Mac OS X Build Script
This script is used to build the Mac OS X app bundle and pack it into dmg file.
For this script to work out of the box, it depends on a number of things:
Python 3.3/3.4
Python 3.4
PyQt4
PyQt5
You should already have this installed, OpenLP doesn't work without it. The
version the script expects is the packaged one available from River Bank
Computing.
@ -111,6 +111,8 @@ def _which(command):
"""
Return absolute path to a command found on system PATH.
"""
if command.startswith('/'):
return command
for path in os.environ["PATH"].split(os.pathsep):
if os.access(os.path.join(path, command), os.X_OK):
return "%s/%s" % (path, command)
@ -237,11 +239,12 @@ class MacosxBuilder(object):
self.sphinx = _which(self.config.get('executables', 'sphinx'))
self.pyinstaller = os.path.abspath(self.config.get('executables', 'pyinstaller'))
self.lrelease = self.config.get('executables', 'lrelease')
self.diskutil = _which(self.config.get('executables', 'diskutil'))
self.hdiutil = self.config.get('executables', 'hdiutil')
self.osascript = _which(self.config.get('executables', 'osascript'))
self.dmgbuild = _which(self.config.get('executables', 'dmgbuild'))
self.mudraw_bin = _which(self.config.get('executables', 'mudrawbin'))
self.mutool_bin = _which(self.config.get('executables', 'mutoolbin'))
if self.mutool_bin:
self.mutool_lib = os.path.abspath(
os.path.join(os.path.dirname(self.mutool_bin), '..', 'lib', 'libjbig2dec.0.dylib'))
def setup_paths(self):
"""
@ -264,9 +267,7 @@ class MacosxBuilder(object):
self.openlp_script = os.path.abspath(os.path.join(self.work_path, 'openlp.py'))
self.hooks_path = os.path.abspath(os.path.join(self.work_path, self.config.get('paths', 'hooks')))
self.app_icon = os.path.abspath(self.config.get('paths', 'app_icon'))
self.dmg_icon = os.path.abspath(self.config.get('paths', 'dmg_icon'))
self.bundle_info = os.path.abspath(self.config.get('paths', 'bundle_info'))
self.dmg_background_img = os.path.abspath(self.config.get('paths', 'dmg_background'))
self.i18n_utils = os.path.join(self.work_path, 'scripts', 'translation_utils.py')
self.source_path = os.path.join(self.work_path, 'openlp')
self.manual_path = os.path.join(self.docs_path, 'manual')
@ -275,12 +276,13 @@ class MacosxBuilder(object):
self.build_path = os.path.join(self.work_path, 'build')
self.dist_app_path = os.path.join(self.work_path, 'dist', 'OpenLP.app')
self.dist_path = os.path.join(self.work_path, 'dist', 'OpenLP.app', 'Contents', 'MacOS')
self.dmg_settings = os.path.abspath(self.config.get('paths', 'dmg_settings'))
# Path to Qt translation files.
from PyQt5.QtCore import QCoreApplication
qt_plug_dir = str(list(QCoreApplication.libraryPaths())[0])
self.qt_translat_path = os.path.join(os.path.dirname(qt_plug_dir), 'translations')
self.qt_translations_path = os.path.join(os.path.dirname(qt_plug_dir), 'translations')
def update_code(self):
"""
@ -453,11 +455,12 @@ class MacosxBuilder(object):
copy(os.path.join(self.script_path, 'LICENSE.txt'), os.path.join(self.dist_path, 'LICENSE.txt'))
self._print_verbose('... mudraw')
if self.mudraw_bin and os.path.isfile(self.mudraw_bin):
copy(os.path.join(self.mudraw_bin), os.path.join(self.dist_path, 'mudraw'))
copy(self.mudraw_bin, os.path.join(self.dist_path, 'mudraw'))
self.relink_mudraw()
elif self.mutool_bin and os.path.isfile(self.mutool_bin):
copy(os.path.join(self.mutool_bin), os.path.join(self.dist_path, 'mutool'))
copy(self.mutool_bin, os.path.join(self.dist_path, 'mutool'))
self.relink_mutool()
copy(self.mutool_lib, os.path.join(self.dist_path, 'libjbig2dec.0.dylib'))
else:
self._print('... WARNING: mudraw and mutool not found')
@ -564,10 +567,10 @@ class MacosxBuilder(object):
if code != 0:
raise Exception('Error running lconvert on %s' % source_path)
self._print('Copying qm files...')
source = self.qt_translat_path
source = self.qt_translations_path
files = os.listdir(source)
for filename in files:
if filename.startswith('qt_') and filename.endswith('.qm') and len(filename) == 8:
if filename.startswith('qt_') and filename.endswith('.qm'):
self._print_verbose('... %s', filename)
copy(os.path.join(source, filename), os.path.join(self.dist_path, 'i18n', filename))
@ -625,86 +628,26 @@ class MacosxBuilder(object):
# Release version does not contain revision in .dmg name.
if self.args.devel:
dmg_name = 'OpenLP-' + str(self.version_string) + '.dmg'
dmg_title = 'OpenLP {version}'.format(version=self.version_string)
else:
dmg_name = 'OpenLP-' + str(self.version_tag) + '.dmg'
dmg_title = 'OpenLP {version}'.format(version=self.version_tag)
dmg_file = os.path.join(self.work_path, 'build', dmg_name)
self.dmg_file = os.path.join(self.work_path, 'dist', dmg_name)
# Remove dmg if it exists.
if os.path.exists(dmg_file):
os.remove(dmg_file)
if os.path.exists(self.dmg_file):
os.remove(self.dmg_file)
# Create empty dmg file.
size = self._get_directory_size(self.dist_app_path) # in bytes.
size = size / (1000 * 1000) # Convert to megabytes.
size += 10 # Additional space in .dmg for other files.
self._print('... dmg disk size: %s' % size)
self._run_command([self.hdiutil, 'create', dmg_file, '-ov', '-megabytes', str(size), '-fs', 'HFS+', '-volname',
'OpenLP'], 'Could not create dmg file.')
# Mount empty dmg file.
old_mounts = self._get_mountpoints()
self._print('... mounting the dmg file: %s' % dmg_file)
self._run_command([self.hdiutil, 'attach', dmg_file], 'Could not mount dmg file, cannot continue.')
new_mounts = self._get_mountpoints()
# Get the mount point from difference between paths
# after mounting and before mounting the dmg file.
dmg_volume_path = list(set(new_mounts) - set(old_mounts))[0]
# Copy OpenLP.app and other files to .dmg
# TODO more reliable way to determine dmg_volume_path
self._print('... Copying the app to the dmg: ' + dmg_volume_path)
self._run_command(['cp', '-R', self.dist_app_path, dmg_volume_path],
'Could not copy app bundle, dmg creation failed.')
# Set icon for dmg file.
# http://endrift.com/blog/2010/06/14/dmg-files-volume-icons-cli/
self._print('... Setting the dmg icon.')
dmg_icon = os.path.join(dmg_volume_path, '.VolumeIcon.icns')
self._run_command(['cp', self.dmg_icon, dmg_icon], 'Could not copy the dmg icon file, dmg creation failed.')
# Set proper dmg icon attributes.
self._run_command(['SetFile', '-c', 'icnC', dmg_icon], 'Could not set dmg icon attributes.')
# Ensures dmg icon will be used while mounted.
self._run_command(['SetFile', '-a', 'C', dmg_volume_path], 'Could not set dmg icon attributes.')
# Create symlink in dmg pointing to the /Applications directory on OS X.
self._print('... Creating symlink to /Applications.')
os.symlink('/Applications', os.path.join(dmg_volume_path, 'Applications'))
# Set dmg background. Requires running Mac OS X gui.
# TODO: better formatting and code refactoring
if not self.args.devel:
self._print('... Setting the background image.')
os.mkdir(os.path.join(dmg_volume_path, '.background'))
self._run_command(['cp', self.dmg_background_img, os.path.join(dmg_volume_path,
'.background/installer-background.png')],
'Could not copy the background image, dmg creation failed.')
self.adjust_dmg_view(os.path.basename(dmg_volume_path))
# Unmount dmg file.
self._print('... unmounting the dmg.')
# Sometimes it could happen that OSX Finder is blocking umount.
# We need to find this process and kill it.
try:
output = subprocess.check_output(['fuser', dmg_volume_path]).strip()
if output:
blocking_proc_pid = int(output.split()[0])
os.kill(int(blocking_proc_pid), signal.SIGKILL)
except Exception as e:
print(str(e))
self._print('... failed to kill process using %s' % dmg_volume_path)
# Unmount dmg file.
self._run_command([self.hdiutil, 'detach', dmg_volume_path],
'Could not unmount the dmg file, dmg creation failed.')
# Compress dmg file.
self._print('... compressing the dmg file')
compressed_dmg = os.path.join(self.work_path, 'dist', os.path.basename(dmg_file)) # Put dmg to 'dist' dir.
# Remove dmg if it exists.
if os.path.exists(compressed_dmg):
os.remove(compressed_dmg)
self._run_command([self.hdiutil, 'convert', dmg_file, '-format', 'UDZO', '-imagekey', 'zlib-level=9', '-o',
compressed_dmg], 'Could not compress the dmg file, dmg creation failed.')
self._print('... %s' % self.script_path)
os.chdir(self.script_path)
self._run_command([self.dmgbuild, '-s', self.dmg_settings, '-D', 'size={size}M'.format(size=size),
'-D', 'icon={icon_path}'.format(icon_path=self.app_icon),
'-D', 'app={dist_app_path}'.format(dist_app_path=self.dist_app_path), dmg_title, self.dmg_file],
'Unable to run dmgbuild')
# Jenkins integration.
# Continuous integration server needs to know the filename of dmg.
@ -713,40 +656,25 @@ class MacosxBuilder(object):
fpath = os.path.join(self.branch_path, 'openlp.properties')
self._print('... writing property file for jenkins: %s' % fpath)
f = open(fpath, 'w')
f.write('OPENLP_DMGNAME=' + os.path.basename(dmg_file) + '\n')
f.write('OPENLP_DMGNAME=' + os.path.basename(self.dmg_file) + '\n')
f.close()
# Dmg done.
self._print('Finished creating dmg file, resulting file: %s' % compressed_dmg)
self.dmg_file = compressed_dmg
def adjust_dmg_view(self, dmg_volume_name):
try:
master_script = os.path.join(self.script_path, 'applescript-adjust-dmg-view.master')
apple_script = os.path.join(self.script_path, 'adjust-dmg-view.applescript')
with open(master_script) as r, open(apple_script, 'w') as w:
apple_script = r.read() % {'dmg_name': dmg_volume_name, 'app_name': 'OpenLP.app'}
w.write(apple_script)
p = Popen([self.osascript, apple_script])
result = p.returncode
if (result != 0):
self._print('Adjusting dmg view failed (non-zero exit code).')
except (IOError, OSError):
self._print('Adjusting dmg view failed.')
self._print('Finished creating dmg file, resulting file: %s' % self.dmg_file)
def main(self):
"""
The main function to run the Mac OS X builder.
"""
self._print_verbose('OpenLP main script: ......%s', self.openlp_script)
self._print_verbose('Script path: .............%s', os.path.split(os.path.abspath(__file__))[0])
self._print_verbose('Script path: .............%s', self.script_path)
self._print_verbose('Branch path: .............%s', self.branch_path)
self._print_verbose('Source path: .............%s', self.source_path)
self._print_verbose('"dist.app" path: .........%s', self.dist_app_path)
self._print_verbose('"dist" path: .............%s', self.dist_path)
self._print_verbose('"hooks" path: ............%s', self.hooks_path)
self._print_verbose('PyInstaller: .............%s', self.pyinstaller)
self._print_verbose('dmgbuild: ................%s', self.dmgbuild)
self._print_verbose('Documentation branch path:%s', self.docs_path)
if self.mudraw_bin:
self._print_verbose('mudraw binary ............%s', self.mudraw_bin)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.