bug 935237 - use libdmg-hfsplus to create DMG files during packaging on Linux. r=gps
authorTed Mielczarek <ted@mielczarek.org>
Fri, 21 Aug 2015 15:33:03 -0400
changeset 292279 4e21632942b8ce945675a31193c117795b3eb052
parent 292278 0a18e5db95a4505d26260e6690a3e6241f0e16de
child 292280 19bd2475605038a80eac3701c1418bb48e713ef1
push id5321
push userbenj@benj.me
push dateFri, 11 Sep 2015 13:45:11 +0000
reviewersgps
bugs935237
milestone43.0a1
bug 935237 - use libdmg-hfsplus to create DMG files during packaging on Linux. r=gps
configure.in
python/mozbuild/mozpack/dmg.py
--- a/configure.in
+++ b/configure.in
@@ -845,16 +845,18 @@ fi
 MOZ_PATH_PROG(XARGS, xargs)
 if test -z "$XARGS" -o "$XARGS" = ":"; then
     AC_MSG_ERROR([xargs not found in \$PATH .])
 fi
 
 MOZ_PATH_PROG(RPMBUILD, rpmbuild, :)
 AC_SUBST(RPMBUILD)
 
+MOZ_PATH_PROG(GENISOIMAGE, genisoimage, :)
+
 if test "$COMPILE_ENVIRONMENT"; then
 
 dnl ========================================================
 dnl = Mac OS X toolchain support
 dnl ========================================================
 
 dnl The universal machinery sets UNIVERSAL_BINARY to inform packager.mk
 dnl that a universal binary is being produced and MOZ_CAN_RUN_PROGRAMS
@@ -8980,16 +8982,18 @@ AC_SUBST_LIST(LIBAV_FFT_ASFLAGS)
 AC_SUBST(MOZ_PACKAGE_JSSHELL)
 AC_SUBST(MOZ_FOLD_LIBS)
 AC_SUBST(MOZ_FOLD_LIBS_FLAGS)
 AC_SUBST(SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE)
 
 AC_SUBST(MOZ_ENABLE_SZIP)
 AC_SUBST(MOZ_SZIP_FLAGS)
 
+AC_SUBST(DMG_TOOL)
+
 dnl Host JavaScript runtime, if any, to use during cross compiles.
 AC_SUBST(JS_BINARY)
 
 if test "$MOZ_DEBUG"; then
     MOZ_EM_DEBUG=1
 fi
 AC_SUBST(MOZ_EM_DEBUG)
 
--- a/python/mozbuild/mozpack/dmg.py
+++ b/python/mozbuild/mozpack/dmg.py
@@ -1,18 +1,20 @@
 # 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
@@ -29,40 +31,81 @@ def rsync(source, dest):
     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'
-    subprocess.check_call(['SetFile', '-a', 'C', dir])
+    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.'
-    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])
+    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)