diff --git a/python/mozbuild/mozbuild/action/unpack_dmg.py b/python/mozbuild/mozbuild/action/unpack_dmg.py new file mode 100644 index 000000000000..5f66d98cf940 --- /dev/null +++ b/python/mozbuild/mozbuild/action/unpack_dmg.py @@ -0,0 +1,35 @@ +# 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/. + +from __future__ import print_function + +from mozpack import dmg + +import argparse +import sys + + +def main(args): + parser = argparse.ArgumentParser( + description='Explode a DMG into its relevant files') + + parser.add_argument('--dsstore', help='DSStore file from') + parser.add_argument('--background', help='Background file from') + parser.add_argument('--icon', help='Icon file from') + + parser.add_argument('dmgfile', metavar='DMG_IN', + help='DMG File to Unpack') + parser.add_argument('outpath', metavar='PATH_OUT', + help='Location to put unpacked files') + + options = parser.parse_args(args) + + dmg.extract_dmg(dmgfile=options.dmgfile, output=options.outpath, + dsstore=options.dsstore, background=options.background, + icon=options.icon) + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/python/mozbuild/mozpack/dmg.py b/python/mozbuild/mozpack/dmg.py index 168f6d8d2b34..769fe91c8106 100644 --- a/python/mozbuild/mozpack/dmg.py +++ b/python/mozbuild/mozpack/dmg.py @@ -9,6 +9,7 @@ import os import platform import shutil import subprocess +import sys from mozbuild.util import ensureParentDir @@ -31,8 +32,8 @@ def chmod(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('/'): + # Ensure a trailing slash on directories so rsync copies the *contents* of source. + if not source.endswith('/') and os.path.isdir(source): source += '/' subprocess.check_call(['rsync', '-a', '--copy-unsafe-links', source, dest]) @@ -156,3 +157,53 @@ def create_dmg(source_directory, output_dmg, volume_name, extra_files): 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 + ], + # dmg is seriously chatty + 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, ' ')): + # Rsync will fail on the presence of this symlink + 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) diff --git a/toolkit/mozapps/installer/upload-files.mk b/toolkit/mozapps/installer/upload-files.mk index 5545ed9c28cd..cafffb42fb8b 100644 --- a/toolkit/mozapps/installer/upload-files.mk +++ b/toolkit/mozapps/installer/upload-files.mk @@ -210,30 +210,12 @@ ifeq ($(MOZ_PKG_FORMAT),DMG) PKG_DMG_SOURCE = $(MOZ_PKG_DIR) INNER_MAKE_PACKAGE = $(call py_action,make_dmg,'$(PKG_DMG_SOURCE)' '$(PACKAGE)') INNER_UNMAKE_PACKAGE = \ - set -ex; \ - rm -rf $(ABS_DIST)/unpack.tmp; \ - mkdir -p $(ABS_DIST)/unpack.tmp; \ - $(_ABS_MOZSRCDIR)/build/package/mac_osx/unpack-diskimage $(UNPACKAGE) /tmp/$(MOZ_PKG_APPNAME)-unpack $(ABS_DIST)/unpack.tmp; \ - rsync -a '$(ABS_DIST)/unpack.tmp/$(_APPNAME)' $(MOZ_PKG_DIR); \ - if test -n '$(MOZ_PKG_MAC_DSSTORE)' ; then \ - mkdir -p '$(dir $(MOZ_PKG_MAC_DSSTORE))'; \ - rsync -a '$(ABS_DIST)/unpack.tmp/.DS_Store' '$(MOZ_PKG_MAC_DSSTORE)'; \ - fi; \ - if test -n '$(MOZ_PKG_MAC_BACKGROUND)' ; then \ - mkdir -p '$(dir $(MOZ_PKG_MAC_BACKGROUND))'; \ - rsync -a '$(ABS_DIST)/unpack.tmp/.background/$(notdir $(MOZ_PKG_MAC_BACKGROUND))' '$(MOZ_PKG_MAC_BACKGROUND)'; \ - fi; \ - if test -n '$(MOZ_PKG_MAC_ICON)' ; then \ - mkdir -p '$(dir $(MOZ_PKG_MAC_ICON))'; \ - rsync -a '$(ABS_DIST)/unpack.tmp/.VolumeIcon.icns' '$(MOZ_PKG_MAC_ICON)'; \ - fi; \ - rm -rf $(ABS_DIST)/unpack.tmp; \ - if test -n '$(MOZ_PKG_MAC_RSRC)' ; then \ - cp $(UNPACKAGE) $(MOZ_PKG_APPNAME).tmp.dmg && \ - hdiutil unflatten $(MOZ_PKG_APPNAME).tmp.dmg && \ - { /Developer/Tools/DeRez -skip plst -skip blkx $(MOZ_PKG_APPNAME).tmp.dmg > '$(MOZ_PKG_MAC_RSRC)' || { rm -f $(MOZ_PKG_APPNAME).tmp.dmg && false; }; } && \ - rm -f $(MOZ_PKG_APPNAME).tmp.dmg; \ - fi + $(call py_action,unpack_dmg, \ + $(if $(MOZ_PKG_MAC_DSSTORE),--dsstore '$(MOZ_PKG_MAC_DSSTORE)') \ + $(if $(MOZ_PKG_MAC_BACKGROUND),--background '$(MOZ_PKG_MAC_BACKGROUND)') \ + $(if $(MOZ_PKG_MAC_ICON),--icon '$(MOZ_PKG_MAC_ICON)') \ + '$(UNPACKAGE)' '$(MOZ_PKG_DIR)' \ + ) endif ifdef MOZ_INTERNAL_SIGNING_FORMAT