import buildconfig
import errno
import mozfile
import os
import platform
import shutil
import subprocess
from mozbuild.util import ensureParentDir
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'
if not source.endswith('/') and os.path.isdir(source):
source += '/'
subprocess.check_call(['rsync', '-a', '--copy-unsafe-links',
source, dest])
def set_folder_icon(dir, tmpdir):
'Set HFS attributes of dir to use a custom icon'
if not is_linux:
subprocess.check_call(['SetFile', '-a', 'C', dir])
else:
hfs = os.path.join(tmpdir, 'staged.hfs')
subprocess.check_call([
buildconfig.substs['HFS_TOOL'], hfs, 'attr', '/', 'C'])
def generate_hfs_file(stagedir, tmpdir, volume_name):
if is_linux:
hfs = os.path.join(tmpdir, 'staged.hfs')
output = subprocess.check_output(['du', '-s', stagedir])
size = (int(output.split()[0]) / 1000) size = int(size * 1.02) subprocess.check_call(['dd', 'if=/dev/zero', 'of={}'.format(hfs),
'bs=1M', 'count={}'.format(size)])
subprocess.check_call([
buildconfig.substs['MKFSHFS'], '-v', volume_name,
hfs])
def create_app_symlink(stagedir, tmpdir):
if is_linux:
hfs = os.path.join(tmpdir, 'staged.hfs')
subprocess.check_call([
buildconfig.substs['HFS_TOOL'], hfs, 'symlink',
'/ ', '/Applications'])
else:
os.symlink('/Applications', os.path.join(stagedir, ' '))
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:
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:
ensureParentDir(output_dmg)
hfs = os.path.join(tmpdir, 'staged.hfs')
subprocess.check_call([
buildconfig.substs['HFS_TOOL'], hfs, 'addall', stagedir])
subprocess.check_call([
buildconfig.substs['DMG_TOOL'],
'build',
hfs,
output_dmg
],
stdout=open(os.devnull, 'wb'))
def check_tools(*tools):
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):
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', 'MKFSHFS', 'HFS_TOOL')
with mozfile.TemporaryDirectory() as tmpdir:
stagedir = os.path.join(tmpdir, 'stage')
os.mkdir(stagedir)
rsync(source_directory, stagedir)
for source, target in extra_files:
full_target = os.path.join(stagedir, target)
mkdir(os.path.dirname(full_target))
shutil.copyfile(source, full_target)
generate_hfs_file(stagedir, tmpdir, volume_name)
create_app_symlink(stagedir, tmpdir)
set_folder_icon(stagedir, tmpdir)
chmod(stagedir)
create_dmg_from_staged(stagedir, output_dmg, tmpdir, volume_name)
def extract_dmg_contents(dmgfile, destdir):
import buildconfig
if is_linux:
with mozfile.TemporaryDirectory() as tmpdir:
hfs_file = os.path.join(tmpdir, 'firefox.hfs')
subprocess.check_call([
buildconfig.substs['DMG_TOOL'],
'extract',
dmgfile,
hfs_file
],
stdout=open(os.devnull, 'wb'))
subprocess.check_call([
buildconfig.substs['HFS_TOOL'], hfs_file, 'extractall', '/', destdir])
else:
unpack_diskimage = os.path.join(buildconfig.topsrcdir, 'build', 'package',
'mac_osx', 'unpack-diskimage')
unpack_mountpoint = os.path.join(
'/tmp', '{}-unpack'.format(buildconfig.substs['MOZ_APP_NAME']))
subprocess.check_call([unpack_diskimage, dmgfile, unpack_mountpoint,
destdir])
def extract_dmg(dmgfile, output, dsstore=None, icon=None, background=None):
if platform.system() not in ('Darwin', 'Linux'):
raise Exception("Don't know how to extract a DMG on '%s'" % platform.system())
if is_linux:
check_tools('DMG_TOOL', 'MKFSHFS', 'HFS_TOOL')
with mozfile.TemporaryDirectory() as tmpdir:
extract_dmg_contents(dmgfile, tmpdir)
if os.path.islink(os.path.join(tmpdir, ' ')):
os.remove(os.path.join(tmpdir, ' '))
rsync(tmpdir, output)
if dsstore:
mkdir(os.path.dirname(dsstore))
rsync(os.path.join(tmpdir, '.DS_Store'), dsstore)
if background:
mkdir(os.path.dirname(background))
rsync(os.path.join(tmpdir, '.background', os.path.basename(background)),
background)
if icon:
mkdir(os.path.dirname(icon))
rsync(os.path.join(tmpdir, '.VolumeIcon.icns'), icon)