Bug 1185666 - Move DMG unpack logic to a python script, support linux. r=mshal
authorJustin Wood <Callek@gmail.com>
Tue, 28 Mar 2017 21:46:19 -0400
changeset 351053 7c5c1555236c33817493d7fee8fa16bebfca81e6
parent 351052 b6380473fdc54f26e43e67e62fc61556f4c7ea3a
child 351054 6cb3516cde53825cec05697645e82ba9b80efd74
push id31599
push usercbook@mozilla.com
push dateTue, 04 Apr 2017 10:35:26 +0000
treeherdermozilla-central@891981e67948 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmshal
bugs1185666
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1185666 - Move DMG unpack logic to a python script, support linux. r=mshal MozReview-Commit-ID: inKT2BWof4
python/mozbuild/mozbuild/action/unpack_dmg.py
python/mozbuild/mozpack/dmg.py
toolkit/mozapps/installer/upload-files.mk
new file mode 100644
--- /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:]))
--- a/python/mozbuild/mozpack/dmg.py
+++ b/python/mozbuild/mozpack/dmg.py
@@ -4,16 +4,17 @@
 
 import buildconfig
 import errno
 import mozfile
 import os
 import platform
 import shutil
 import subprocess
+import sys
 
 from mozbuild.util import ensureParentDir
 
 is_linux = platform.system() == 'Linux'
 
 
 def mkdir(dir):
     if not os.path.isdir(dir):
@@ -26,18 +27,18 @@ def mkdir(dir):
 
 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('/'):
+    # 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])
 
 
 def set_folder_icon(dir, tmpdir):
     'Set HFS attributes of dir to use a custom icon'
     if not is_linux:
@@ -151,8 +152,58 @@ def create_dmg(source_directory, output_
             mkdir(os.path.dirname(full_target))
             shutil.copyfile(source, full_target)
         generate_hfs_file(stagedir, tmpdir, volume_name)
         create_app_symlink(stagedir, tmpdir)
         # Set the folder attributes to use a custom icon
         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)
--- a/toolkit/mozapps/installer/upload-files.mk
+++ b/toolkit/mozapps/installer/upload-files.mk
@@ -205,40 +205,22 @@ endif
 
 ifeq ($(MOZ_PKG_FORMAT),DMG)
   PKG_SUFFIX	= .dmg
 
   _ABS_MOZSRCDIR = $(shell cd $(MOZILLA_DIR) && pwd)
   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
   MOZ_SIGN_PREPARED_PACKAGE_CMD=$(MOZ_SIGN_CMD) $(foreach f,$(MOZ_INTERNAL_SIGNING_FORMAT),-f $(f)) $(foreach i,$(SIGN_INCLUDES),-i $(i)) $(foreach x,$(SIGN_EXCLUDES),-x $(x))
   ifeq (WINNT,$(OS_ARCH))
     MOZ_SIGN_PREPARED_PACKAGE_CMD += --nsscmd '$(ABS_DIST)/bin/shlibsign$(BIN_SUFFIX) -v -i'
   endif
 endif