diff options
Diffstat (limited to 'python/mozbuild/mozpack/dmg.py')
-rw-r--r-- | python/mozbuild/mozpack/dmg.py | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/python/mozbuild/mozpack/dmg.py b/python/mozbuild/mozpack/dmg.py new file mode 100644 index 000000000..036302214 --- /dev/null +++ b/python/mozbuild/mozpack/dmg.py @@ -0,0 +1,121 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import errno +import mozfile +import os +import platform +import shutil +import subprocess + +is_linux = platform.system() == 'Linux' + +def mkdir(dir): + if not os.path.isdir(dir): + try: + os.makedirs(dir) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + +def chmod(dir): + 'Set permissions of DMG contents correctly' + subprocess.check_call(['chmod', '-R', 'a+rX,a-st,u+w,go-w', dir]) + + +def rsync(source, dest): + 'rsync the contents of directory source into directory dest' + # Ensure a trailing slash so rsync copies the *contents* of source. + if not source.endswith('/'): + source += '/' + subprocess.check_call(['rsync', '-a', '--copy-unsafe-links', + source, dest]) + + +def set_folder_icon(dir): + 'Set HFS attributes of dir to use a custom icon' + if not is_linux: + #TODO: bug 1197325 - figure out how to support this on Linux + subprocess.check_call(['SetFile', '-a', 'C', dir]) + + +def create_dmg_from_staged(stagedir, output_dmg, tmpdir, volume_name): + 'Given a prepared directory stagedir, produce a DMG at output_dmg.' + if not is_linux: + # Running on OS X + hybrid = os.path.join(tmpdir, 'hybrid.dmg') + subprocess.check_call(['hdiutil', 'makehybrid', '-hfs', + '-hfs-volume-name', volume_name, + '-hfs-openfolder', stagedir, + '-ov', stagedir, + '-o', hybrid]) + subprocess.check_call(['hdiutil', 'convert', '-format', 'UDBZ', + '-imagekey', 'bzip2-level=9', + '-ov', hybrid, '-o', output_dmg]) + else: + import buildconfig + uncompressed = os.path.join(tmpdir, 'uncompressed.dmg') + subprocess.check_call([ + buildconfig.substs['GENISOIMAGE'], + '-V', volume_name, + '-D', '-R', '-apple', '-no-pad', + '-o', uncompressed, + stagedir + ]) + subprocess.check_call([ + buildconfig.substs['DMG_TOOL'], + 'dmg', + uncompressed, + output_dmg + ], + # dmg is seriously chatty + stdout=open(os.devnull, 'wb')) + +def check_tools(*tools): + ''' + Check that each tool named in tools exists in SUBSTS and is executable. + ''' + import buildconfig + for tool in tools: + path = buildconfig.substs[tool] + if not path: + raise Exception('Required tool "%s" not found' % tool) + if not os.path.isfile(path): + raise Exception('Required tool "%s" not found at path "%s"' % (tool, path)) + if not os.access(path, os.X_OK): + raise Exception('Required tool "%s" at path "%s" is not executable' % (tool, path)) + + +def create_dmg(source_directory, output_dmg, volume_name, extra_files): + ''' + Create a DMG disk image at the path output_dmg from source_directory. + + Use volume_name as the disk image volume name, and + use extra_files as a list of tuples of (filename, relative path) to copy + into the disk image. + ''' + if platform.system() not in ('Darwin', 'Linux'): + raise Exception("Don't know how to build a DMG on '%s'" % platform.system()) + + if is_linux: + check_tools('DMG_TOOL', 'GENISOIMAGE') + with mozfile.TemporaryDirectory() as tmpdir: + stagedir = os.path.join(tmpdir, 'stage') + os.mkdir(stagedir) + # Copy the app bundle over using rsync + rsync(source_directory, stagedir) + # Copy extra files + for source, target in extra_files: + full_target = os.path.join(stagedir, target) + mkdir(os.path.dirname(full_target)) + shutil.copyfile(source, full_target) + # Make a symlink to /Applications. The symlink name is a space + # so we don't have to localize it. The Applications folder icon + # will be shown in Finder, which should be clear enough for users. + os.symlink('/Applications', os.path.join(stagedir, ' ')) + # Set the folder attributes to use a custom icon + set_folder_icon(stagedir) + chmod(stagedir) + create_dmg_from_staged(stagedir, output_dmg, tmpdir, volume_name) |